124 lines
5.1 KiB
Python
124 lines
5.1 KiB
Python
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()
|