141 lines
4.7 KiB
TypeScript
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([]);
|
|
});
|
|
});
|