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, "../../.."); function transpileDataUrl(filePath, replacements = new Map()) { const result = ts.transpileModule(fs.readFileSync(filePath, "utf8"), { compilerOptions: { module: ts.ModuleKind.ES2022, target: ts.ScriptTarget.ES2022 }, fileName: filePath, reportDiagnostics: true, }); assert.deepEqual(result.diagnostics, []); let source = result.outputText; for (const [specifier, replacement] of replacements) { source = source.replaceAll(`"${specifier}"`, JSON.stringify(replacement)); } return `data:text/javascript;base64,${Buffer.from(source).toString("base64")}`; } const gatesUrl = transpileDataUrl(path.join(repoRoot, "web/protocol/capability-gates.ts")); const selectorUrl = transpileDataUrl( path.join(repoRoot, "web/protocol/engine-variant.ts"), new Map([["./capability-gates", gatesUrl]]), ); const bootstrapUrl = transpileDataUrl( path.join(repoRoot, "web/app/src/engine-client/engine-variant-bootstrap.ts"), new Map([["../../../protocol/engine-variant", selectorUrl]]), ); const { bootstrapWebEngineRelease, engineVariantStatusLabel, EngineVariantLoadError, } = await import(bootstrapUrl); const manifest = JSON.parse(fs.readFileSync( path.join(repoRoot, "tests/golden/M6-04A/engine-manifest-v2.json"), "utf8", )); const ready = { crossOriginIsolated: true, sharedArrayBuffer: true, worker: true }; class FakeSession { constructor(variant) { this.variant = variant; this.opened = []; this.disposals = 0; this.state = { handles: 1, workers: variant.id === "pthread" ? 1 : 0, timers: 0, pendingRequests: 0, }; } async openProject(project) { this.opened.push(project); } resourceState() { return { ...this.state }; } testOnlySeedTrackedResources() { this.state.timers = 1; this.state.pendingRequests = 1; } async dispose() { this.disposals += 1; this.state = { handles: 0, workers: 0, timers: 0, pendingRequests: 0 }; return this.resourceState(); } } function dependencies() { const sessions = []; return { sessions, initialize: async (variant) => { const session = new FakeSession(variant); sessions.push(session); return session; }, }; } test("M6-05A/B/C injects one pthread failure, cleans it and falls back once", async () => { const deps = dependencies(); const outcome = await bootstrapWebEngineRelease( manifest.releaseId, manifest, "AUTO", ready, deps, undefined, { failPthreadAfterInitialize: true, seedTrackedPthreadResources: true }, ); assert.deepEqual(outcome.result.attempted, ["pthread", "single"]); assert.equal(deps.sessions.length, 2); assert.equal(deps.sessions[0].disposals, 1); assert.deepEqual(outcome.result.failedAttemptCleanup, { handles: 0, workers: 0, timers: 0, pendingRequests: 0, }); assert.equal(outcome.result.selected, "single"); assert.deepEqual(outcome.result.fallbackReason, { code: "PTHREAD_INITIALIZATION_FAILED", message: "TEST_PTHREAD_INITIALIZATION_FAILURE", }); assert.equal(outcome.result.openCount, 0); assert.equal(deps.sessions[0].opened.length, 0); assert.equal(deps.sessions[1].opened.length, 0); }); test("M6-05D opens a pending project once only after the fallback settles", async () => { const deps = dependencies(); const project = { id: "pending-project" }; const outcome = await bootstrapWebEngineRelease( manifest.releaseId, manifest, "AUTO", ready, deps, project, { failPthreadAfterInitialize: true }, ); assert.equal(outcome.result.openCount, 1); assert.deepEqual(deps.sessions[0].opened, []); assert.deepEqual(deps.sessions[1].opened, [project]); }); test("M6-05E exposes selected, attempted and fallbackReason without pthread READY UI", async () => { const deps = dependencies(); const outcome = await bootstrapWebEngineRelease( manifest.releaseId, manifest, "AUTO", ready, deps, undefined, { failPthreadAfterInitialize: true }, ); const label = engineVariantStatusLabel(outcome.result); assert.deepEqual( { selected: outcome.result.selected, attempted: outcome.result.attempted, fallbackReason: outcome.result.fallbackReason?.code, }, { selected: "single", attempted: ["pthread", "single"], fallbackReason: "PTHREAD_INITIALIZATION_FAILED", }, ); assert.equal(label, "Runtime: single (pthread fallback: PTHREAD_INITIALIZATION_FAILED)"); assert.doesNotMatch(label, /pthread\s+ready/i); }); test("M6-08E treats a manifest hash mismatch as fatal without fallback or project open", async () => { const initialized = []; const project = { id: "must-stay-unopened" }; await assert.rejects( bootstrapWebEngineRelease( manifest.releaseId, manifest, "AUTO", ready, { initialize: async (variant) => { initialized.push(variant.id); if (variant.id === "pthread") { throw new EngineVariantLoadError("ENGINE_VARIANT_RESOURCE_HASH_MISMATCH", variant.resources.wasm.url); } return new FakeSession(variant); }, }, project, ), (error) => error?.code === "ENGINE_VARIANT_INTEGRITY_FAILED" && error.cause?.code === "ENGINE_VARIANT_RESOURCE_HASH_MISMATCH" && JSON.stringify(error.attempted) === JSON.stringify(["pthread"]), ); assert.deepEqual(initialized, ["pthread"]); }); test("M6-08D returns refresh-required before initialization or project open", async () => { let initializationCount = 0; const outcome = await bootstrapWebEngineRelease( "blender-wasm-previous", manifest, "AUTO", ready, { initialize: async (variant) => { initializationCount += 1; return new FakeSession(variant); }, }, { id: "must-stay-unopened" }, ); assert.deepEqual(outcome, { result: { status: "REFRESH_REQUIRED", expectedReleaseId: "blender-wasm-previous", actualReleaseId: manifest.releaseId, manifest: null, }, session: null, }); assert.equal(initializationCount, 0); }); test("M6-08E normalizes a fallback variant hash mismatch and never opens the project", async () => { const initialized = []; const pthreadSession = new FakeSession(manifest.variants[1]); await assert.rejects( bootstrapWebEngineRelease( manifest.releaseId, manifest, "AUTO", ready, { initialize: async (variant) => { initialized.push(variant.id); if (variant.id === "single") { throw new EngineVariantLoadError("ENGINE_VARIANT_RESOURCE_HASH_MISMATCH", variant.resources.wasm.url); } return pthreadSession; }, }, { id: "must-stay-unopened" }, { failPthreadAfterInitialize: true }, ), (error) => error?.code === "ENGINE_VARIANT_INTEGRITY_FAILED" && error.cause?.code === "ENGINE_VARIANT_RESOURCE_HASH_MISMATCH" && JSON.stringify(error.attempted) === JSON.stringify(["pthread", "single"]), ); assert.deepEqual(initialized, ["pthread", "single"]); assert.equal(pthreadSession.disposals, 1); assert.deepEqual(pthreadSession.opened, []); });