from collections.abc import Iterator from datetime import datetime, timedelta import pytest from app.config import Settings from app.core import clock @pytest.fixture(autouse=True) def _fresh_clock(monkeypatch: pytest.MonkeyPatch) -> Iterator[None]: monkeypatch.setattr(clock, "get_settings", lambda: Settings(_env_file=None, demo_clock=True)) clock.register_persistence(None, None) clock.reset() yield clock.register_persistence(None, None) clock.reset() def _close(a: datetime, b: datetime) -> bool: return abs(a - b) < timedelta(seconds=5) def test_now_is_tz_aware_in_settings_tz() -> None: now = clock.now() assert now.tzinfo is not None assert now.utcoffset() == timedelta(hours=3) # Europe/Moscow def test_advance_shifts_time() -> None: before = clock.now() after = clock.advance(days=1, hours=2, minutes=30) assert _close(after, before + timedelta(days=1, hours=2, minutes=30)) assert clock.offset() == timedelta(days=1, hours=2, minutes=30) assert clock.today() == clock.now().date() def test_set_now_and_reset() -> None: target = datetime(2026, 10, 20, 9, 0) assert _close(clock.set_now(target), target.replace(tzinfo=clock.now().tzinfo)) clock.reset() assert clock.offset() == timedelta(0) assert _close(clock.now(), clock._real_now()) def test_persistence_hook_loads_and_saves() -> None: saved: list[timedelta] = [] clock.register_persistence(lambda: timedelta(days=5), saved.append) assert clock.offset() == timedelta(days=5) clock.advance(days=1) assert saved[-1] == timedelta(days=6) clock.reset() assert saved[-1] == timedelta(0) def test_persistence_load_none_keeps_offset() -> None: clock.advance(hours=3) clock.register_persistence(lambda: None, None) assert clock.offset() == timedelta(hours=3) def test_real_mode_ignores_offset_and_forbids_moving(monkeypatch: pytest.MonkeyPatch) -> None: clock.advance(days=3) monkeypatch.setattr(clock, "get_settings", lambda: Settings(_env_file=None, demo_clock=False)) assert clock.offset() == timedelta(0) assert _close(clock.now(), clock._real_now()) with pytest.raises(RuntimeError): clock.advance(days=1) with pytest.raises(RuntimeError): clock.set_now(datetime(2026, 1, 1))