From 71d8e532d8aa284393f2e2c1094c1879a4bc6332 Mon Sep 17 00:00:00 2001 From: garethx Date: Wed, 26 Aug 2026 10:42:11 +0100 Subject: [PATCH] Report what Hookdeck did when the retry test times out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This test fails intermittently — roughly one full run in four, never when the file runs alone. `test:live` runs on every pull request, so it fails unrelated work, and "timed out waiting for a delivery" is all the evidence that survives. That message cannot distinguish an event that was never created, one still queued, one delivered against a connection the CLI was no longer attached to, or a retry rule that was replaced before it applied. Those have different fixes, and guessing between them has already cost several rounds. So the timeout now reports the event and its attempt triggers, the connection's paused and disabled state, and the rules actually in force. It does not fix the flake: it makes the next occurrence say what happened, in CI, without needing to be reproduced afterwards. Co-Authored-By: Claude Opus 5 --- test/live/delivery.test.mjs | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/test/live/delivery.test.mjs b/test/live/delivery.test.mjs index 619072d..69145c3 100644 --- a/test/live/delivery.test.mjs +++ b/test/live/delivery.test.mjs @@ -151,7 +151,37 @@ test('real deliveries into the node', { skip, concurrency: false }, async (t) => ); assert.ok(accepted.ok, `the edge refused the event: ${accepted.status}`); - const delivered = await receiver.waitFor(marker, { attempts: 150 }); + // On timeout, say what Hookdeck actually did. "No delivery arrived" cannot + // distinguish an event that was never created, one still queued, one + // delivered elsewhere, or a retry rule that never applied — and those need + // different fixes. This failure is intermittent and has so far only been + // seen in a full run, so the evidence has to be captured when it happens + // rather than reproduced afterwards. + let delivered; + try { + delivered = await receiver.waitFor(marker, { attempts: 150 }); + } catch (error) { + const { models: events = [] } = await api( + 'GET', + `/events?source_id=${connection.source.id}&limit=5`, + ); + const seen = []; + for (const event of events) { + const { models: tries = [] } = await api('GET', `/attempts?event_id=${event.id}&limit=10`); + seen.push( + `${event.id} ${event.status} [${tries + .map((a) => `${a.trigger}:${a.response_status}`) + .join(', ')}]`, + ); + } + const state = await api('GET', `/connections/${connection.id}`); + throw new Error( + `${error.message}\n refusals=${refusals} recorded=${receiver.deliveries.length}` + + `\n connection paused=${state.paused_at} disabled=${state.disabled_at}` + + `\n rules=${JSON.stringify(state.rules)}` + + `\n events=${seen.join(' | ') || 'none'}`, + ); + } receiver.setHandler(null); assert.equal(refusals, 1, 'the outage was never exercised');