// Golden-vector tests for the wire protocol: every frame must serialize
// byte-identically to what the deployed TypeScript build emits (a scrambled key
// order compiles fine and desyncs every client — dev/docs/CLJS.md), and
// isGameFrame must accept and reject exactly what the TS did.
import test from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import * as lib from "../test-dist/testlib.js";
const v = JSON.parse(readFileSync(new URL("./vectors/frames.json", import.meta.url), "utf8"));
const { A, B } = v.ids;
const bs = (score, over = false, grid = ".".repeat(200), snake = null) =>
({ grid, score, lines: 0, level: 1, snake, over });
// One builder per frame shape, in the same order as the recorded table.
const builders = [
() => lib.mkHi("zed", 2),
() => lib.mkLv(),
() => lib.mkLo(),
() => lib.mkRo(A, [[A, "zed", 1]], "race", 5000, 1),
() => lib.mkRo(A, [], "survival", 100, 0),
() => lib.mkSt(3, 12345, [A, B], "race-survival", 2000),
() => lib.mkBs(3, 1, bs(42)),
() => lib.mkOv(3, null, "void", [], []),
() => lib.mkOv(3, A, "survivor", [[A, "zed", 900, 0]], [[A, "zed", 2]]),
() => lib.mkSn(4, [A], "race", 500),
];
test("every frame serializes byte-identically to the TS original", () => {
assert.equal(builders.length, v.good.length, "builder table drifted from the fixture");
for (let i = 0; i < builders.length; i++) {
assert.equal(JSON.stringify(builders[i]()), v.good[i].json, `frame ${i}`);
}
});
test("isGameFrame accepts every real frame", () => {
for (let i = 0; i < builders.length; i++) {
assert.equal(v.good[i].accepted, true, `fixture ${i} should have been accepted by the TS`);
assert.equal(Boolean(lib.isGameFrame(JSON.parse(v.good[i].json))), true, v.good[i].json);
assert.equal(Boolean(lib.isGameFrame(builders[i]())), true, `built frame ${i}`);
}
});
test("isGameFrame rejects exactly the junk the TS rejected", () => {
for (const { desc, accepted } of v.junk) {
assert.equal(accepted, false, `fixture says the TS accepted ${desc}`);
const value = desc === "undefined" ? undefined : JSON.parse(desc);
assert.equal(Boolean(lib.isGameFrame(value)), false, desc);
}
});
test("boardStateOf: key order and grid serialization off a real game", () => {
for (const { seed, json, grid } of v.boardStates) {
const g = lib.makeGame({}, seed);
for (let i = 0; i < 5; i++) lib.gameHardDrop(g);
assert.deepEqual(
[...g.board.grid].map((r) => [...r].map((c) => (c === 0 ? "." : c)).join("")),
grid,
`seed ${seed}: board`,
);
assert.equal(JSON.stringify(lib.boardStateOf(g)), json, `seed ${seed}: bs bytes`);
}
});
test("serializeGrid drops the hidden spawn rows", () => {
const b = lib.makeBoard();
const { HIDDEN_ROWS, COLS, ROWS } = lib.boardConsts();
b.grid[0][0] = "I"; // hidden — must not appear
b.grid[HIDDEN_ROWS][0] = "T"; // first visible cell
const s = lib.serializeGrid(b.grid);
assert.equal(s.length, COLS * ROWS);
assert.equal(s[0], "T");
assert.ok(!s.includes("I"));
});
test("mode labels and protocol constants are the deployed values", () => {
assert.deepEqual({ ...lib.modeLabel }, v.modeLabel);
assert.equal(lib.protocolOf("0123456789abcdefGARBAGE"), "/game2/2/0123456789abcdef");
});