Skip to content

test(api): behavioural cover for the raw-route session gate - #246

Merged
rkm1 merged 1 commit into
mainfrom
test/session-gate-behaviour
Sep 8, 2026
Merged

test(api): behavioural cover for the raw-route session gate#246
rkm1 merged 1 commit into
mainfrom
test/session-gate-behaviour

Conversation

@rkm1

@rkm1 rkm1 commented Sep 8, 2026

Copy link
Copy Markdown
Member

Why

requireSessionMiddleware — the single supported gate for the raw
ext.routes escape hatch — merged in #236 with exactly one test: that it
carries the CORE_GATE_MIDDLEWARE tag. Nothing asserted the 401 that is its
entire reason for existing, and nothing asserted that it resolves the session
through #237's per-request memo.

That second gap is why #236 shipped a semantic regression through a textually
clean rebase. The gate was written before the memo landed, so it resolved the
session itself; the only thing that says so is a resolution count, and there
was no test counting.

What the tests found

Writing them turned up two real defects, both fixed here:

1. A live 401→500 on current main. The gate had lost the defensive read of
env.SESSION_SECRET it originally shipped with. Some deployments expose env
as a proxy whose getter throws for an unbound key; the escaping TypeError
turns a route whose documented answer is 401 into a 500. Removing the
duplicate session resolution was right — removing this read along with it
was not. Restored.

2. A comment that claimed something untrue. It said resolving directly
"would hand the handler an unfrozen copy". It would not: the gate used its
session for the null check and discarded it. The actual second cost is that
the gate and the handler behind it would answer from two independent
resolutions, so a revocation landing between them decides the two
differently. Corrected, and now covered by a test asserting they share one
frozen object.

The tests

middleware.test.ts — five behavioural cases: 401 with the handler never
invoked; handler runs and its response passes through for a session; a GET is
gated too (unlike csrf's safe-method skip); fails closed on a missing
SESSION_SECRET; 401 rather than 500 on a throwing getter.

request-identity.test.ts — the gate joins the T2 chain guard, plus three
memo cases: it reuses a resolution the request already paid for, it and the
handler share one frozen session, and the no-context fallback still refuses
an anonymous request.

Verification

Red in both directions, not just green:

Gate body under test Result
#236's original the 3 memo assertions fail
current main the throwing-getter case fails
this branch 119 passed across the 3 files

tsc --build (the repo's lint) clean. No public API surface change, so no
snapshot regeneration.

🤖 Generated with Claude Code

`requireSessionMiddleware` landed in #236 with only one test: that it
carries the `CORE_GATE_MIDDLEWARE` tag. Nothing asserted what it does —
neither the 401 that is its entire reason for existing, nor that it reads
the session through #237's per-request memo.

The second one is not hypothetical. #236 was written before the memo
landed and rebased over it textually clean, so the gate resolved the
session itself; only a resolution count says so, and there was none.

Two defects the tests found, both fixed here:

- The gate had lost the defensive read of `env.SESSION_SECRET` it shipped
  with. Some deployments expose `env` as a proxy whose getter throws for
  an unbound key, and an escaping TypeError turns a route whose documented
  answer is 401 into a 500. Removing the duplicate session resolution was
  right; removing this read with it was not.
- The comment claimed resolving directly "would hand the handler an
  unfrozen copy". It would not — the gate used its session for the null
  check and discarded it. The real second cost is that the gate and the
  handler behind it answer from two independent resolutions, so a
  revocation landing between them decides the two differently. Corrected,
  and covered by a test asserting they share one frozen object.

Verified red in both directions rather than only green here: against
#236's original gate body the three memo assertions fail, and against
current main the throwing-getter case fails — so that one is a live
defect this closes, not a guard against a future one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@rkm1
rkm1 added this pull request to the merge queue Sep 8, 2026
Merged via the queue into main with commit 72fddf0 Sep 8, 2026
15 checks passed
@rkm1
rkm1 deleted the test/session-gate-behaviour branch September 8, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant