TEST REPORT: Paid-But-Locked Fix (Stripe + Supabase) v1.0.0
Run by DENO=~/.local/bin/deno MUTATIONS=1 bash tests/run_all.sh; full output in tests/logs/run-20261102.txt.
Started 2026-11-02 07:41:04 GMT, finished 07:44:40 GMT (from date). Box: Linux, Deno 2.9.8, Node v24.22.1.
Result: 5/5 steps, FAILED STEPS: 0. 21 Deno tests (webhook, SQL, snippets, Next.js adapter) + 5 doctor tests (Deno also runs these
under Node compatibility, so its line reads "26 passed"), doctor exit codes on both fixtures, mutation check 10/10 caught.
What is real and what is not
- Real: the shipped migration SQL on a real Postgres engine (PGlite 0.5.14 = Postgres compiled to WASM) with Supabase-like roles
(
anon,authenticated,service_role), default grants, anauth.userstable andauth.uid()from a JWT claim. The realstripenpm SDK 23.1.0 (constructEventAsync+ SubtleCrypto provider) and real@supabase/supabase-js2.118.0 calling.rpc(). Signatures are made by an independent WebCrypto HMAC signer in the tests, not by the SDK being tested. - Mocked: PostgREST (a ~40-line stand-in serving
POST /rest/v1/rpc/<fn>, running the SQL as the role in the key) and Stripe (events are hand-built JSON in the snapshot format; no Stripe API calls). - Not tested: a live Supabase project (real Kong/PostgREST/GoTrue,
supabase functions deploy, the realverify_jwtgateway behaviour; the 401 claim comes from Supabase's docs and the reports listed in the handoff), a live Stripe account orstripe listen, real Next.js build/runtime (the route is exercised by calling its exportedPOSTwith aRequest), Postgres versions other than PGlite's 18.x, Vercel Edge runtime.
1. Webhook + SQL (tests/webhook.test.ts, 17)
Happy path (link + grant, billing_access.has_access); subscription event before checkout link still unlocks once linked; duplicate
event id is a no-op (200 duplicate); stale (older event.created) update never overwrites; deleted removes access and a late "active"
can't resurrect it; status policy (active/trialing/past_due grant; unpaid/incomplete/incomplete_expired/paused/canceled don't);
pre-Basil top-level current_period_end still read; signature failures -> 400 with nothing written (missing header, wrong secret,
tampered body, stale timestamp, re-stringified body); GET -> 405; DB failure -> 500, event not marked processed, retry then succeeds;
an error inside the SQL function rolls back the event record too; unlinkable checkouts recorded and 200'd with an outcome; one-time
async payment unlocks only after async_payment_succeeded; unhandled types -> 200 ignored; signed-in users cannot call the RPC,
write billing rows or see other users' rows; Deno fact check: sync constructEvent() throws "SubtleCryptoProvider cannot be used in a
synchronous context", constructEventAsync works; missing env var fails at startup.
2. Snippets + Next.js (tests/snippets_and_next.test.ts 3, tests/next_adapter.test.ts 1)
waitForAccess resolves once access lands, gives up at the timeout, tolerates transient errors. Next.js route: valid delivery 200 +
access granted; bad signature 400.
3. Doctor (tests/doctor.test.mjs, 5 + 2 exit-code steps)
fixtures/bad-app triggers all 9 rules at the expected files; fixtures/good-app (a copy of the shipped code with config.toml) is
has zero findings; CLI exit codes 1 / 0 / 2 (errors / clean / bad dir) and --json parses; a commented-out req.json() causes no finding; fixture files are unchanged after a scan (read-only). Running the doctor on the pack folder itself
reports VERIFY-JWT-ON, which is expected: the pack ships a config.toml.snippet, not a config.toml.
4. Mutation check (tests/mutation_check.sh, 10/10 caught)
Each bug is injected into a copy and the suite must fail: RPC executable by authenticated; out-of-order guard removed; canceled can be resurrected; dedupe removed; past_due no longer grants; one-time granted before payment; UPDATE policy for users; JSON parse + re-stringify before verify; 200 on DB failure; verification skipped.
Failures seen during development (fixed before the logged run)
- Deno type errors on
rows[0](fixed with typed queries). - Doctor fix text printed a literal
\n(reworded). - The "comments ignored" doctor test passed without testing anything; added a fixture with a commented-out
req.json()so it now fails if comment stripping breaks.
Nothing was published, deployed or sent.
Pre-launch re-run 2026-11-02
DENO=~/.local/bin/deno MUTATIONS=1 bash tests/run_all.sh; full output in tests/logs/run-20261102-prelaunch.txt, started 08:00:30 GMT, finished 08:03:59 GMT. 5/5 steps, FAILED STEPS: 0: 26 Deno-run tests passed (21 + the 5 doctor tests under Node compatibility), 5/5 doctor tests under Node, doctor exit codes 0/1 on the good/bad fixtures, mutation check 10/10 caught. Counts unchanged. Still not tested against a live Supabase project or live Stripe account.
Extra pre-launch check: the shipped migration on a throwaway native PostgreSQL 17.11 (not PGlite), with the same Supabase-like roles: 8/8 checks passed (migration applies; subscription-before-checkout unlocks once linked; other user sees nothing; duplicate event is a no-op; deleted is not resurrected by a stale event; anon and authenticated cannot call stripe_apply_event; a signed-in user cannot insert billing rows: RLS refuses it). Output appended to tests/logs/run-20261102-prelaunch.txt. Stripe test mode was not run: no test-mode Stripe account or key was available.