// Golden-vector tests: wire frames must serialize byte-identically to the TS
// implementation's (fixtures recorded off the real game brain driven through a
// fake NetLike, so they are exactly the bytes the deployed clients emit).
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 vectors = JSON.parse(readFileSync(new URL("./vectors/frames.json", import.meta.url), "utf8"));
// Every builder is called with the values the fixture recorded, so a key-order
// or dropped-field regression shows up as a byte diff. `f` is the recorded
// frame, so the arguments come from the fixture rather than from this file.
const BUILD = {
ro: (f) => lib.mkRo(f.host, f.ps, f.age),
tr: (f) => lib.mkTr(f.bpm, f.org),
pg: (f) => lib.mkPg(f.c),
po: (f) => lib.mkPo(f.c, f.h),
hi: (f) => lib.mkHi(f.nick, f.inst),
sn: (f) => lib.mkSn(f.bpm, f.org, f.loops),
nt: (f) => lib.mkNt(f.i, f.n, f.v),
nf: (f) => lib.mkNf(f.i, f.n),
ts: () => lib.mkTs(),
tc: (f) => lib.mkTc(f.f, f.g),
te: () => lib.mkTe(),
lp: (f) => lib.mkLp(f.id, f.i, f.len, f.ev),
lx: (f) => lib.mkLx(f.id),
ap: () => lib.mkAp(),
};
test("the fixtures cover every frame type the protocol defines", () => {
const seen = new Set(vectors.map((v) => v.frame.t));
assert.deepEqual([...seen].sort(), Object.keys(BUILD).sort());
});
for (const [i, v] of vectors.entries()) {
test(`${v.name} #${i}: "${v.frame.t}" serializes byte-identically to TS`, () => {
const build = BUILD[v.frame.t];
assert.ok(build, `no builder for ${v.frame.t}`);
assert.equal(JSON.stringify(build(v.frame)), v.json);
});
}
test("no frame builder is wider than the eight-pair js-obj key-order limit", () => {
// dev/docs/CLJS.md: at nine pairs `js-obj` silently emits in hash order.
for (const v of vectors) {
assert.ok(
Object.keys(v.frame).length <= 8,
`${v.frame.t} has ${Object.keys(v.frame).length} keys — build it with sequential unchecked-set`,
);
}
});
test("isGameFrame accepts frames, rejects junk", () => {
for (const v of vectors) assert.equal(lib.isGameFrame(v.frame), true, v.frame.t);
for (const junk of [null, undefined, 42, "hi", [], {}, { t: 7 }]) {
assert.equal(Boolean(lib.isGameFrame(junk)), false, `junk: ${JSON.stringify(junk)}`);
}
// an array IS typeof "object" but carries no string `t`
assert.equal(Boolean(lib.isGameFrame(["ro"])), false);
});
test("the compiled-in constants still match the TS ones", () => {
const inst = JSON.parse(readFileSync(new URL("./vectors/instruments.json", import.meta.url), "utf8"));
const c = lib.constants();
assert.equal(c.appSalt, inst.appSalt);
assert.equal(c.maxMusicians, 8);
assert.equal(c.minBpm, inst.minBpm);
assert.equal(c.maxBpm, inst.maxBpm);
assert.equal(c.defaultBpm, inst.defaultBpm);
assert.deepEqual([...c.loopLengths], inst.loopLengths);
assert.deepEqual([...c.instruments], inst.instruments);
assert.deepEqual([...c.instGlyphs], inst.instGlyphs);
assert.deepEqual([...c.colors], inst.colors);
});