@@ -103,6 +103,97 @@ def test_handle_precheck_get_does_not_execute(strategy_module, monkeypatch):
103103 assert observed ["called" ] is False
104104
105105
106+ def test_handle_probe_checks_account_snapshot_without_success_notification (strategy_module , monkeypatch ):
107+ observed = {"events" : [], "disconnects" : 0 , "notifications" : []}
108+
109+ class FakeIB :
110+ def disconnect (self ):
111+ observed ["disconnects" ] += 1
112+
113+ snapshot = types .SimpleNamespace (
114+ buying_power = 123.0 ,
115+ total_equity = 456.0 ,
116+ positions = (types .SimpleNamespace (symbol = "SOXL" ),),
117+ )
118+
119+ monkeypatch .setattr (strategy_module , "build_request_log_context" , lambda : types .SimpleNamespace (run_id = "run-001" ))
120+ monkeypatch .setattr (strategy_module , "build_execution_report" , lambda log_context , ** _kwargs : {"status" : "pending" })
121+ monkeypatch .setattr (
122+ strategy_module ,
123+ "persist_execution_report" ,
124+ lambda report , ** _kwargs : observed .setdefault ("report" , dict (report )) or "/tmp/runtime-report.json" ,
125+ )
126+ monkeypatch .setattr (
127+ strategy_module ,
128+ "log_runtime_event" ,
129+ lambda context , event , ** fields : observed ["events" ].append ((event , fields )),
130+ )
131+ monkeypatch .setattr (strategy_module , "load_strategy_plugin_signals" , lambda : ((), None ))
132+ monkeypatch .setattr (strategy_module , "attach_strategy_plugin_report" , lambda * args , ** kwargs : None )
133+ monkeypatch .setattr (strategy_module , "connect_ib" , lambda : FakeIB ())
134+ monkeypatch .setattr (strategy_module , "build_portfolio_snapshot" , lambda ib : snapshot )
135+ monkeypatch .setattr (
136+ strategy_module ,
137+ "publish_notification" ,
138+ lambda ** _kwargs : observed ["notifications" ].append (_kwargs ),
139+ )
140+
141+ with strategy_module .app .test_request_context ("/probe" , method = "POST" ):
142+ body , status = strategy_module .handle_probe ()
143+
144+ assert status == 200
145+ assert body == "Probe OK"
146+ assert [event for event , _fields in observed ["events" ]] == [
147+ "health_probe_received" ,
148+ "health_probe_completed" ,
149+ ]
150+ assert observed ["report" ]["status" ] == "ok"
151+ assert observed ["report" ]["summary" ]["buying_power" ] == 123.0
152+ assert observed ["report" ]["summary" ]["total_equity" ] == 456.0
153+ assert observed ["report" ]["summary" ]["positions_count" ] == 1
154+ assert observed ["disconnects" ] == 1
155+ assert observed ["notifications" ] == []
156+
157+
158+ def test_handle_probe_failure_sends_notification (strategy_module , monkeypatch ):
159+ observed = {"events" : [], "notifications" : []}
160+
161+ monkeypatch .setattr (strategy_module , "build_request_log_context" , lambda : types .SimpleNamespace (run_id = "run-001" ))
162+ monkeypatch .setattr (strategy_module , "build_execution_report" , lambda log_context , ** _kwargs : {"status" : "pending" })
163+ monkeypatch .setattr (strategy_module , "persist_execution_report" , lambda report , ** _kwargs : observed .setdefault ("report" , dict (report )) or "/tmp/runtime-report.json" )
164+ monkeypatch .setattr (
165+ strategy_module ,
166+ "log_runtime_event" ,
167+ lambda context , event , ** fields : observed ["events" ].append ((event , fields )),
168+ )
169+ monkeypatch .setattr (strategy_module , "load_strategy_plugin_signals" , lambda : ((), None ))
170+ monkeypatch .setattr (strategy_module , "attach_strategy_plugin_report" , lambda * args , ** kwargs : None )
171+ monkeypatch .setattr (
172+ strategy_module ,
173+ "connect_ib" ,
174+ lambda : (_ for _ in ()).throw (RuntimeError ("probe failed" )),
175+ )
176+ monkeypatch .setattr (
177+ strategy_module ,
178+ "publish_notification" ,
179+ lambda ** kwargs : observed ["notifications" ].append (kwargs ),
180+ )
181+
182+ with strategy_module .app .test_request_context ("/probe" , method = "POST" ):
183+ body , status = strategy_module .handle_probe ()
184+
185+ assert status == 500
186+ assert body == "Error"
187+ assert observed ["report" ]["status" ] == "error"
188+ assert observed ["report" ]["errors" ][0 ]["stage" ] == "health_probe"
189+ assert [event for event , _fields in observed ["events" ]] == [
190+ "health_probe_received" ,
191+ "health_probe_failed" ,
192+ ]
193+ assert len (observed ["notifications" ]) == 1
194+ assert "probe failed" in observed ["notifications" ][0 ]["detailed_text" ]
195+
196+
106197def test_build_extra_notification_lines_includes_account_id (strategy_module ):
107198 lines = strategy_module .build_extra_notification_lines (())
108199 assert any ("U18308207" in line for line in lines )
0 commit comments