Skip to content

m12-pr15: DLQ contract pin (tests/queue/test_dlq.py)#33

Open
BalaShankar9 wants to merge 1 commit into
m12-pr14-requirements-lockfilefrom
m12-pr15-test-dlq
Open

m12-pr15: DLQ contract pin (tests/queue/test_dlq.py)#33
BalaShankar9 wants to merge 1 commit into
m12-pr14-requirements-lockfilefrom
m12-pr15-test-dlq

Conversation

@BalaShankar9

Copy link
Copy Markdown
Owner

Summary

Adds backend/tests/queue/test_dlq.py — 8 tests that pin the DLQ
behaviour the operator runbook (scripts/ops/dlq_replay.py) and the
queue_dlq_total Prometheus snapshot rely on.

No production code changed.

Why

The existing backend/tests/core/events/test_stream_consumer.py covers
exactly one DLQ path (test_dlq_after_max_deliveries_via_reclaim).
That test asserts behavioural shape but not the contract that
downstream consumers (operator runbook, replay tool, metrics snapshot)
depend on.

If a future refactor drops a field, swaps the xadd/xack order, or
collapses the two DLQ branches, the queue keeps working but the runbook
silently breaks.

What's pinned

# Test Invariant
1 test_dlq_entry_has_full_5_field_schema exact 5 keys: consumer, source_stream, source_msg_id, reason, event
2 test_dlq_xadd_happens_before_source_xack crash-safety: a crash between the two leaves a duplicate (recoverable) not a drop
3 test_dlq_reason_truncated_to_500_chars bounded log + metric label cardinality
4 test_dlq_increments_queue_dlq_metric queue_dlq_total Prometheus snapshot counter wires up
5 test_dlq_inline_when_delivery_attempt_already_exceeds delivery_attempt > max_deliveries branch is distinct from "handler raised on final attempt"
6 test_dlq_decodes_bytes_fields_losslessly real redis-py returns bytes; the round-trip must produce a JSON-decodable string with the original event_id
7 test_dlq_entry_replays_via_dlq_replay_tool scripts/ops/dlq_replay.py::_build_replay_payload accepts the DLQ entry as-is
8 test_no_dlq_when_handler_eventually_succeeds negative control: success path produces no DLQ entry, source ack present

Local verification

cd backend && pytest tests/queue/test_dlq.py -v
# 8 passed in 0.11s

Stacked on

PR #32 (m12-pr14-requirements-lockfile). Base of this PR is
m12-pr14-requirements-lockfile; merging that first will collapse
the diff to the m12-pr15 surface only.

Files

  • backend/tests/queue/__init__.py (new, empty)
  • backend/tests/queue/test_dlq.py (new, 8 tests, ~280 lines)
  • context/TESTING_CONTEXT.md — m12-pr15 row in "shipped"
  • context/CHANGELOG_INTELLIGENCE.md — m12-pr15 row

Adds backend/tests/queue/test_dlq.py — 8 tests that pin the DLQ
behaviour the operator runbook and replay tooling rely on:

1. 5-field schema (consumer, source_stream, source_msg_id, reason, event)
2. xadd(DLQ) BEFORE xack(source) — crash-safety invariant
3. reason truncated to 500 chars (log size + metric label cardinality)
4. queue_dlq_total Prometheus counter increments on every DLQ
5. inline branch (delivery_attempt > max_deliveries) fires distinctly
   from the "handler raised on final attempt" branch
6. bytes-typed Redis fields decode losslessly into the DLQ event JSON
7. round-trip with scripts/ops/dlq_replay.py::_build_replay_payload
8. negative control: successful handler -> no DLQ, source acked

Coverage gap closed: the existing test_stream_consumer.py covers the
single "max_deliveries via reclaim" happy-fail path. The new file pins
the *contract* — schema, ordering, metric, replay-tool round-trip — so
a future refactor can't quietly break the runbook.

No production code changed. Stacked on PR #32 (m12-pr14).

Files:
- backend/tests/queue/__init__.py (new)
- backend/tests/queue/test_dlq.py (new, 8 tests, ~280 lines)
- context/TESTING_CONTEXT.md: m12-pr15 row in shipped section
- context/CHANGELOG_INTELLIGENCE.md: m12-pr15 row

Local: pytest tests/queue/test_dlq.py -v -> 8 passed in 0.11s
Copilot AI review requested due to automatic review settings May 9, 2026 04:41

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a dedicated DLQ contract test module to pin the Redis Streams consumer’s DLQ invariants relied on by operator tooling and metrics, plus updates the /context ledger to reflect the shipped work. No production code paths are modified.

Changes:

  • Added backend/tests/queue/test_dlq.py with 8 async tests that lock down DLQ schema, ordering, truncation, metrics, bytes decoding, replay-tool compatibility, and negative-control behavior.
  • Added backend/tests/queue/__init__.py to introduce the tests.queue package.
  • Updated context/TESTING_CONTEXT.md and context/CHANGELOG_INTELLIGENCE.md to move the DLQ test entry to “Shipped (m12-pr15)” and add the PR ledger row.

Reviewed changes

Copilot reviewed 3 out of 4 changed files in this pull request and generated 1 comment.

File Description
context/TESTING_CONTEXT.md Moves DLQ contract tests from “Planned” to “Shipped” with a brief invariant summary.
context/CHANGELOG_INTELLIGENCE.md Adds the m12-pr15 ledger row and corrects the m12-pr14 PR reference to #32.
backend/tests/queue/test_dlq.py New DLQ contract test suite covering schema, crash-safety ordering, truncation, metrics, branch distinction, bytes decoding, replay-tool compatibility, and success-path non-DLQ.
backend/tests/queue/init.py New (empty) package initializer for queue tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +235 to +238
- `tests/queue/test_dlq.py` — DLQ contract pin: 5-field schema, xadd-
before-ack ordering, reason truncation (500 chars), metric increment,
inline `delivery_attempt > max_deliveries` branch, bytes-field decode,
round-trip with `scripts/ops/dlq_replay.py::_build_replay_payload`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants