import contextlib import io import json import os import unittest from unittest.mock import patch os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://user:pass@localhost:5432/ai_ict_test") from scripts import run_analysis_decision_monitor class AnalysisDecisionMonitorTests(unittest.TestCase): def test_evaluate_passes_analysis_decision_only_state_without_real_fills(self) -> None: payload = run_analysis_decision_monitor.evaluate_analysis_decision_monitor_payloads( payloads=_healthy_payloads(), base_url="http://example.test", instrument_id=1, timeframe="1m", ) self.assertTrue(payload["passed"]) self.assertEqual(payload["blockers"], []) self.assertEqual(payload["observed"]["broker_fills"], 0) self.assertFalse(payload["observed"]["real_trade_sample_gate_required"]) self.assertEqual(payload["observed"]["real_trade_sample_gate_status"], "deferred_analysis_decision_only") def test_evaluate_blocks_when_real_fill_gate_returns_to_current_actions(self) -> None: payloads = _healthy_payloads() payloads["launch_readiness"] = { **payloads["launch_readiness"], "real_trade_sample_gate_required": True, "real_trade_sample_gate_status": "waiting_for_real_fill_sample", "warning_reasons": ["real_trade_sample_verified"], "next_actions": ["wait_for_and_verify_real_fill_sample"], } payload = run_analysis_decision_monitor.evaluate_analysis_decision_monitor_payloads( payloads=payloads, base_url="http://example.test", instrument_id=1, timeframe="1m", ) self.assertFalse(payload["passed"]) self.assertIn("real_trade_sample_gate_deferred", payload["blockers"]) self.assertIn("real_trade_sample_not_current_action", payload["blockers"]) def test_main_outputs_json_from_monitor_payload(self) -> None: monitor_payload = { "passed": True, "generated_ts": "2026-05-11T00:00:00+00:00", "blockers": [], "warnings": [], } output = io.StringIO() with patch("scripts.run_analysis_decision_monitor.run_monitor", return_value=monitor_payload), contextlib.redirect_stdout(output): exit_code = run_analysis_decision_monitor.main(env={"ANALYSIS_DECISION_MONITOR_OUTPUT_JSON": "1"}) self.assertEqual(exit_code, 0) self.assertEqual(json.loads(output.getvalue())["passed"], True) def _healthy_payloads() -> dict: return { "health": {"app": "ai-ict", "database_configured": True}, "launch_readiness": { "analysis_only_ready": True, "restricted_decision_support_ready": True, "decision_support_ready": False, "operator_soft_rollout_ready": True, "operator_soft_rollout_mode": "restricted_decision_support", "real_trade_sample_gate_required": False, "real_trade_sample_gate_status": "deferred_analysis_decision_only", "blocking_reasons": [], "warning_reasons": ["real_live_path_ready"], "next_actions": ["start_restricted_decision_support_soft_rollout", "keep_real_live_disabled_until_adapter_ready"], "market_state": { "stream_state": {"code": "market_stream_connected"}, "freshness": {"code": "market_data_fresh"}, "data_gap_state": {"code": "market_data_gap_clear"}, }, }, "broker_state": {"status": "broker_state_ok", "fills": [], "positions": [], "orders": [], "error": None}, "runtime_status": { "runtime_health": {"level": "ok"}, "current_blockers": [], "current_warnings": [], "degraded_mode_state": {"is_degraded": False}, "latest_market_state_by_symbol": { "BTC-USDT": { "stream_state": {"code": "market_stream_connected"}, "freshness": {"code": "market_data_fresh"}, "data_gap_state": {"code": "market_data_gap_clear"}, } }, }, "decision_packet": { "status": "monitoring", "action_recommendation": "restricted_decision_support", "operator_mode": "observe_only", "trust_classification": "blacklist", "execution_allowed": None, "why_allowed": ["restricted_decision_support_allowed", "execution_assist_not_allowed"], "why_blocked": ["trust_model_decision_support_restricted"], }, "review_packet": { "decision_packet": {"status": "monitoring"}, "trust_score": {"restricted_decision_support_list": ["SWEEP_MSS_FVG"]}, "research_council_summary": {"overall_stance": "observe_only"}, }, "p6_governance_tick": { "status": "no_op_healthy", "effective_health_state": "healthy", "requires_cycle_run": False, "requires_bounded_slice": False, }, } if __name__ == "__main__": unittest.main()