fix: preserve sell event routing owner
This commit is contained in:
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user