import fs from "node:fs"; import path from "node:path"; import { fileURLToPath, pathToFileURL } from "node:url"; const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../.."); const planPath = path.join(root, "tests/golden/WBV2/internal-submodule-smoke-plan.json"); const reportPath = path.join(root, "tests/golden/WBV2/internal-submodule-smoke-report.json"); const plan = JSON.parse(fs.readFileSync(planPath, "utf8")); const execute = process.argv.includes("--execute"); const resume = process.argv.includes("--resume"); const interruptionIndex = process.argv.indexOf("--record-interruption"); const interruptedSubmodule = interruptionIndex >= 0 ? process.argv[interruptionIndex + 1] : null; if (plan.operation !== "ONE_PASS_INTERNAL_SUBMODULE_SMOKE" || plan.policy.oneSmokePerSubmodule !== true || plan.policy.retryFailedSubmodule !== false) throw new Error("invalid internal submodule smoke policy"); if (new Set(plan.cases.map((item) => item.submoduleId)).size !== plan.cases.length || new Set(plan.cases.map((item) => item.smokeId)).size !== plan.cases.length) throw new Error("duplicate internal submodule smoke identity"); if (!plan.cases.every((item) => item.runOnce === true && item.maxAttempts === 1)) throw new Error("internal submodule smoke is not once-only"); if (interruptedSubmodule) { if (fs.existsSync(reportPath)) throw new Error("cannot replace an existing internal submodule smoke report"); const interrupted = plan.cases.find((item) => item.submoduleId === interruptedSubmodule); if (!interrupted) throw new Error(`unknown interrupted submodule ${interruptedSubmodule}`); const cases = plan.cases.map((item) => ({ ordinal: item.ordinal, smokeId: item.smokeId, submoduleId: item.submoduleId, mode: item.mode, attempts: item.submoduleId === interruptedSubmodule ? 1 : 0, status: item.submoduleId === interruptedSubmodule ? "FAIL" : "PENDING", ...(item.submoduleId === interruptedSubmodule ? { evidence: { route: item.mode, recognized: false, outcome: "PROCESS_ABORT", error: "C++ exception catching was disabled in the WASM link" } } : {}), })); fs.writeFileSync(reportPath, `${JSON.stringify({ schemaVersion: 1, planId: plan.planId, operation: "ONE_PASS_INTERNAL_SUBMODULE_SMOKE_EXECUTION", status: "INTERRUPTED", startedAt: new Date().toISOString(), policy: plan.policy, summary: { total: cases.length, attempts: 1, passed: 0, failed: 1, pending: cases.length - 1, retries: 0 }, cases }, null, 2)}\n`); process.stdout.write(`wbv2-submodule-smoke-interruption-recorded submodule=${interruptedSubmodule} attempts=1 pending=${cases.length - 1}\n`); process.exit(0); } if (!execute && !resume) { process.stdout.write(`wbv2-submodule-smoke-plan-ok submodules=${plan.cases.length} once=true\n`); process.exit(0); } if (fs.existsSync(reportPath) && !resume) { const prior = JSON.parse(fs.readFileSync(reportPath, "utf8")); if (prior.planId === plan.planId && prior.cases?.some((item) => item.attempts > 0)) { process.stderr.write(`wbv2-submodule-smoke-once-only: ${plan.planId} already executed\n`); process.exit(2); } } let checkpoint = fs.existsSync(reportPath) ? JSON.parse(fs.readFileSync(reportPath, "utf8")) : null; if (resume && (!checkpoint || checkpoint.planId !== plan.planId || !["INTERRUPTED", "RUNNING"].includes(checkpoint.status))) { throw new Error("resume requires the matching INTERRUPTED checkpoint"); } const runtimeChecks = { listFeatureRoutes: ["web/app/src/engine-client/WebEngineClient.ts", "async listFeatureRoutes("], invokeFeatureRoute: ["web/app/src/engine-client/WebEngineClient.ts", "async invokeFeatureRoute("], featureRouteCoverage: ["web/app/src/app/App.tsx", "featureRouteCoverage"], featureRouteGaps: ["web/app/src/app/App.tsx", "featureRouteGaps"], describeServerOperator: ["web/app/src/engine-client/WebEngineClient.ts", "async describeServerOperator("], invokeBrowserEquivalent: ["web/protocol/blender-operator.ts", "invokeBrowserEquivalent"], featureRouteRuntimeAudit: ["tools/web/check-web-engine-command-registry.mjs", "featureRouteRuntimeAudit"], }; const startedAt = checkpoint?.startedAt ?? new Date().toISOString(); const results = checkpoint?.cases ?? []; const flush = (status = "RUNNING") => { const attempted = results.filter((item) => item.attempts === 1); const failed = attempted.filter((item) => item.status !== "PASS"); const pending = results.filter((item) => item.attempts === 0); fs.writeFileSync(reportPath, `${JSON.stringify({ schemaVersion: 1, planId: plan.planId, operation: "ONE_PASS_INTERNAL_SUBMODULE_SMOKE_EXECUTION", status, startedAt, policy: plan.policy, summary: { total: plan.cases.length, attempts: attempted.length, passed: attempted.length - failed.length, failed: failed.length, pending: pending.length, retries: 0 }, cases: results }, null, 2)}\n`); }; let engine; let handle = 0; const rawCall = (cmd, params) => { const request = new TextEncoder().encode(JSON.stringify({ cmd, params })); const input = engine._malloc(request.byteLength); const dataOut = engine._malloc(4); const lengthOut = engine._malloc(4); engine.HEAPU8.set(request, input); let resultPointer = 0; try { const exitCode = engine._web_engine_call_json(handle, input, request.byteLength, dataOut, lengthOut); resultPointer = engine.HEAPU32[dataOut >>> 2]; const length = engine.HEAPU32[lengthOut >>> 2]; const payload = length ? JSON.parse(new TextDecoder().decode(engine.HEAPU8.slice(resultPointer, resultPointer + length))) : null; const error = payload?.error ?? (engine._web_engine_last_error_message() ? engine.UTF8ToString(engine._web_engine_last_error_message()) : ""); return { exitCode, payload, error }; } finally { if (resultPointer) engine._web_engine_free_buffer(resultPointer); engine._free(input); engine._free(dataOut); engine._free(lengthOut); } }; try { const factory = (await import(pathToFileURL(path.join(root, "web/app/src/vendor/blender/web_engine.js")))).default; engine = await factory({ wasmBinary: fs.readFileSync(path.join(root, "web/app/src/vendor/blender/web_engine.wasm")) }); handle = engine._web_engine_create(); const blend = fs.readFileSync(path.join(root, "tests/files/web/basic_scene.blend")); const pointer = engine._malloc(blend.byteLength); engine.HEAPU8.set(blend, pointer); const openCode = engine._web_engine_open_blend(handle, pointer, blend.byteLength); engine._free(pointer); if (openCode !== 0) throw new Error("internal submodule smoke could not open basic_scene.blend"); for (const item of plan.cases) { const prior = results.find((candidate) => candidate.submoduleId === item.submoduleId); if (prior?.attempts === 1) continue; const attempted = { ordinal: item.ordinal, smokeId: item.smokeId, submoduleId: item.submoduleId, mode: item.mode, attempts: 1, status: "FAIL", evidence: { route: item.mode, recognized: false, outcome: "PROCESS_ABORT", error: "process ended before the submodule returned" } }; if (prior) Object.assign(prior, attempted); else results.push(attempted); flush("INTERRUPTED"); let status = "PASS"; let evidence; if (item.mode === "RUNTIME_ROUTE") { const [file, marker] = runtimeChecks[item.submoduleId] ?? []; const found = Boolean(file && marker && fs.readFileSync(path.join(root, file), "utf8").includes(marker)); status = found ? "PASS" : "FAIL"; evidence = { file, marker, found }; } else { const call = item.mode === "WASM_EDIT_ROUTE" ? rawCall("applyCommand", { command: { type: item.submoduleId } }) : rawCall(item.submoduleId, {}); const generic = /(?:command|cmd).*(?:not implemented|unsupported JSON command)|unified JSON cmd is not implemented/iu.test(call.error ?? ""); status = generic ? "FAIL" : "PASS"; evidence = { route: item.mode, exitCode: call.exitCode, recognized: !generic, outcome: call.payload?.ok === true ? "SUCCEEDED" : "FAILED_CLOSED", error: call.error || null }; } const result = { ...attempted, status, evidence }; Object.assign(prior ?? results.find((candidate) => candidate.submoduleId === item.submoduleId), result); flush(); } } finally { if (engine && handle) engine._web_engine_destroy(handle); } const failed = results.filter((item) => item.status !== "PASS"); const report = { schemaVersion: 1, planId: plan.planId, operation: "ONE_PASS_INTERNAL_SUBMODULE_SMOKE_EXECUTION", status: failed.length ? "FAILED_FOCUSED_FIX_REQUIRED" : "PASS", startedAt, finishedAt: new Date().toISOString(), policy: plan.policy, summary: { total: results.length, attempts: results.length, passed: results.length - failed.length, failed: failed.length, pending: 0, retries: 0 }, cases: results, }; fs.writeFileSync(reportPath, `${JSON.stringify(report, null, 2)}\n`); process.stdout.write(`wbv2-submodule-smoke-${report.status.toLowerCase()} total=${results.length} passed=${report.summary.passed} failed=${failed.length} attempts=${results.length} retries=0\n`); process.exit(failed.length ? 1 : 0);