Files
KDL_WORK/kdl-wasm/web/tests/workspace/storage.test.ts
2026-06-28 08:20:33 +08:00

141 lines
4.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { sampleOlpProject } from "../../src/olp/index.js";
import {
MemoryWorkspaceStorage,
detectWorkspaceDamage,
exportWorkspaceBundle,
importWorkspaceBundle,
initializeWorkspace,
migrateManifest,
readJson,
readManifest,
readProjectModel,
restoreWorkspace,
snapshotWorkspace,
writeJson,
writeProjectModel
} from "../../src/workspace/index.js";
const NOW = "2026-06-27T00:00:00.000Z";
describe("Workspace storage", () => {
it("creates project manifest and conventional project layout", async () => {
const storage = new MemoryWorkspaceStorage();
const manifest = await initializeWorkspace(storage, {
projectId: "demo_cell",
name: "Demo Cell",
model: sampleOlpProject(),
now: NOW
});
expect(manifest.entrypoints.model).toBe("model/olp-project.json");
expect(await storage.list()).toEqual(["model/olp-project.json", "project.json"]);
expect(await readManifest(storage)).toEqual(manifest);
expect((await readProjectModel(storage)).project.id).toBe("demo_cell");
});
it("supports text/json read-write-delete with OPFS-like async storage", async () => {
const storage = new MemoryWorkspaceStorage();
await storage.writeText("reports/readme.txt", "hello");
await writeJson(storage, "imports/report.json", { ok: true, count: 2 });
expect(await storage.readText("reports/readme.txt")).toBe("hello");
expect(await readJson(storage, "imports/report.json")).toEqual({ ok: true, count: 2 });
await storage.delete("reports/readme.txt");
expect(await storage.exists("reports/readme.txt")).toBe(false);
});
it("snapshots and restores an equivalent workspace", async () => {
const source = new MemoryWorkspaceStorage();
await initializeWorkspace(source, {
projectId: "demo_cell",
name: "Demo Cell",
model: sampleOlpProject(),
now: NOW
});
await source.writeText("programs/main.grl", "language grl 0.1\n");
const snapshot = await snapshotWorkspace(source);
const target = new MemoryWorkspaceStorage({ "old.txt": "delete me" });
await restoreWorkspace(target, snapshot);
expect(await target.list()).toEqual(await source.list());
expect(await snapshotWorkspace(target)).toEqual(snapshot);
});
it("exports and imports a stable JSON bundle with checksum", async () => {
const source = new MemoryWorkspaceStorage();
await initializeWorkspace(source, {
projectId: "demo_cell",
name: "Demo Cell",
model: sampleOlpProject(),
now: NOW
});
await source.writeText("programs/main.grl", "language grl 0.1\n");
const bundle = await exportWorkspaceBundle(source);
const target = new MemoryWorkspaceStorage();
const report = await importWorkspaceBundle(target, bundle);
expect(report).toEqual({ ok: true, damages: [] });
expect(await snapshotWorkspace(target)).toEqual(await snapshotWorkspace(source));
});
it("migrates legacy manifests and detects damaged workspaces", async () => {
expect(migrateManifest({
schemaVersion: 0,
projectId: "legacy",
name: "Legacy",
createdAt: NOW,
updatedAt: NOW,
modelPath: "legacy/model.json"
})).toMatchObject({
schemaVersion: 1,
projectId: "legacy",
entrypoints: { model: "legacy/model.json" }
});
const storage = new MemoryWorkspaceStorage();
await initializeWorkspace(storage, {
projectId: "demo_cell",
name: "Demo Cell",
model: sampleOlpProject(),
now: NOW
});
const checksum = (await snapshotWorkspace(storage)).checksum;
const model = await readProjectModel(storage);
model.operations[0]!.pathId = "missing_path";
await writeProjectModel(storage, model, NOW);
expect(await detectWorkspaceDamage(storage, checksum)).toMatchObject({
ok: false,
damages: [
expect.objectContaining({ code: "WORKSPACE_MODEL_INVALID" }),
expect.objectContaining({ code: "WORKSPACE_CHECKSUM_MISMATCH" })
]
});
});
it("rejects tampered bundles before import", async () => {
const source = new MemoryWorkspaceStorage();
await initializeWorkspace(source, {
projectId: "demo_cell",
name: "Demo Cell",
model: sampleOlpProject(),
now: NOW
});
const tampered = JSON.parse(await exportWorkspaceBundle(source)) as {
files: Record<string, string>;
};
tampered.files["project.json"] = "{}";
const target = new MemoryWorkspaceStorage();
expect(await importWorkspaceBundle(target, JSON.stringify(tampered))).toMatchObject({
ok: false,
damages: [expect.objectContaining({ code: "WORKSPACE_CHECKSUM_MISMATCH" })]
});
expect(await target.list()).toEqual([]);
});
});