"""장 마감 중 sleep이 개장(09:00)을 건너뛰지 않는지 검증 — ORB 수집 누락 방지.""" from datetime import datetime, timedelta import pytest from app.core import market_schedule as ms def _at(monkeypatch, y, m, d, hh, mm): fake = datetime(y, m, d, hh, mm, tzinfo=ms.KST) class _DT(datetime): @classmethod def now(cls, tz=None): return fake if tz else fake.replace(tzinfo=None) monkeypatch.setattr(ms, "datetime", _DT) return fake def test_seconds_until_open_same_day(monkeypatch): _at(monkeypatch, 2026, 9, 23, 8, 59) # 수요일 08:59 assert ms.seconds_until_market_open() == pytest.approx(60, abs=1) def test_none_during_market_hours(monkeypatch): _at(monkeypatch, 2026, 9, 23, 10, 0) assert ms.seconds_until_market_open() is None def test_skips_weekend(monkeypatch): _at(monkeypatch, 2026, 9, 25, 16, 0) # 금요일 폐장 후 secs = ms.seconds_until_market_open() nxt = datetime(2026, 9, 25, 16, 0, tzinfo=ms.KST) + timedelta(seconds=secs) assert nxt.weekday() == 0 and (nxt.hour, nxt.minute) == (9, 0) # 다음 월요일 09:00 def test_sleep_never_overshoots_open(monkeypatch): """스케줄러가 쓰는 계산식이 개장을 넘겨 자지 않는다.""" for hh, mm in [(8, 0), (8, 59), (7, 30), (6, 0)]: _at(monkeypatch, 2026, 9, 23, hh, mm) until = ms.seconds_until_market_open() sleep = 1800.0 if until is None else max(15.0, min(1800.0, until)) assert sleep <= until, f"{hh}:{mm} — sleep {sleep}s가 개장({until}s 후)을 넘김" def test_pre_market_window(monkeypatch): """08:30~09:00은 개장 전 준비 구간 (배치 분석 선행 실행).""" _at(monkeypatch, 2026, 9, 23, 8, 45) assert ms.is_pre_market_window() is True _at(monkeypatch, 2026, 9, 23, 8, 20) assert ms.is_pre_market_window() is False _at(monkeypatch, 2026, 9, 23, 9, 0) assert ms.is_pre_market_window() is False # 개장 후는 정규 경로 _at(monkeypatch, 2026, 9, 26, 8, 45) # 토요일 assert ms.is_pre_market_window() is False