fix(intents): isolate alarm context lookup and sanitize alarm failure logs
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
5dd3d57f86
commit
1ca9e60c98
@@ -101,6 +101,24 @@ class DuplicateInFlightIntentStore:
|
||||
raise AssertionError("finalize must not be called for duplicate_in_flight")
|
||||
|
||||
|
||||
class ContextFailingSlotRepository(InMemorySlotRepository):
|
||||
"""Raise only on the N+1th fetch_slot to simulate post-finalize lookup failure."""
|
||||
|
||||
def __init__(self, *, fail_on_fetch_call_no=None):
|
||||
super().__init__()
|
||||
self._fetch_calls = 0
|
||||
self._fail_on_fetch_call_no = fail_on_fetch_call_no
|
||||
|
||||
def fetch_slot(self, slot_id):
|
||||
self._fetch_calls += 1
|
||||
if self._fail_on_fetch_call_no == self._fetch_calls:
|
||||
raise RuntimeError("context lookup failed")
|
||||
return super().fetch_slot(slot_id)
|
||||
|
||||
def fail_on_next_fetch(self):
|
||||
self._fail_on_fetch_call_no = self._fetch_calls + 1
|
||||
|
||||
|
||||
def _make_holding_slot(repo, svc, instance_key="default", portfolio_id="pf-A", symbol="005930", qty=10):
|
||||
"""Helper: creates universe, puts slot in HOLDING state with given qty."""
|
||||
repo.reconcile_universe(instance_key, {portfolio_id: symbol})
|
||||
@@ -512,6 +530,60 @@ def test_reconcile_warned_alarm_finalize_failure_and_publish_failure_are_isolate
|
||||
assert d3.reason == "reconcile_warned"
|
||||
|
||||
|
||||
def test_reconcile_warned_buy_context_lookup_failure_does_not_change_response():
|
||||
repo = ContextFailingSlotRepository(fail_on_fetch_call_no=2)
|
||||
svc = SlotService(repo)
|
||||
warn_store = InMemoryReconcileWarningStore()
|
||||
intent_store = InMemoryIntentStore()
|
||||
alarms = RecordingAlarmEmitter()
|
||||
repo.reconcile_universe("default", {"pf-A": "005930"})
|
||||
slot = repo.find_slot_by_portfolio("default", "pf-A")
|
||||
warn_store.set_warned(str(slot.slot_id), portfolio_id="pf-A", case_code="B")
|
||||
intent_svc = IntentService(
|
||||
svc, repo, intent_store, instance_key="default", warn_store=warn_store, alarm_emitter=alarms
|
||||
)
|
||||
|
||||
decision = intent_svc.submit_buy(
|
||||
BuyIntent(intent_id="buy-context-fail", slot_id=slot.slot_id, strategy_id="stg-buy")
|
||||
)
|
||||
assert decision.status == "rejected"
|
||||
assert decision.reason == "reconcile_warned"
|
||||
assert len(alarms.calls) == 1
|
||||
_, _, _, fields = alarms.calls[0]
|
||||
assert fields["intent_id"] == "buy-context-fail"
|
||||
assert fields["strategy_id"] == "stg-buy"
|
||||
assert "slot_id" not in fields
|
||||
assert "portfolio_id" not in fields
|
||||
assert "symbol" not in fields
|
||||
|
||||
|
||||
def test_reconcile_warned_sell_context_lookup_failure_does_not_change_response():
|
||||
repo = ContextFailingSlotRepository()
|
||||
svc = SlotService(repo)
|
||||
warn_store = InMemoryReconcileWarningStore()
|
||||
intent_store = InMemoryIntentStore()
|
||||
alarms = RecordingAlarmEmitter()
|
||||
slot = _make_holding_slot(repo, svc, qty=10, portfolio_id="pf-A", symbol="005930")
|
||||
repo._fail_on_fetch_call_no = repo._fetch_calls + 2
|
||||
warn_store.set_warned(str(slot.slot_id), portfolio_id="pf-A", case_code="C")
|
||||
intent_svc = IntentService(
|
||||
svc, repo, intent_store, instance_key="default", warn_store=warn_store, alarm_emitter=alarms
|
||||
)
|
||||
|
||||
decision = intent_svc.submit_sell(
|
||||
SellIntent(intent_id="sell-context-fail", slot_id=slot.slot_id, sell_strategy_id="stg-sell")
|
||||
)
|
||||
assert decision.status == "rejected"
|
||||
assert decision.reason == "reconcile_warned"
|
||||
assert len(alarms.calls) == 1
|
||||
_, _, _, fields = alarms.calls[0]
|
||||
assert fields["intent_id"] == "sell-context-fail"
|
||||
assert fields["strategy_id"] == "stg-sell"
|
||||
assert "slot_id" not in fields
|
||||
assert "portfolio_id" not in fields
|
||||
assert "symbol" not in fields
|
||||
|
||||
|
||||
# REQ-104 AC-3: no warn, no pause => buy/sell admitted normally
|
||||
def test_intent_service_no_warn_admitted_normally():
|
||||
repo = InMemorySlotRepository()
|
||||
|
||||
Reference in New Issue
Block a user