# 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, an `auth.users` table and `auth.uid()` from a JWT claim. The real
  `stripe` npm SDK 23.1.0 (`constructEventAsync` + SubtleCrypto provider) and real `@supabase/supabase-js` 2.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 real `verify_jwt` gateway
  behaviour; the 401 claim comes from Supabase's docs and the reports listed in the handoff), a live Stripe account or `stripe listen`,
  real Next.js build/runtime (the route is exercised by calling its exported `POST` with a `Request`), 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)
1. Deno type errors on `rows[0]` (fixed with typed queries).
2. Doctor fix text printed a literal `\n` (reworded).
3. 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.
