fix: preserve sell event routing owner

This commit is contained in:
Hermes Agent
2026-09-20 20:34:00 +09:00
parent 3f6a967aa1
commit c66acf6e47
20 changed files with 766 additions and 32 deletions
@@ -101,6 +101,31 @@ class TestPublishRetryOnHalfOpen:
assert reconnect_called["yes"], "_force_reconnect must be called on first failure"
def test_sell_retry_reuses_identical_payload_and_mandatory_flag(self):
t = _make_transport()
_inject_connected(t)
payload = {
"slot_id": "slot-95",
"event_type": "slot_filled_sell",
"sequence_at": 11,
}
routing_key = "slot_filled_sell.SELL-B.005930"
with (
patch.object(
t,
"_do_basic_publish",
side_effect=[OSError("first attempt"), None],
) as publish,
patch.object(t, "_force_reconnect"),
):
t.publish_event(routing_key, payload)
assert publish.call_count == 2
assert publish.call_args_list[0] == publish.call_args_list[1]
assert publish.call_args_list[0].args[0] == routing_key
assert publish.call_args_list[0].kwargs["mandatory"] is True
# ---------------------------------------------------------------------------
# REQ-091 AC-2: retry also fails → TransportUnavailableError raised
@@ -5,6 +5,7 @@ queue/routing keys to avoid colliding with other tests or runs.
"""
from __future__ import annotations
import json
import os
import threading
import time
@@ -149,6 +150,63 @@ def test_event_publish_and_per_strategy_consume(mq_url):
_drain_queue(mq_url, queue_b)
def test_sell_event_retry_preserves_payload_and_strategy_route(mq_url, monkeypatch):
"""AC-0039: live retry keeps identity and uses mandatory confirmed publish."""
suffix = uuid.uuid4().hex[:8]
owner = f"SELL-{suffix}"
other_owner = f"OTHER-{suffix}"
queue = topology.strategy_event_queue_name(owner)
other_queue = topology.strategy_event_queue_name(other_owner)
routing_key = topology.event_routing_key("slot_filled_sell", owner, "005930")
payload = {
"slot_id": f"slot-{suffix}",
"event_type": "slot_filled_sell",
"sequence_at": 7,
}
publisher = RabbitMQTransport(mq_url, timeout=5.0)
attempts: list[tuple[str, bytes, bool]] = []
try:
publisher.declare_topology()
publisher.declare_strategy_queue(
queue, topology.strategy_event_binding_pattern(owner),
)
publisher.declare_strategy_queue(
other_queue, topology.strategy_event_binding_pattern(other_owner),
)
original_publish = publisher._do_basic_publish
def fail_first_then_publish(rk: str, body: bytes, *, mandatory: bool) -> None:
attempts.append((rk, body, mandatory))
if len(attempts) == 1:
raise OSError("forced first-attempt failure")
original_publish(rk, body, mandatory=mandatory)
monkeypatch.setattr(publisher, "_do_basic_publish", fail_first_then_publish)
publisher.publish_event(routing_key, payload)
assert len(attempts) == 2
assert attempts[0] == attempts[1]
assert attempts[0][0] == routing_key
assert attempts[0][2] is True
assert json.loads(attempts[0][1]) == payload
publisher._ensure_connected()
channel = publisher._channel
assert channel is not None
method, _props, body = channel.basic_get(queue, auto_ack=True)
assert method is not None
assert body is not None
assert json.loads(body) == payload
other_method, _other_props, _other_body = channel.basic_get(
other_queue, auto_ack=True,
)
assert other_method is None
finally:
publisher.close()
_drain_queue(mq_url, queue)
_drain_queue(mq_url, other_queue)
def test_call_rpc_timeout(mq_url):
queue = f"trex.test.rpc.timeout.{uuid.uuid4().hex[:8]}"
client = RabbitMQTransport(mq_url, timeout=5.0)