import assert from "node:assert/strict"; import fs from "node:fs"; import path from "node:path"; import test from "node:test"; import ts from "typescript"; const repoRoot = path.resolve(import.meta.dirname, "../../.."); const sourcePath = path.join(repoRoot, "web/protocol/recent-projects.ts"); const transpiled = ts.transpileModule(fs.readFileSync(sourcePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: sourcePath, reportDiagnostics: true, }); assert.deepEqual(transpiled.diagnostics, []); const moduleUrl = "data:text/javascript;base64," + Buffer.from(transpiled.outputText).toString("base64"); const { normalizeRecentProjects, parseRecentProjectIndex, removeRecentProject, upsertRecentProject, classifyRecentProjectIdentity, } = await import(moduleUrl); function project(projectId, overrides = {}) { return { schemaVersion: 1, projectId, displayName: `${projectId}.blend`, revision: 7, bytes: 1024, sha256: (projectId.charCodeAt(0) % 16).toString(16).repeat(64), updatedAt: "2026-08-15T12:00:00.000Z", lastOpenedAt: "2026-08-15T12:00:00.000Z", backend: "opfs", ...overrides, }; } test("M7-09 recent projects sort and deduplicate deterministically", () => { const older = project("alpha", { revision: 2, updatedAt: "2026-08-15T08:00:00-04:00", lastOpenedAt: "2026-08-15T08:00:00-04:00" }); const newer = project("alpha", { revision: 3, updatedAt: "2026-08-15T12:01:00.000Z", lastOpenedAt: "2026-08-15T12:01:00.000Z" }); const second = project("beta", { lastOpenedAt: "2026-08-15T11:59:00.000Z" }); const forward = normalizeRecentProjects([older, second, newer]); const reverse = normalizeRecentProjects([newer, second, older]); assert.deepEqual(forward, reverse); assert.deepEqual(forward.index.projects.map(({ projectId, revision }) => [projectId, revision]), [["alpha", 3], ["beta", 7]]); assert.equal(forward.index.projects[0].updatedAt, "2026-08-15T12:01:00.000Z"); }); test("M7-09 malformed records are quarantined without hiding valid projects", () => { const parsed = parseRecentProjectIndex({ schemaVersion: 1, projects: [project("valid"), { ...project("bad"), sha256: "not-a-digest" }, { ...project("blank"), displayName: " " }], }); assert.equal(parsed.quarantined, 2); assert.deepEqual(parsed.index.projects.map((item) => item.projectId), ["valid"]); assert.deepEqual(parseRecentProjectIndex({ schemaVersion: 99, projects: [] }), { index: { schemaVersion: 1, projects: [] }, quarantined: 1, }); }); test("M7-09 upsert and removal keep a bounded stable index", () => { let index = { schemaVersion: 1, projects: [] }; index = upsertRecentProject(index, project("alpha")); index = upsertRecentProject(index, project("alpha", { revision: 9, lastOpenedAt: "2026-08-15T12:02:00Z" })); index = upsertRecentProject(index, project("beta")); assert.deepEqual(index.projects.map(({ projectId, revision }) => [projectId, revision]), [["alpha", 9], ["beta", 7]]); assert.deepEqual(removeRecentProject(index, "alpha").projects.map((item) => item.projectId), ["beta"]); assert.equal(normalizeRecentProjects(index.projects, 0).index.projects.length, 0); }); test("M7-10 recent project integrity classifies missing and mismatched content", () => { const expected = { revision: 7, bytes: 1024, sha256: "a".repeat(64) }; assert.equal(classifyRecentProjectIdentity(expected, undefined), "MISSING"); assert.equal(classifyRecentProjectIdentity(expected, { ...expected, sha256: "b".repeat(64) }), "HASH_MISMATCH"); assert.equal(classifyRecentProjectIdentity(expected, { ...expected, revision: 8 }), "METADATA_MISMATCH"); assert.equal(classifyRecentProjectIdentity(expected, expected), undefined); });