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

forked from trevato/cs

The room's two doors are tested where they are opened: POST /api/room and the socket upgrade answer to src/server.test.ts #352

Merged opened this

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

Merged

`src/server.test.ts` grows a section at the end, "the room's two doors", and `src/server.ts` exports one thing it did not before — the hub — so a test can hand a room back; no behaviour changed.

shipped production is serving it deploy ae07c2d

opened this

src/server.test.ts grows a section at the end, "the room's two doors", and src/server.ts exports one thing it did not before — the hub — so a test can hand a room back; no behaviour changed.

POST /api/room is now tested at the door: it answers a code as no-store application/json with the whole security floor and the session cookie it arrived with; an unknown map is de_forge and only the exact word long is the long match (the room the hub holds is checked too, rules and all); a cross-origin POST, a GET, a HEAD, {, and a body past the kilobyte cap are all refused and leave no world behind; one caller gets three rooms then a 429 with retry-after: 20; and past ROOM_CAP the answer is a 503 whose sentence names the cap. The upgrade: no room is 400 "not a room code", a well-formed code for no room is 404 with its sentence, another site is 403 before the room is even looked up, and a plain GET /ws — with a real code, lower-cased, so it is the upgrade that failed and not the lookup — is the app's own 404 document. The 101 is read off a raw socket in handshake, because a WebSocket hands a page the socket and never the response, and it carries the session cookie and the four floor headers. Then a real client: hello parsed by readDown and a snapshot applied by applySnapshot against a world carved from the seed it was handed; a name of ten control codes and twenty-six letters comes back as fourteen, which is the door's cut at 24 and not the room's at 20; a text frame of exactly INPUT_BYTES is dropped while the binary frame behind it drives the seat; and a frame past MESSAGE_MAX closes the connection with a code that is the runtime's and never the hub's own 4001.

Worth a look: each test takes a session cookie nobody else uses, so ROOM_LIMIT — three per caller, keyed by address and session — imposes no order on the file and the flood test sits in the middle rather than last. The ROOM_CAP test fills nineteen rooms through the hub and opens only the twentieth through the route; nineteen POSTs would want seven callers, and nineteen rooms cost 6.5 ms measured, so the limiter picked that and not the clock. Every test closes its sockets and deletes its room, and the last test reads hub.rooms and hub.members() to prove it. Ten mutations of src/server.ts were run against the new tests — the cut at 24, the cookie on the 101, the 404 on an unknown code, maxPayloadLength, the upgrade's same-origin check, the room limiter, the map fallback, the length, no-store, the text-frame guard — and each was caught by the test that claims it.

Wall time: bun test src/server.test.ts was 1.10 s for 73 tests and is 1.17 s for 88; the whole suite is 23–25 s before and after, which is its own run-to-run noise. The section lives in this 1,764-line file rather than in a sibling because bun test uses one process and one module registry: a second file importing ./server.ts would share this file's server and, running first, would set FORGE_APP_DATA after the bundles were built somewhere else — the docstring says so. One thing I did not fix: src/range/boot.test.ts fails about once in twenty-five runs on its own, because the range seeds its targets from Date.now() (src/range/main.ts:73); that is #351, and it is not this pull's file.

🤖 Generated with Claude Code

Closes #335

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs