Skip to content

Integration Tests: PeanarJob #59

@martianboy

Description

@martianboy

A. Job Execution & Promise Contract

# Scenario Expected Observation
A1 Successful handler_perform() resolves, no explicit cancel, retry, or reject. channel.ack is sent once; message disappears from queue; promise resolves with handler result.
A2 Handler throws synchronous error. Job rejects promise; reject() path is followed (tests in section C).
A3 Handler returns rejected promise. Same behaviour as A-2.
A4 Handler hangs – exceed a reasonable timeout (e.g. 5 s) to confirm nothing is auto-acked. No ack or reject; message remains unacked (visible via rabbitmqctl list_queues messages_unacknowledged).
A5 Duplicate resolve – handler resolves, then calls resolve again (hack a stub). Only first resolve honoured; no double ack; second resolve ignored (check debug log).

B. Explicit Cancel Flow

# Scenario Expected Observation
B1 Call job.cancel() before _perform() finishes. cancel event emitted, promise rejects with PeanarJobCancelledError; no ack sent.
B2 Call cancel() after _perform() already resolved. Second “late” cancel is ignored; exactly one ack.

C. Reject / Retry Handling

(All require def.retry_exchange to be declared beforehand.)

# Scenario Expected Observation
C1 Retry eligible – handler throws; attempt=1, max_retries=3. Job declares retry queue, channel.reject(requeue=false) sent; message re-emerges on original queue after retry_delay; attempt incremented in payload.
C2 Max retries reached – trigger on final allowed attempt. Job publishes to error_exchange; original delivery is acked; message appears once in error queue/exchange and never again in main queue.
C3 Retry flag off – handler throws an error with retry=false. Message sent directly to error_exchange (or discarded if none).
C4 Unlimited retries – set max_retries=-1; cause 3 consecutive failures. Message retries indefinitely (at least three times within test window).
C5 Retry exchange missing – omit def.retry_exchange but throw error. Job throws PeanarInternalError; consumer channel emits exception; message stays unacked (verify).

D. Ack / Reject Semantics & Delivery-Tag Accuracy

# Scenario Expected Observation
D1 Large delivery tag – publish > 2^53 messages then process one; ensure tag > JS safe integer. ack fails (precision loss) → expect “unknown delivery tag” from broker.
Confirms BigInt→Number truncation bug.
D2 Manual reject path – inside handler call job.reject() explicitly. See channel.reject; message routed per retry/error logic.
D3 Reject when channel lost – close channel before reject. PeanarAdapterError thrown; message redelivers on new channel.

E. Error-Exchange / Dead-Letter Routing

# Scenario Expected Observation
E1 Job final-fails with def.error_exchange set to fanout exchange. One new message arrives in bound error queue with JSON body { id, name, error, args }.
E2 Same but with topic exchange and custom routingKey. Message uses requested routing key.
E3 No error_exchange configured; retries exhausted. Job issues channel.reject(requeue=false); message is discarded (queue’s messages_ready and messages_unacknowledged back to 0).

F. Queue Pause / Resume Delegation

# Scenario Expected Observation
F1 Call pauseQueue() during steady consumption. Broker stops delivering from queue; rabbitmqctl list_consumers shows consumer flow-state = blocked.
F2 Call resumeQueue() and verify delivery resumes. New messages begin flowing; backlog drains.

G. Retry Queue Declaration Side-Effects

# Scenario Expected Observation
G1 Trigger two retries in rapid succession; ensure _declareRetryQueues runs twice. Second call succeeds but broker logs “queue already exists”; verify idempotence (no channel-error 406).
G2 Change retry_delay between attempts (e.g. 1 s then 5 s) and retry. Expect broker precondition error because queue arguments mismatch, illustrating why declaration per job is risky.

H. Channel Failure / Redelivery Edge Cases

# Scenario Expected Observation
H1 Mid-flight channel close – close channel after delivery but before job finishes. Handler finishes; when ack is attempted an exception is thrown; message redelivers via new channel; second processing succeeds.
H2 Connection drop – kill TCP connection; wait for reconnection logic; confirm job is re-delivered exactly once after reconnect.

I. Subclass / Extension-Point Behaviour

# Scenario Expected Observation
I1 Create subclass overriding _perform() and calling super._perform(). Both base and subclass logic run; final ack behaviour unchanged.
I2 Subclass overrides retry() to implement custom back-off; ensure original retry queue still used. Custom delay honoured; retries proceed.

Coverage checklist

These scenarios exercise:

  • Handler success & failure paths
  • ack, reject, cancel intent & side-effects
  • Retry-queue declaration and routing
  • Error-exchange publishing
  • Delivery-tag precision & channel coupling
  • Channel/connection failure resilience
  • Public helper APIs (pauseQueue, resumeQueue, retry, subclass hooks)

Metadata

Metadata

Assignees

Labels

tech-debtTechnical debt accumulated over time

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions