fix(ac-0024): address 4 blockers: NUL byte in C identity, instance_key isolation, clear_warned gate, production wiring
Co-authored-by: genichin <4282227+genichin@users.noreply.github.com>
This commit is contained in:
co-authored by
genichin
parent
153a563f6d
commit
f1864a6aa8
@@ -839,3 +839,85 @@ def test_publish_alarm_returns_bool():
|
||||
failing = _make_failing_emitter()
|
||||
result2 = failing.publish_alarm("test_code", "Test title")
|
||||
assert result2 is False
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Regression tests for ac-0024 blocker fixes
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def test_c_identity_key_contains_no_nul_byte():
|
||||
"""_c_identity_key must not contain NUL (\\x00) — PostgreSQL TEXT bind would fail."""
|
||||
key = _c_identity_key("my-instance", "005930")
|
||||
assert "\x00" not in key, f"identity key contains NUL: {key!r}"
|
||||
# Must still be collision-resistant (different symbols must produce different keys)
|
||||
key2 = _c_identity_key("my-instance", "005931")
|
||||
assert key != key2
|
||||
# Must still be collision-resistant (different instances must produce different keys)
|
||||
key3 = _c_identity_key("other-instance", "005930")
|
||||
assert key != key3
|
||||
|
||||
|
||||
def test_retry_pending_resolved_ignores_other_instance_states():
|
||||
"""_retry_pending_resolved must not touch pending states belonging to a different instance."""
|
||||
from trex_core.application.reconciliation import _retry_pending_resolved
|
||||
|
||||
ns = InMemoryReconcileNotificationStore()
|
||||
other_slot_id = "slot-other-instance"
|
||||
# pending_resolved belonging to *another* instance
|
||||
ns.set_state(other_slot_id, case_code="B", instance_key="other-instance",
|
||||
symbol="005930", quantity_a=10, quantity_b=7, lifecycle_state="pending_resolved")
|
||||
|
||||
emitter, transport = _make_emitter()
|
||||
_retry_pending_resolved(
|
||||
emitter, ns,
|
||||
case_code="B",
|
||||
instance_key=INSTANCE, # current instance — should NOT see other-instance states
|
||||
active_identity_keys=set(),
|
||||
)
|
||||
|
||||
# No resolved alarm published for other instance's state
|
||||
codes = _alarm_codes(transport)
|
||||
assert "reconcile_mismatch_resolved" not in codes
|
||||
# State preserved (not consumed by wrong instance)
|
||||
assert ns.get_state(other_slot_id) is not None
|
||||
|
||||
|
||||
def test_b_sync_does_not_publish_resolved_when_clear_warned_fails():
|
||||
"""Resolved alarm must NOT be published when clear_warned() raises (warning still active)."""
|
||||
from trex_core.application.reconciliation import sync_slot_to_broker
|
||||
|
||||
class FailingWarnStore(InMemoryReconcileWarningStore):
|
||||
def clear_warned(self, slot_id):
|
||||
raise RuntimeError("DB unavailable")
|
||||
|
||||
ns = InMemoryReconcileNotificationStore()
|
||||
slot = _make_slot(qty=10.0)
|
||||
slot_id_str = str(slot.slot_id)
|
||||
repo = _repo_with_slot(slot)
|
||||
ss = SlotService(repo)
|
||||
ws = FailingWarnStore()
|
||||
ws.set_warned(slot_id_str, portfolio_id="pf-A", case_code="B")
|
||||
ns.set_state(slot_id_str, case_code="B", instance_key=INSTANCE,
|
||||
symbol="005930", quantity_a=10, quantity_b=7, lifecycle_state="notified")
|
||||
|
||||
emitter, transport = _make_emitter()
|
||||
result = sync_slot_to_broker(
|
||||
slot_id=slot_id_str,
|
||||
slot_repo=repo,
|
||||
slot_service=ss,
|
||||
broker=FakeBroker({"005930": 7}), # qty matches → clear attempt made
|
||||
instance_key=INSTANCE,
|
||||
warn_store=ws,
|
||||
alarm_emitter=emitter,
|
||||
notification_store=ns,
|
||||
)
|
||||
|
||||
# warning_cleared is False because clear_warned raised
|
||||
assert result["warning_cleared"] is False
|
||||
# No resolved alarm should have been published
|
||||
codes = _alarm_codes(transport)
|
||||
assert "reconcile_mismatch_resolved" not in codes
|
||||
# notification state untouched
|
||||
state = ns.get_state(slot_id_str)
|
||||
assert state is not None
|
||||
assert state["lifecycle_state"] == "notified"
|
||||
|
||||
Reference in New Issue
Block a user