The broker-backed test starts with eight telemetry envelopes. Its fault plan repeats two event identities, so the publisher schedules ten deliveries. The final assertions expect ten deliveries, eight inserts, and two duplicates. I use those numbers as a trail through the system.
A store test can prove what SQLite does with a repeated identity. It cannot tell me whether the publisher, exchange, queue, and consumer agree. For that, the test has to take the same path as a delivery.
8 generated events
→ 10 scheduled deliveries
→ RabbitMQ exchange + queue
→ worker callback
→ SQLite transaction
→ basic.ack (called, not asserted)
→ SQL summary: 10 delivered / 8 inserted / 2 duplicate
The first count exists before RabbitMQ.
The fault injector is a pure transformation. In this test it duplicates sequences four and eight without changing their event_id. The schedule therefore contains “ten deliveries, eight unique” before the publisher opens a socket; its receipt repeats those counts after publishing.
I kept that decision outside the broker on purpose. If the release count is wrong, the fault-plan test fails with a sequence list. RabbitMQ is not asked to explain a scheduling mistake.
The callback is the middle of the trip.
The worker copies and deserialises the delivery, then hands the envelope to PersistAsync. One SQLite transaction decides whether the identity is new, updates the replay cursor and counters, and commits. Only after that method returns does the worker send basic.ack.
That ordering is the part I cared about. A successful callback log would be too early; the durable result is the event row and the counters committed beside it. Invalid JSON is rejected, while a storage exception leaves the delivery eligible for another attempt.
The last count belongs to SQLite.
The integration test creates a unique exchange, queue, and temporary database, publishes through a real RabbitMQ service, then polls the store until all ten deliveries have an outcome. That verifies the routing and consumer path without substituting a mocked channel.
The acknowledgement is exercised but not proved. The test does not inspect the queue after basic.ack, and it does not kill the worker between commit and acknowledgement to observe a redelivery. A missing ack could still leave the expected SQL counts in place before the connection closes. The duplicate-store test covers the database decision, but not that crash window end to end.
For this experiment, the path is inspectable from the eight source envelopes to the three SQL counts. That is the whole claim; the missing crash test remains visible instead of being hidden inside a general lesson.