test(custom): pin the import gate
A file being imported is not necessarily one this reader wrote. Extracts the record validator so both the store read and the import path share it, and covers what it has to guarantee: a partly-corrupt file costs the reader only the broken records, a hue that is not a hex colour never reaches a style attribute, an export written before F13 is a file with nothing of its own rather than an error, and an event whose dates contradict themselves does not land. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
c87ea4c602
commit
6de1434115
+53
-1
@@ -16,7 +16,7 @@ import { metaFor } from "../src/shared/games.ts";
|
||||
import { dailiesId } from "../src/shared/daily.ts";
|
||||
import { eventId, GameId } from "../src/shared/schema.ts";
|
||||
import { clockFor } from "../src/shared/time.ts";
|
||||
import { readerInstant } from "../src/client/state/useCustom.ts";
|
||||
import { readerInstant, validRecords } from "../src/client/state/useCustom.ts";
|
||||
|
||||
const AT = "2026-08-17T12:00:00.000Z";
|
||||
|
||||
@@ -284,3 +284,55 @@ describe("readerInstant", () => {
|
||||
expect(readerInstant("2026-02-30", null, "start")).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("validRecords — the import gate", () => {
|
||||
test("keeps the good records and drops only the bad ones", () => {
|
||||
// A partly-corrupt file must not cost the reader the parts that are fine.
|
||||
const kept = validRecords(
|
||||
{
|
||||
"mygame:a": { id: "mygame:a", name: "A", hue: "#123456", at: AT },
|
||||
"mygame:b": { id: "mygame:b", name: "B", hue: "not-a-colour", at: AT },
|
||||
"mygame:c": "nonsense",
|
||||
},
|
||||
CustomGame,
|
||||
);
|
||||
expect(Object.keys(kept)).toEqual(["mygame:a"]);
|
||||
});
|
||||
|
||||
test("refuses a hue that is not a hex colour", () => {
|
||||
// It reaches a style attribute, and an import is not necessarily a file
|
||||
// this reader wrote.
|
||||
const kept = validRecords(
|
||||
{
|
||||
"mygame:x": {
|
||||
id: "mygame:x",
|
||||
name: "X",
|
||||
hue: "red; background:url(javascript:alert(1))",
|
||||
at: AT,
|
||||
},
|
||||
},
|
||||
CustomGame,
|
||||
);
|
||||
expect(kept).toEqual({});
|
||||
});
|
||||
|
||||
test("an export written before F13 simply has none", () => {
|
||||
// Not an error — a file from a device that had nothing of its own.
|
||||
expect(validRecords(undefined, CustomEvent)).toEqual({});
|
||||
expect(validRecords(null, CustomEvent)).toEqual({});
|
||||
});
|
||||
|
||||
test("drops an event whose dates contradict themselves", () => {
|
||||
const kept = validRecords(
|
||||
{
|
||||
"myevent:aaaaaaaaaa": {
|
||||
...ownEvent(),
|
||||
id: "myevent:aaaaaaaaaa",
|
||||
endsAt: "2026-08-01T00:00:00.000Z",
|
||||
},
|
||||
},
|
||||
CustomEvent,
|
||||
);
|
||||
expect(kept).toEqual({});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user