test(stripe): regression-guard webhook idempotency fence ordering (BUNYIP-210) #249

Merged
Claude-Run merged 1 commit from test/bunyip-210-webhook-fence-regression into main 2026-06-26 08:02:48 +02:00
Member

The BUNYIP-210 ordering fix (claim event as processing, promote to done only after the handler succeeds, release the claim on failure) shipped in PR #233 but without the regression test the issue's suggested fix called for. Add it.

Extract the fence's core decision out of claim_webhook_event into a pure classify_claim(won_claim, existing_status) so the regression-critical invariant is unit-testable without a live Postgres (CI runs --lib tests with SQLX_OFFLINE and no database). The decision is unchanged: only a done row may short-circuit a redelivery; a won claim is Owned; every other state (no row from a released claim, a stale/in-flight processing row, any non-terminal status) routes to Owned/InFlight so Stripe's retry re-runs the handler instead of being swallowed with a bare 200. That swallow-the-retry behaviour is exactly what the old record-before-success fence got wrong.

Derive PartialEq/Eq/Debug on EventClaim for the assertions and add three tests covering the non-done-never-skipped invariant, the legitimate done short-circuit, and the won-claim-owns-the-work path.

#BUNYIP-210

The BUNYIP-210 ordering fix (claim event as `processing`, promote to `done` only after the handler succeeds, release the claim on failure) shipped in PR #233 but without the regression test the issue's suggested fix called for. Add it. Extract the fence's core decision out of `claim_webhook_event` into a pure `classify_claim(won_claim, existing_status)` so the regression-critical invariant is unit-testable without a live Postgres (CI runs `--lib` tests with SQLX_OFFLINE and no database). The decision is unchanged: only a `done` row may short-circuit a redelivery; a won claim is `Owned`; every other state (no row from a released claim, a stale/in-flight `processing` row, any non-terminal status) routes to `Owned`/`InFlight` so Stripe's retry re-runs the handler instead of being swallowed with a bare 200. That swallow-the-retry behaviour is exactly what the old record-before-success fence got wrong. Derive `PartialEq`/`Eq`/`Debug` on `EventClaim` for the assertions and add three tests covering the non-done-never-skipped invariant, the legitimate `done` short-circuit, and the won-claim-owns-the-work path. #BUNYIP-210
test(stripe): regression-guard webhook idempotency fence ordering (BUNYIP-210)
All checks were successful
E2E / Playwright against deployment (pull_request) Successful in 28s
Check / fmt + clippy + build + tests (pull_request) Successful in 9m49s
Create release / Create release from merged PR (pull_request) Has been skipped
69f11271c9
The BUNYIP-210 ordering fix (claim event as `processing`, promote to `done` only after the handler succeeds, release the claim on failure) shipped in PR #233 but without the regression test the issue's suggested fix called for. Add it.

Extract the fence's core decision out of `claim_webhook_event` into a pure `classify_claim(won_claim, existing_status)` so the regression-critical invariant is unit-testable without a live Postgres (CI runs `--lib` tests with SQLX_OFFLINE and no database). The decision is unchanged: only a `done` row may short-circuit a redelivery; a won claim is `Owned`; every other state (no row from a released claim, a stale/in-flight `processing` row, any non-terminal status) routes to `Owned`/`InFlight` so Stripe's retry re-runs the handler instead of being swallowed with a bare 200. That swallow-the-retry behaviour is exactly what the old record-before-success fence got wrong.

Derive `PartialEq`/`Eq`/`Debug` on `EventClaim` for the assertions and add three tests covering the non-done-never-skipped invariant, the legitimate `done` short-circuit, and the won-claim-owns-the-work path.

#BUNYIP-210
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!249
No description provided.