from __future__ import annotations from datetime import date import pytest from app.jobs import update_market as job from app.services.fx_rates import FxRatesUpdateResult from app.services.market_data import MarketDataUpdateResult @pytest.fixture def calls(monkeypatch) -> dict: """Подменяет сервисы: запоминает аргументы, отдаёт заданный результат.""" state: dict = { "market": MarketDataUpdateResult(on_date=date(2026, 10, 8), updated=1), "fx": FxRatesUpdateResult(date_to=date(2026, 10, 8), saved=1), "market_args": None, "fx_args": None, } async def _market_update(on_date, user): # noqa: ANN001 state["market_args"] = (on_date, user) if isinstance(state["market"], Exception): raise state["market"] return state["market"] async def _fx_update(up_to, user): # noqa: ANN001 state["fx_args"] = (up_to, user) return state["fx"] monkeypatch.setattr(job.market_data, "update", _market_update) monkeypatch.setattr(job.fx_rates, "update", _fx_update) return state def test_explicit_date_runs_both_updates_as_system(calls: dict) -> None: code = job.main(["--date", "2026-10-08"]) assert code == 0 assert calls["market_args"] == (date(2026, 10, 8), "system") assert calls["fx_args"] == (date(2026, 10, 8), "system") def test_default_date_is_report_date_by_calendar(calls: dict, monkeypatch) -> None: async def _report_date(): return date(2026, 10, 9) monkeypatch.setattr(job.business_calendar, "report_date", _report_date) assert job.main([]) == 0 assert calls["market_args"][0] == date(2026, 10, 9) def test_default_date_without_calendar_skips_weekends_only( calls: dict, monkeypatch ) -> None: async def _report_date(): raise RuntimeError("БД недоступна") monkeypatch.setattr(job.business_calendar, "report_date", _report_date) monkeypatch.setattr(job, "previous_business_day", lambda: date(2026, 10, 9)) assert job.main([]) == 0 assert calls["market_args"][0] == date(2026, 10, 9) def test_no_trades_is_not_an_error(calls: dict) -> None: calls["market"] = MarketDataUpdateResult(on_date=date(2026, 10, 8), no_trades=2) assert job.main(["--date", "2026-10-08"]) == 0 def test_security_errors_give_nonzero_exit(calls: dict) -> None: calls["market"] = MarketDataUpdateResult(on_date=date(2026, 10, 8), errors=1) assert job.main(["--date", "2026-10-08"]) == 1 def test_fx_error_gives_nonzero_exit(calls: dict) -> None: calls["fx"] = FxRatesUpdateResult(date_to=date(2026, 10, 8), error="ЦБ недоступен") assert job.main(["--date", "2026-10-08"]) == 1 def test_crash_in_quotes_still_updates_fx_and_fails(calls: dict) -> None: calls["market"] = RuntimeError("БД недоступна") assert job.main(["--date", "2026-10-08"]) == 1 assert calls["fx_args"] is not None def test_bad_date_argument_exits_with_2(calls: dict) -> None: with pytest.raises(SystemExit) as exc_info: job.main(["--date", "08.10.2026"]) assert exc_info.value.code == 2 assert calls["market_args"] is None