from __future__ import annotations from types import SimpleNamespace import pytest import src.bootstrap.app_factory as app_factory from src.bootstrap.application import ApplicationComposition class RecordingJournal: def __init__(self) -> None: self.info_calls: list[ tuple[str, str, dict[str, object]] ] = [] def log_info( self, event: str, message: str, context: dict[str, object], ) -> None: self.info_calls.append( ( event, message, context, ) ) def log_critical( self, event: str, message: str, context: dict[str, object], ) -> None: del event, message, context def make_settings() -> SimpleNamespace: return SimpleNamespace( bot_token="test-token", bot_parse_mode="HTML", log_level="INFO", app_env="test", exchange_name="dzengi", default_symbol="BTC/USD_LEVERAGE", trade_stream=SimpleNamespace(enabled=True), ) def test_create_app_builds_one_application_composition( monkeypatch: pytest.MonkeyPatch, ) -> None: settings = make_settings() bot = object() dispatcher = object() runtime = object() journal = RecordingJournal() observed_runtime_settings: list[object] = [] registered_bots: list[object] = [] routed_dispatchers: list[object] = [] monkeypatch.setattr( app_factory, "load_settings", lambda: settings, ) monkeypatch.setattr( app_factory, "setup_logging", lambda level: None, ) monkeypatch.setattr( app_factory, "init_schema", lambda: None, ) monkeypatch.setattr( app_factory, "JournalService", lambda: journal, ) def build_runtime(received_settings: object) -> object: observed_runtime_settings.append(received_settings) return runtime monkeypatch.setattr( app_factory, "build_trade_stream_production_runtime", build_runtime, ) monkeypatch.setattr( app_factory, "Bot", lambda **kwargs: bot, ) monkeypatch.setattr( app_factory, "Dispatcher", lambda: dispatcher, ) monkeypatch.setattr( app_factory.NotificationTargetRegistry, "set_bot", registered_bots.append, ) monkeypatch.setattr( app_factory, "setup_routers", routed_dispatchers.append, ) application = app_factory.create_app() assert isinstance(application, ApplicationComposition) assert application.bot is bot assert application.dispatcher is dispatcher assert application.trade_stream_runtime is runtime assert observed_runtime_settings == [settings] assert registered_bots == [bot] assert routed_dispatchers == [dispatcher] assert journal.info_calls[0][2]["trade_stream_enabled"] is True def test_runtime_build_error_is_fatal( monkeypatch: pytest.MonkeyPatch, ) -> None: expected = RuntimeError("invalid Trade Stream settings") monkeypatch.setattr( app_factory, "load_settings", make_settings, ) monkeypatch.setattr( app_factory, "setup_logging", lambda level: None, ) monkeypatch.setattr( app_factory, "init_schema", lambda: None, ) monkeypatch.setattr( app_factory, "JournalService", RecordingJournal, ) def fail_runtime_build(settings: object) -> None: del settings raise expected monkeypatch.setattr( app_factory, "build_trade_stream_production_runtime", fail_runtime_build, ) with pytest.raises(RuntimeError) as exc_info: app_factory.create_app() assert exc_info.value is expected