generated from coulomb/repo-seed
Bootstrap @citation-evidence/engine as a standalone TypeScript package with shared types and engine services copied from the umbrella MVP. All 89 tests pass with lint and typecheck clean.
99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
/**
|
|
* Per-session engine snapshot round-trip.
|
|
*
|
|
* The workplan (CE-WP-0005-T01) requires that two sessions persisted
|
|
* under the per-session key scheme can each be restored independently
|
|
* — proving the storage layout actually partitions data by session.
|
|
*/
|
|
|
|
import { describe, expect, it } from "vitest";
|
|
|
|
import type { Document, DocumentRepresentation } from "@shared/document";
|
|
import type { DocumentId, RepresentationId, SessionId } from "@shared/ids";
|
|
|
|
import {
|
|
attachPersister,
|
|
createEngine,
|
|
engineSnapshotKey,
|
|
restoreFromStorage,
|
|
type Engine,
|
|
type EngineSnapshot,
|
|
} from "./index";
|
|
|
|
function memoryStorage(): Pick<Storage, "getItem" | "setItem" | "removeItem"> {
|
|
const map = new Map<string, string>();
|
|
return {
|
|
getItem: (k) => map.get(k) ?? null,
|
|
setItem: (k, v) => void map.set(k, v),
|
|
removeItem: (k) => void map.delete(k),
|
|
};
|
|
}
|
|
|
|
function seedDoc(engine: Engine, label: string): { id: DocumentId } {
|
|
const id = `doc_${label}` as DocumentId;
|
|
const repId = `rep_${label}` as RepresentationId;
|
|
const document: Document = {
|
|
id,
|
|
mediaType: "application/pdf",
|
|
title: `Doc ${label}`,
|
|
createdAt: "2026-05-25T00:00:00.000Z",
|
|
updatedAt: "2026-05-25T00:00:00.000Z",
|
|
};
|
|
const representation: DocumentRepresentation = {
|
|
id: repId,
|
|
documentId: id,
|
|
representationType: "pdf-text",
|
|
contentHash: `hash-${label}`,
|
|
canonicalText: `text for ${label}`,
|
|
pageMap: [{ page: 1, width: 100, height: 100 }],
|
|
offsetMap: [{ page: 1, globalStart: 0, globalEnd: 12, pageLength: 12 }],
|
|
generatedAt: "2026-05-25T00:00:00.000Z",
|
|
};
|
|
engine.documents.register({ document, representation });
|
|
return { id };
|
|
}
|
|
|
|
describe("per-session engine snapshot round-trip", () => {
|
|
it("keeps two sessions' snapshots isolated under per-session storage keys", () => {
|
|
const storage = memoryStorage();
|
|
const sessA = "sess_aaa" as SessionId;
|
|
const sessB = "sess_bbb" as SessionId;
|
|
|
|
// Author session A.
|
|
const engineA = createEngine();
|
|
const offA = attachPersister(engineA, { key: engineSnapshotKey(sessA), storage });
|
|
const a1 = seedDoc(engineA, "a1");
|
|
const a2 = seedDoc(engineA, "a2");
|
|
offA();
|
|
|
|
// Author session B with completely different documents.
|
|
const engineB = createEngine();
|
|
const offB = attachPersister(engineB, { key: engineSnapshotKey(sessB), storage });
|
|
const b1 = seedDoc(engineB, "b1");
|
|
offB();
|
|
|
|
// Restore each into its own fresh engine; assert isolation.
|
|
const restoredA = createEngine();
|
|
const resA = restoreFromStorage(restoredA, { key: engineSnapshotKey(sessA), storage });
|
|
expect(resA.restored).toBe(true);
|
|
const aIds = restoredA.documents.list().map((d) => d.id).sort();
|
|
expect(aIds).toEqual([a1.id, a2.id].sort());
|
|
|
|
const restoredB = createEngine();
|
|
const resB = restoreFromStorage(restoredB, { key: engineSnapshotKey(sessB), storage });
|
|
expect(resB.restored).toBe(true);
|
|
const bIds = restoredB.documents.list().map((d) => d.id);
|
|
expect(bIds).toEqual([b1.id]);
|
|
|
|
// Sanity: each snapshot key really does hold a distinct snapshot.
|
|
const rawA = storage.getItem(engineSnapshotKey(sessA));
|
|
const rawB = storage.getItem(engineSnapshotKey(sessB));
|
|
expect(rawA).not.toBeNull();
|
|
expect(rawB).not.toBeNull();
|
|
const snapA = JSON.parse(rawA!) as EngineSnapshot;
|
|
const snapB = JSON.parse(rawB!) as EngineSnapshot;
|
|
expect(snapA.documents).toHaveLength(2);
|
|
expect(snapB.documents).toHaveLength(1);
|
|
});
|
|
});
|