feat: launch local-first Sudoku workbench
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { SUDOKU_DOCUMENT_SCHEMA } from "../../src/formats";
|
||||
import {
|
||||
PROJECT_RECORD_SCHEMA,
|
||||
ProjectLibrary,
|
||||
createProjectRecord,
|
||||
normalizeProjectRecord,
|
||||
} from "../../src/storage";
|
||||
|
||||
const puzzle = {
|
||||
schema: SUDOKU_DOCUMENT_SCHEMA,
|
||||
version: 1 as const,
|
||||
size: 9,
|
||||
givens: Array<number>(81).fill(0),
|
||||
constraints: [],
|
||||
};
|
||||
|
||||
describe("local project library", () => {
|
||||
it("stores independent bounded clones in the memory fallback", async () => {
|
||||
const library = new ProjectLibrary({ indexedDB: null });
|
||||
const record = createProjectRecord(puzzle, {
|
||||
id: "one",
|
||||
title: "First",
|
||||
now: 100,
|
||||
progress: {
|
||||
version: 1,
|
||||
values: Array<number>(81).fill(0),
|
||||
cornerMarks: Array.from({ length: 81 }, () => [1, 2]),
|
||||
centerMarks: Array.from({ length: 81 }, () => [3]),
|
||||
colors: Array<number>(81).fill(2),
|
||||
elapsedMs: 1234,
|
||||
},
|
||||
});
|
||||
await library.put(record);
|
||||
const loaded = await library.get("one");
|
||||
expect(loaded).toEqual(record);
|
||||
(loaded?.puzzle.givens as number[])[0] = 9;
|
||||
expect((await library.get("one"))?.puzzle.givens[0]).toBe(0);
|
||||
expect(library.mode).toBe("memory");
|
||||
});
|
||||
|
||||
it("lists newest first, deletes and clears", async () => {
|
||||
const library = new ProjectLibrary({ indexedDB: null });
|
||||
await library.put(
|
||||
createProjectRecord(puzzle, { id: "old", title: "Old", now: 1 }),
|
||||
);
|
||||
await library.put(
|
||||
createProjectRecord(puzzle, { id: "new", title: "New", now: 2 }),
|
||||
);
|
||||
expect((await library.list()).map(({ id }) => id)).toEqual(["new", "old"]);
|
||||
expect(await library.delete("old")).toBe(true);
|
||||
expect(await library.delete("missing")).toBe(false);
|
||||
await library.clear();
|
||||
expect(await library.list()).toEqual([]);
|
||||
});
|
||||
|
||||
it("exports and imports an explicitly versioned library", async () => {
|
||||
const source = new ProjectLibrary({ indexedDB: null });
|
||||
await source.put(createProjectRecord(puzzle, { id: "one", now: 10 }));
|
||||
const exported = await source.exportAll();
|
||||
const target = new ProjectLibrary({ indexedDB: null });
|
||||
expect(await target.importAll(exported)).toBe(1);
|
||||
expect(await target.get("one")).toEqual(await source.get("one"));
|
||||
});
|
||||
|
||||
it("falls back when IndexedDB cannot be opened", async () => {
|
||||
const broken = {
|
||||
open: () => {
|
||||
throw new Error("blocked");
|
||||
},
|
||||
} as unknown as IDBFactory;
|
||||
const library = new ProjectLibrary({ indexedDB: broken });
|
||||
expect(await library.ready()).toBe("memory");
|
||||
await library.put(createProjectRecord(puzzle, { id: "fallback", now: 1 }));
|
||||
expect(await library.get("fallback")).toBeDefined();
|
||||
});
|
||||
|
||||
it("rejects malformed record versions and progress", () => {
|
||||
expect(() =>
|
||||
normalizeProjectRecord({
|
||||
schema: PROJECT_RECORD_SCHEMA,
|
||||
version: 2,
|
||||
}),
|
||||
).toThrow(/Unsupported project record/u);
|
||||
const record = createProjectRecord(puzzle, { id: "bad", now: 1 });
|
||||
expect(() =>
|
||||
normalizeProjectRecord({
|
||||
...record,
|
||||
progress: { version: 1, values: [0] },
|
||||
}),
|
||||
).toThrow(/must contain 81/u);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user