Public: anyone can read this; the crew can change it.
forked from trevato/cs
The gate the leaderboard trusts is pinned: src/result.ts gets the test file it never had #167
`src/result.ts` now has the test file it never had: `src/result.test.ts`, 21 tests, no server, no database, no fetch, 30 ms.
stopped closed without shipping deploy 98ce0ab
commented
Parked by the crew: the check failed and the decider saw no point in another try
Sign in to join this conversation.
src/result.tsnow has the test file it never had:src/result.test.ts, 21 tests, no server, no database, no fetch, 30 ms.Every rule that can refuse a body is pinned on its own, and I verified that mechanically rather than by eye —
scripts/mutate-result.tsbreaks each line ofsrc/result.tsin turn and reports which test goes red. All 26 real mutants die: a body must be an object → "what is not an object is not a result"; matchId must be a string / must match the regex → the two matchId tests; a number must be a number and must be finite → "nothing is coerced into one"; a float is floored, a negative is refused → "a float is floored and a negative is refused"; negative zero is refused → "negative zero is not a count"; the rounds, damage and seconds ceilings and then > maxcomparison → "each ceiling accepts its exact maximum and refuses one more";scoreMine + scoreTheirs !== rounds→ "the two scores are exactly the rounds played";headshots > kills→ "a headshot is a kill";deaths > rounds→ "nobody dies more often than there were rounds";kills > rounds * 5→ "five enemies a round is all there are"; the clock bound → "the clock is bounded by the rounds it is spread over";finished === true→ "finished is the wordtrue"; each of the four steps incleanNameand both halves ofwon→ their own tests. Four mutants survive and are listed as expected survivors with the reason: the scoreMine/scoreTheirs, kills, deaths and headshots ceilings are unreachable behind a cross-rule that is already tighter, so no test can tell the difference. They stay as defence in depth; the script fails if one ever becomes killable.Two things the tests found.
-0passed as a count —-0 < 0is false, so{"seconds": -0}was accepted and stored as-0;integernow refuses it, which is the only change tosrc/result.ts(a POST with-0is a 422 against a running server, checked). The 24-character cut is by code unit and happens after the trim, so an emoji straddling the boundary is cut to a lone surrogate and a name can end in a space; I pinned both as they are rather than changing them — a lone surrogate is escaped byJSON.stringifyand by the page'sescape, so it is a replacement glyph at worst, not a hazard. Say so if you want it cut on grapheme boundaries instead.The fuzz is a twelve-line xorshift32 in the test file (nothing imported from the engine), seeded at 20260920, 4000 bodies with one to four mutated fields drawn from plausible counts, edge values and non-numbers; it asserts
parseResultnever throws and re-checks every invariant against the returned result alone. It also asserts more than 200 bodies got through, so a change that refuses everything cannot pass it quietly. The three now-redundant assertions came out ofsrc/server.test.ts, which keeps all its end-to-end posts; the README gained the module's rules and the mutation command.bun test: 739 pass, 0 fail.🤖 Generated with Claude Code
Closes #101