game3 / client / test / frames.test.mjs
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// Golden-vector tests: wire frames must serialize byte-identically to the TS
// implementation's (fixtures generated while the TS was canon — recorded off
// the real hostTick/sendSnap/broadcastRoster code paths).
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"));
const byName = Object.fromEntries(vectors.map((v) => [v.name, v]));

// Each builder is called with the values the fixture recorded, so a key-order
// or omitted-field regression shows up as a byte diff.
const built = {
  "ro-idle": () => lib.mkRo("12D3KooWA", [["12D3KooWA", "zed", 0, 2], ["12D3KooWB", "rex", 1, 0]], false),
  "ro-live": () => lib.mkRo("12D3KooWA", [["12D3KooWA", "zed", 0, 2], ["12D3KooWB", "rex", 1, 0]], true),
  hi: () => lib.mkHi("zed", 3),
  in: () => lib.mkIn(3),
  fu: () => lib.mkFu(),
  st: () => lib.mkSt(2, 125, 360, ["12D3KooWA", "12D3KooWB"], [[20, 4, 2], [20, 26, 0]]),
  tk: () => lib.mkTk(1, [[0, 20, 5], [1, 20, 25]], null, []),
  "tk-newbloom": () => lib.mkTk(24, [[0, 20, 6], [1, 20, 24]], [[21, 4]], []),
  "tk-splash": () => lib.mkTk(6, [[0, 20, 7], [1, 20, 23]], null, [0]),
  ov: () => lib.mkOv("12D3KooWA", [31, 11], [["12D3KooWA", 1], ["12D3KooWB", 0]]),
  "ov-draw": () => lib.mkOv(null, [1, 1], [["12D3KooWA", 0], ["12D3KooWB", 0]]),
  ma: () => lib.mkMa("12D3KooWA", [["12D3KooWA", 3], ["12D3KooWB", 0]]),
  lo: () => lib.mkLo(),
  sn: () => {
    const v = byName.sn.frame;
    return lib.mkSn(v.round, v.tickMs, v.ticks, v.n, v.order, v.ps, v.runs, v.bl);
  },
};

test("every recorded frame type has a builder under test", () => {
  assert.deepEqual(Object.keys(built).sort(), vectors.map((v) => v.name).sort());
});

for (const [name, make] of Object.entries(built)) {
  test(`frame ${name} serializes byte-identically to TS`, () => {
    assert.equal(JSON.stringify(make()), byName[name].json);
  });
}

test("tk omits nb and sp when empty, exactly as the TS spread did", () => {
  assert.equal(JSON.stringify(lib.mkTk(4, [[0, 1, 1]], null, [])), '{"t":"tk","n":4,"h":[[0,1,1]]}');
  assert.equal(JSON.stringify(lib.mkTk(4, [], null, null)), '{"t":"tk","n":4,"h":[]}');
  // and carries them, in wire order t,n,h,nb,sp, when present
  assert.equal(
    JSON.stringify(lib.mkTk(4, [[0, 1, 1]], [[2, 3]], [0])),
    '{"t":"tk","n":4,"h":[[0,1,1]],"nb":[[2,3]],"sp":[0]}',
  );
});

test("ro omits live unless the sender is host mid-round", () => {
  assert.ok(!JSON.stringify(lib.mkRo("h", [], false)).includes("live"));
  assert.ok(JSON.stringify(lib.mkRo("h", [], true)).includes('"live":1'));
});

test("isGameFrame accepts frames, rejects junk", () => {
  for (const v of vectors) assert.equal(lib.isGameFrame(v.frame), true, v.name);
  for (const junk of [null, undefined, 42, "hi", [], {}, { t: 7 }]) {
    assert.equal(Boolean(lib.isGameFrame(junk)), false, `junk: ${JSON.stringify(junk)}`);
  }
});

static mirror of HEAD · about · clone: git clone https://git.ardegazu.ro/game3.git