Public: anyone can read this; the crew can change it.

forked from trevato/cs

The doors the board is written through get their first test files: src/gate.ts and src/run.ts #363

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`src/gate.ts` and `src/run.ts` now have their own test files, `src/gate.test.ts` and `src/run.test.ts`. Both test plain functions, with no server. `bun run test` (typecheck, then `bun test`) passes at 1470 tests.

shipped production is serving it deploy ae07c2d

opened this

src/gate.ts and src/run.ts now have their own test files, src/gate.test.ts and src/run.test.ts. Both test plain functions, with no server. bun run test (typecheck, then bun test) passes at 1470 tests.

A refactor with no behaviour change. The door's steps were written inline in route in src/server.ts, so they could only be reached over HTTP. I moved three of them into src/gate.ts without changing them:

  • readBody, which reads the body under its cap, and BODY_MAX.
  • admit(gate, request), which reads, parses the JSON and checks the gate's shape. It returns what came through, or a refusal with its status and words: 413 too large, 400 bad json, or 422 <the gate's refusal>.
  • playerOf, which records a verified identity under its own name and a guest under the name they claimed.

route now calls admit and still answers with the same statuses and the same order: same-origin check, rate limit, body, write, board. A local boot answered / with 200, a POST of nope to /api/run with 400 bad json, and {} with 422 bad run. AGENTS.md now points to readBody in src/gate.ts.

src/gate.test.ts builds its own Requests and a test-only Gate, and checks what a caller sees:

  • The cap: a body exactly at the cap is read whole, and one byte more is 413. A chunked body with no Content-Length gets the same answer, and a body that never ends is cut off rather than read forever.
  • JSON and shape: bodies that aren't JSON get 400, and JSON of the wrong shape gets the gate's own 422.
  • Counts and names: numbers that aren't counts, including -0, NaN, strings, bigints and values over the ceiling, are refused. Names are cleaned, trimmed and cut at 24.
  • Who it belongs to: a guest is recorded under the name they claimed, and a verified identity always under its own name.

src/run.test.ts reads every limit from DRILLS through mostPlates, mostShots, mostScore and mostPerPlate, with no literals, and loops over every drill:

  • A real run is taken: a script that cannot miss plays thirty seconds of each drill through the range's own start, step and shoot, and the run it produces is accepted.
  • Every ceiling from both sides: at the limit is taken, and one hit more, one shot fewer than the hits, one shot too many, a score out of reach, a negative or overlong clock, and a seed outside 32 bits are each refused.
  • Drill, id and name: unknown drill names are refused, including __proto__ and toString, along with bad run ids. A body's invented fields are dropped.

I removed each ceiling in run.ts one at a time and each turned a test red, with one exception: the overall mostScore cap. Hits are capped at mostPlates and score at hits × mostPerPlate, so those two already imply it and no request can reach it on its own. I left it in because this pull changes no behaviour.

A hole, fixed here. cleanName at src/gate.ts:59 only stripped C0 controls and DEL. A post like {"name":"a\u009b2J…"} stored U+009B, which some terminals treat as a control sequence (CSI), in the name. U+2028 and U+2029, which act as line breaks, also got through even though the rule says a name is one line. The class now also covers U+0080–U+009F and U+2028–U+2029, matching the terminal's own printable filter. Only names containing those characters change.

src/independence.test.ts now lists gate.ts, run.ts and result.ts as the app's files, not a game's, and allows run.ts and its test to read range/targets.ts.

I did not add HTTP tests for these doors; #335 and the posting-doors issue cover that end.

To try it: bun test src/gate.test.ts src/run.test.ts. On the preview, a name containing U+009B posted to /api/run now shows a space in its place on the board.

🤖 Generated with Claude Code

Closes #347

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs