Skip to content

fix(sqs): dead_letter never worked — receive() did not retain message bodies#11

Merged
abhi-bhat-lyzr merged 1 commit into
LYZR-OSS:mainfrom
parshva-lyzr:fix/sqs-dead-letter-pending
Jun 12, 2026
Merged

fix(sqs): dead_letter never worked — receive() did not retain message bodies#11
abhi-bhat-lyzr merged 1 commit into
LYZR-OSS:mainfrom
parshva-lyzr:fix/sqs-dead-letter-pending

Conversation

@parshva-lyzr

Copy link
Copy Markdown
Contributor

Summary

Fixes the 7 test-setup errors on main (NameError: DLQ_NAME is not defined) — and the real bug they were masking.

The bug

The new SQS dead_letter() emulation reads the original message body from self._pending (documented as "retained between receive() and delete()"), but receive() never stored anything there. Every dead_letter() call on SQS raised No pending message for receipt handle unconditionally. It shipped untested: the fixture was updated to wire a RedrivePolicy for dead-letter tests, but the DLQ_NAME constant was missing, so the fixture errored at setup — and no dead-letter tests existed anyway. (The Azure implementation was already correct.)

Changes

  • receive() retains receipt_handle → raw body in _pending so dead_letter() can forward the original payload to the DLQ
  • nack() drops the pending entry (the handle goes stale on redelivery; the redelivered message stores a fresh one)
  • close() / purge() clear the pending map (no leak across lifecycle events)
  • Tests: define DLQ_NAME; add the missing coverage —
    • SQS via moto: dead-letter moves the message to the DLQ with the DeadLetterReason message attribute and deletes it from the source; unknown handle raises; queue with no RedrivePolicy and no dlq_url= raises a clear MessagingError; explicit dlq_url= constructor path; nack-then-dead-letter works on the fresh handle; get_queue_depth()
    • Azure via mocks: dead_letter_message(reason=..., error_description=...) called and receiver released; unknown handle raises; get_queue_depth() resolves through ServiceBusAdministrationClient

Test plan

  • pytest tests/195 passed, 0 errors (was 179 passed + 7 errors)
  • ruff check clean on all touched files
  • Rebuilt the wheel and re-ran the live (non-pytest) harnesses: cloudrift livetests/ 6/6 against a real moto SQS server + local Redis/Mongo; lyzr-memory's 9-scenario live suite (wheel installed into its venv, real uvicorn boots of both app trees) all green

… bodies

The SQS dead_letter() emulation reads the original message body from
self._pending, but receive() never populated it, so every dead_letter()
call raised 'No pending message' regardless of the handle. The missing
DLQ_NAME test constant masked this: the fixture errored at setup before
any dead-letter test could run (and none existed).

- receive(): retain receipt_handle -> raw body in _pending
- nack(): drop the pending entry (handle goes stale on redelivery;
  the redelivered message stores a fresh one)
- close()/purge(): clear the pending map
- tests: define DLQ_NAME; add SQS coverage via moto (dead-letter moves
  the message to the DLQ with the DeadLetterReason attribute and deletes
  it from the source, unknown handle raises, no-RedrivePolicy/no-dlq_url
  raises, explicit dlq_url path, nack-then-dead-letter, get_queue_depth)
  and Azure mock coverage (dead_letter_message call + receiver release,
  unknown handle, get_queue_depth via the management client)

195 passed (was 179 passed + 7 fixture errors)
@sonarqubecloud

Copy link
Copy Markdown

@abhi-bhat-lyzr abhi-bhat-lyzr merged commit d8a8ad0 into LYZR-OSS:main Jun 12, 2026
2 checks passed
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.

4 participants