"""Snapshot scheduler tests (REQ-030 AC-1).""" from __future__ import annotations from trex_core.adapters.broker.paper import PaperBroker from trex_core.adapters.portfolio.stub_read import StubPortfolioRead from trex_core.application.portfolio import PortfolioItem, SnapshotStore from trex_core.application.snapshot_scheduler import SnapshotScheduler def test_run_once_fetches_universe_and_swaps_store(): read = StubPortfolioRead(daemon_id="d-1", items=[ PortfolioItem(portfolio_id="pf-X", symbol="X", cash_balance=100, asset_quantity=0), ]) store = SnapshotStore() sched = SnapshotScheduler(read_port=read, store=store, broker=PaperBroker()) assert sched.run_once() is True pids = {a.item.portfolio_id for a in store.list_assets()} assert pids == {"pf-X"} # universe membership + strategy params come from fetch (baseline) # cash/quantity are baseline values (option D will overwrite later) a = store.get("pf-X") assert a.item.cash_balance == 100 assert a.warning is False def test_run_once_failure_sets_degraded_signal(): class BadRead: def whoami(self): return "d-2" def get_portfolios(self, daemon_id): from trex_core.application.portfolio import PortfolioHubUnavailableError raise PortfolioHubUnavailableError("simulated") store = SnapshotStore() sched = SnapshotScheduler(read_port=BadRead(), store=store, broker=PaperBroker()) assert sched.run_once() is False reason, msg = store.degraded_state() assert reason is not None and reason.value == "portfolio_hub_unavailable" def test_swap_preserves_warning_for_carried_over_portfolio(): store = SnapshotStore() store.swap([PortfolioItem(portfolio_id="pf-X", symbol="X", cash_balance=100, asset_quantity=0)]) store.set_warning("pf-X", True) # second swap includes the same portfolio with refreshed cash store.swap([PortfolioItem(portfolio_id="pf-X", symbol="X", cash_balance=200, asset_quantity=0)]) a = store.get("pf-X") assert a.warning is True assert a.item.cash_balance == 200 def test_option_d_update_cash_position_overrides_baseline(): """REQ-030 wiring — when *_after lands we update the authoritative values.""" store = SnapshotStore() store.swap([PortfolioItem(portfolio_id="pf-X", symbol="X", cash_balance=100, asset_quantity=0)]) store.update_cash_position("pf-X", cash_balance=92.5, asset_quantity=1) a = store.get("pf-X") assert a.item.cash_balance == 92.5 assert a.item.asset_quantity == 1