import asyncio from datetime import datetime, timezone from orchestrator.contracts.error_taxonomy import ReasonCode from orchestrator.contracts.result_contract import FinalSignal, Signal from orchestrator.live_mode import LiveMode def _signal(*, source: str, direction: int, confidence: float) -> Signal: return Signal( ticker="AAPL", direction=direction, confidence=confidence, source=source, timestamp=datetime(2026, 4, 11, 12, 0, tzinfo=timezone.utc), ) class _StubOrchestrator: def __init__(self, responses): self._responses = responses def get_combined_signal(self, ticker: str, date: str): response = self._responses[(ticker, date)] if isinstance(response, Exception): raise response return response def test_live_mode_serializes_degraded_contract_shape(): live_mode = LiveMode( _StubOrchestrator( { ("AAPL", "2026-04-11"): FinalSignal( ticker="AAPL", direction=-1, confidence=0.42, quant_signal=None, llm_signal=_signal(source="llm", direction=-1, confidence=0.6), timestamp=datetime(2026, 4, 11, 12, 1, tzinfo=timezone.utc), degrade_reason_codes=(ReasonCode.QUANT_SIGNAL_FAILED.value,), metadata={ "contract_version": "v1alpha1", "data_quality": {"state": "stale_data", "source": "quant"}, "source_diagnostics": { "quant": {"reason_code": ReasonCode.STALE_DATA.value} }, }, ) } ) ) results = asyncio.run(live_mode.run_once(["AAPL"], "2026-04-11")) assert results == [ { "contract_version": "v1alpha1", "ticker": "AAPL", "date": "2026-04-11", "status": "degraded_success", "result": { "direction": -1, "confidence": 0.42, "quant_direction": None, "llm_direction": -1, "timestamp": "2026-04-11T12:01:00+00:00", }, "error": None, "degradation": { "degraded": True, "reason_codes": [ReasonCode.QUANT_SIGNAL_FAILED.value], "source_diagnostics": { "quant": {"reason_code": ReasonCode.STALE_DATA.value} }, }, "data_quality": {"state": "stale_data", "source": "quant"}, } ] def test_live_mode_serializes_failure_contract_shape(): live_mode = LiveMode( _StubOrchestrator( { ("AAPL", "2026-04-11"): ValueError("both quant and llm signals are None") } ) ) results = asyncio.run(live_mode.run_once(["AAPL"], "2026-04-11")) assert results == [ { "contract_version": "v1alpha1", "ticker": "AAPL", "date": "2026-04-11", "status": "failed", "result": None, "error": { "code": "live_signal_failed", "message": "both quant and llm signals are None", "retryable": False, }, "degradation": { "degraded": True, "reason_codes": [ReasonCode.BOTH_SIGNALS_UNAVAILABLE.value], "source_diagnostics": {}, }, "data_quality": None, } ]