63 lines
3.6 KiB
JavaScript
63 lines
3.6 KiB
JavaScript
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/editing-domain-recovery.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 recovery = await import(moduleUrl);
|
|
|
|
const hash = "a".repeat(64);
|
|
const base = (domain, dataId) => ({
|
|
schemaVersion: 1,
|
|
domain,
|
|
baseline: { objectIds: [`object:${domain}`], dataIds: [dataId], objectCount: 1, revision: 4, identityHash: hash },
|
|
workerRestart: { status: "RECOVERED", workerGeneration: 2, revisionBefore: 4, revisionAfter: 4, hashBefore: hash, hashAfter: hash, liveHandles: 1, temporaryResourcesAfter: 0 },
|
|
oom: { status: "RECOVERED", faultPoint: "GPU_GEOMETRY_UPLOAD", code: "GPU_GEOMETRY_BUDGET_EXCEEDED", revisionBefore: 4, revisionAfter: 4, hashBefore: hash, hashAfter: hash, releasedBytes: 4096, temporaryResourcesAfter: 0 },
|
|
gpuRelease: { status: "RECOVERED", backend: "WEBGL2", releaseCount: 1, reinitCount: 1, disposedResources: 6, visiblePixels: 12, pixelHashBefore: hash, pixelHashAfter: hash },
|
|
smallScene: { status: "RECOVERED", revision: 4, identityHash: hash, objectCount: 1, dataIds: [dataId], visiblePixels: 12 },
|
|
});
|
|
|
|
test("M9-14 parses all three editing domains and preserves recovery invariants", () => {
|
|
const reports = recovery.parseEditingDomainRecoverySuite([
|
|
base("CURVE", "curve:Recovery"),
|
|
base("GREASE_PENCIL", "grease-pencil:Recovery"),
|
|
base("PAINT", "mesh:Recovery"),
|
|
]);
|
|
assert.deepEqual(reports.map((report) => report.domain), ["CURVE", "GREASE_PENCIL", "PAINT"]);
|
|
assert.equal(reports.every((report) => report.workerRestart.hashAfter === report.baseline.identityHash), true);
|
|
assert.equal(reports.every((report) => report.oom.releasedBytes > 0 && report.gpuRelease.visiblePixels > 0), true);
|
|
});
|
|
|
|
test("M9-14 rejects stale identity, duplicate domains and GPU release drift", () => {
|
|
const curve = base("CURVE", "curve:Recovery");
|
|
const stale = structuredClone(curve);
|
|
stale.smallScene.identityHash = "b".repeat(64);
|
|
assert.throws(() => recovery.parseEditingDomainRecoveryEvidence(stale), /smallScene identity/);
|
|
assert.throws(() => recovery.parseEditingDomainRecoverySuite([curve, curve, base("PAINT", "mesh:Recovery")]), /duplicate editing domain/);
|
|
const releaseDrift = structuredClone(curve);
|
|
releaseDrift.gpuRelease.releaseCount = 2;
|
|
assert.throws(() => recovery.parseEditingDomainRecoveryEvidence(releaseDrift), /release and reinitialize exactly once/);
|
|
});
|
|
|
|
test("M9-14 summarizes only visible objects in the requested editing domain", () => {
|
|
const snapshot = {
|
|
nodes: [
|
|
{ id: "object:curve", type: "CURVE", visible: true, dataId: "curve:one" },
|
|
{ id: "object:hidden", type: "CURVE", visible: false, dataId: "curve:two" },
|
|
{ id: "object:mesh", type: "MESH", visible: true, dataId: "mesh:one" },
|
|
],
|
|
};
|
|
assert.deepEqual(recovery.summarizeEditingDomain(snapshot, "CURVE"), { objectIds: ["object:curve"], dataIds: ["curve:one"], objectCount: 1 });
|
|
assert.deepEqual(recovery.summarizeEditingDomain(snapshot, "PAINT"), { objectIds: ["object:mesh"], dataIds: ["mesh:one"], objectCount: 1 });
|
|
assert.throws(() => recovery.summarizeEditingDomain({ nodes: [] }, "GREASE_PENCIL"), /DOMAIN_MISSING/);
|
|
});
|