import assert from "node:assert/strict"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { pathToFileURL } from "node:url"; import test from "node:test"; import ts from "typescript"; const root = path.resolve(import.meta.dirname, "../../.."); const temporary = fs.mkdtempSync(path.join(os.tmpdir(), "io-format-ui-gate-unit-")); const sourcePath = path.join(root, "web/protocol/io-format-ui-gate.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, []); fs.writeFileSync(path.join(temporary, "io-format-ui-gate.mjs"), transpiled.outputText); const gate = await import(pathToFileURL(path.join(temporary, "io-format-ui-gate.mjs"))); const registry = JSON.parse(fs.readFileSync(path.join(root, "web/app/src/capabilities/io-format-ui-registry.json"), "utf8")); test("M12-05C keeps only matrix-declared executable routes in UI", () => { const parsed = gate.parseIOFormatUIRegistry(registry); assert.deepEqual(parsed.importRoutes, []); assert.deepEqual(parsed.exportRoutes.map((route) => route.format), ["GLB"]); const commands = [ { id: "file.export-glb", ioFormat: { format: "GLB", operation: "EXPORT", execution: "LOCAL" } }, { id: "file.import-obj", ioFormat: { format: "OBJ", operation: "IMPORT", execution: "LOCAL" } }, { id: "file.export-usd", ioFormat: { format: "USD", operation: "EXPORT", execution: "SERVER" } }, { id: "edit.undo" }, ]; assert.deepEqual(gate.filterIOFormatOperatorCommands(commands, parsed).map((command) => command.id), ["file.export-glb", "edit.undo"]); }); test("M12-05C file selection is project-only while import routes are blocked", () => { const parsed = gate.parseIOFormatUIRegistry(registry); assert.equal(gate.ioFormatUIAccept(parsed), ".blend,application/octet-stream"); assert.deepEqual(gate.gateIOFormatFileSelection("scene.blend", parsed), { status: "READY", kind: "BLEND" }); assert.deepEqual(gate.gateIOFormatFileSelection("mesh.obj", parsed), { status: "BLOCKED", code: "IO_FORMAT_UNSUPPORTED", extension: ".obj" }); assert.deepEqual(gate.gateIOFormatFileSelection("scene.glb", parsed), { status: "BLOCKED", code: "IO_FORMAT_UNSUPPORTED", extension: ".glb" }); }); test("M12-05C rejects malformed route metadata", () => { const bad = structuredClone(registry); bad.importRoutes = [{ format: "OBJ", operation: "IMPORT", execution: "LOCAL", extensions: [".obj"] }]; assert.doesNotThrow(() => gate.parseIOFormatUIRegistry(bad)); const parsed = gate.parseIOFormatUIRegistry(bad); assert.equal(gate.filterIOFormatOperatorCommands([{ id: "import-obj", ioFormat: { format: "OBJ", operation: "IMPORT", execution: "LOCAL" } }], parsed).length, 1); bad.importRoutes[0].extensions = [".not-obj"]; assert.throws(() => gate.parseIOFormatUIRegistry(bad), { code: "IO_FORMAT_UNSUPPORTED" }); }); test.after(() => fs.rmSync(temporary, { recursive: true, force: true }));