Files
OpenFrontIO/tests/StatsSchema.test.ts
T
Evan Pelle 43d07ca85f refactor: rename core-public -> engine-public; fold helpers in; drop shared
Tighten the package model around the determinism invariant: everything the
engine imports must itself be deterministic.

- Rename core-public -> engine-public (package, dir, `engine-public/*` alias,
  all 118 import sites). It is the deterministic public surface the engine
  depends on and that client/server also use.
- Move the pure formatting helpers (renderNumber/renderTroops) from shared
  into engine-public/format — the engine uses them and they're deterministic.
- Drop the now-empty shared package (and its alias). `shared` was intended for
  client/server-shared, engine-forbidden code; there is none yet, so it is
  removed and can be re-added when such code appears.

Final graph: engine-public (leaf) <- engine <- {client, server};
client/server also -> engine-public.

Verified: tsc clean; 1364 + 65 tests pass; prod build succeeds; engine-public
is a leaf; engine has no client/server imports. Lockfile regenerated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-10 20:32:32 +00:00

78 lines
2.0 KiB
TypeScript

import { PlayerStatsLeafSchema } from "engine-public/ApiSchemas";
import { PlayerStatsSchema } from "engine-public/StatsSchemas";
function testPlayerSchema(
json: string,
expectSuccess = true,
expectThrow = false,
): void {
const parse = () => {
const raw = JSON.parse(json);
const result = PlayerStatsSchema.safeParse(raw);
return result.success;
};
if (expectSuccess) {
// Expect success
expect(parse()).toBeTruthy();
} else if (!expectThrow) {
// Expect failure
expect(parse()).toBeFalsy();
} else {
// Expect throw
expect(parse).toThrow();
}
}
describe("StatsSchema", () => {
test("Parse empty", () => {
testPlayerSchema("{}");
});
test("Parse partial", () => {
testPlayerSchema('{"units":{"port":["0","0","0","1"]}}');
});
test("Parse invalid", () => {
testPlayerSchema("[]", false);
testPlayerSchema("null", false);
testPlayerSchema('"null"', false);
testPlayerSchema('"undefined"', false);
});
test("Parse failure", () => {
testPlayerSchema("", false, true);
testPlayerSchema("undefined", false, true);
testPlayerSchema("{", false, true);
testPlayerSchema("{}}", false, true);
});
test("null array elements coerce to 0n (LEFT JOIN rows with no stats)", () => {
// Postgres SUM() over all-NULL rows returns NULL. These should parse as 0n.
testPlayerSchema(
'{"attacks":[null,null,null],"betrayals":null,"gold":[null,null,null,null,null,null]}',
);
});
});
describe("PlayerStatsLeafSchema", () => {
test("null stat values coerce to 0n", () => {
const result = PlayerStatsLeafSchema.safeParse({
wins: "0",
losses: "1",
total: "1",
stats: { attacks: [null, null, null], betrayals: null },
});
expect(result.success).toBe(true);
});
test("missing required field (wins) still fails — undefined is not coerced", () => {
const result = PlayerStatsLeafSchema.safeParse({
losses: "1",
total: "1",
stats: {},
});
expect(result.success).toBe(false);
});
});