import assert from "node:assert/strict"; import { test } from "node:test"; import { createHash } from "node:crypto"; import { mkdtemp, mkdir, writeFile, rm } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import { compileCapabilityResourceSource, compileCapabilitySource, formatQx, parseQx, walkSyntax, } from "../src/capability-language/index.js"; import { compileExecutionPlan, checkExecutionWorld, componentArtifactDigest, componentSharedModules, expectedArtifactReceipt, checkExecutionInstallation, clientContractCatalog, type ComponentArtifact, type PackageArtifacts, } from "../src/execution-world/index.js"; import { validateWorkspaceRevision } from "../src/capability-model/index.js"; const source = { repository: "https://example.test/components.git", commit: "a".repeat(40) }; const contract = (name: string) => { const result = compileCapabilityResourceSource( `interface ${name} id "interface:${name}" revision "interface:${name}@1" { }`, { source }, ); assert.ok(result.ok && result.resource.kind === "interface", JSON.stringify(result.diagnostics)); return result.resource.revision; }; const data = contract("Data"), other = contract("Other"); const interfaces = new Map([ ["Data", data], ["Other", other], ]); const componentSource = (body = 'component Card id "component:card" subject Data runtime react;') => `import interface Data; package UI id "package:ui" revision "package:ui@1" { ${body} }`; const compilePackage = (body?: string) => compileCapabilityResourceSource(componentSource(body), { source, environment: { interfaces, interfaceClosure: [...interfaces.values()] }, }); const workspace = (presentation = 'presentation Card id "presentation:card" for Data using UI.Card;') => { const pkg = compilePackage(); assert.ok(pkg.ok && pkg.resource.kind === "package", JSON.stringify(pkg.diagnostics)); return compileCapabilitySource( `workspace Example id "workspace:example" revision "workspace:example@1" commit "${source.commit}" { import interface Data; import interface Other; import package UI; atom One id "atom:one"; atom Two id "atom:two"; conform One as Data id "conformance:one:data" { } conform Two as Data id "conformance:two:data" { } ${presentation} }`, "workspace.qx", { interfaces, interfaceClosure: [...interfaces.values()], packages: new Map([["UI", pkg.resource.revision]]), }, ); }; test("interface-only component declarations serve multiple conforming atoms without capability exports", () => { const result = workspace(); assert.ok(result.ok, JSON.stringify(result.diagnostics)); const plan = compileExecutionPlan(result.workspace); assert.equal(plan.definition.packages[0]!.exports.length, 0); assert.deepEqual(plan.definition.packages[0]!.components, [ { id: "component:card", displayName: "Card", subject: "interface:Data@1", runtime: "react", }, ]); assert.equal(plan.definition.presentations?.[0]?.componentId, "component:card"); assert.equal(plan.definition.workspace.conformances.length, 2); assert.equal(plan.definition.operations.length, 0); }); test("component declarations survive formatting and syntax inspection", () => { const text = formatQx(componentSource()); const parsed = parseQx(text); assert.deepEqual(parsed.diagnostics, []); assert.ok([...walkSyntax(parsed.root)].some((node) => node.kind === "componentDecl")); assert.equal(formatQx(text), text); }); test("component and presentation contracts reject wrong subjects, duplicates and unavailable runtimes", () => { for (const [input, message] of [ ['component Card id "component:card" subject Missing runtime react;', /Missing/], ['component Card id "component:card" subject Data runtime arduino;', /Unsupported component runtime/], [ 'component Card id "component:card" subject Data runtime react; component Other id "component:card" subject Data runtime react;', /Duplicate component/, ], [ 'component Card id "component:card" subject Data runtime react; component Card id "component:other" subject Data runtime react;', /Duplicate component/, ], ] as const) { const result = compilePackage(input); assert.equal(result.ok, false); assert.match(JSON.stringify(result.diagnostics), message); } for (const [input, message] of [ ['presentation Card id "presentation:card" for Other using UI.Card;', /Component requires/], ['presentation Card id "presentation:card" for Data using UI.Missing;', /Unknown component/], [ 'presentation Card id "presentation:card" for Data using UI.Card; presentation Duplicate id "presentation:card" for Data using UI.Card;', /Duplicate presentation/, ], ] as const) { const result = workspace(input); assert.equal(result.ok, false); assert.match(JSON.stringify(result.diagnostics), message); } }); test("model validation rechecks presentation metadata instead of trusting the source parser", () => { const result = workspace(); assert.ok(result.ok, JSON.stringify(result.diagnostics)); result.workspace.presentations![0]!.componentId = "missing"; assert.ok(validateWorkspaceRevision(result.workspace).some((issue) => issue.code === "invalid-presentation")); assert.throws(() => compileExecutionPlan(result.workspace), /no installed package component/); }); test("checked component-only artifacts install without a service and reject missing or substituted entries", async () => { const result = workspace(); assert.ok(result.ok, JSON.stringify(result.diagnostics)); const plan = compileExecutionPlan(result.workspace), pkg = plan.definition.packages[0]!; const bytes = new TextEncoder().encode("export const Card = () => null;\n"); const paths = [ "components/card.mjs", ...componentSharedModules.map((_name, i) => `share/quixos/shared/runtime-${i}.mjs`), ]; const files = paths.map((file) => ({ path: file, digest: createHash("sha256").update(bytes).digest("hex"), bytes: bytes.length, mediaType: "text/javascript", })); const body: Omit = { id: "component:card", subject: "interface:Data@1", compatibility: "quixos-react-component-v1", path: paths[0]!, exportName: "Card", files, sharedModules: Object.fromEntries( componentSharedModules.map((name, i) => [name, paths[i + 1]!]), ) as ComponentArtifact["sharedModules"], }; const manifest: PackageArtifacts = { schemaVersion: 1, packageRevisionId: pkg.revision, bindingDigest: pkg.bindingDigest, sourceDigest: pkg.sourceDigest, versions: plan.definition.versions, implementations: [], components: [{ ...body, contentDigest: componentArtifactDigest(body) }], }; const world = await checkExecutionWorld(plan, [manifest], async () => bytes); assert.equal(world.artifacts.length, 1); assert.equal(world.artifacts[0]!.target, "browser-renderer"); await assert.rejects( checkExecutionWorld(plan, [{ ...manifest, components: [] }], async () => bytes), /Missing component artifact/, ); const substitute = structuredClone(manifest); substitute.components![0]!.subject = "interface:Other@1"; const { contentDigest: _digest, ...changed } = substitute.components![0]!; substitute.components![0]!.contentDigest = componentArtifactDigest(changed); await assert.rejects( checkExecutionWorld(plan, [substitute], async () => bytes), /declared subject/, ); await assert.rejects( checkExecutionWorld(plan, [manifest], async () => new Uint8Array(bytes.length)), /Artifact bytes/, ); const missingShared = structuredClone(manifest); delete (missingShared.components![0]!.sharedModules as Partial).react; await assert.rejects( checkExecutionWorld(plan, [missingShared], async () => bytes), /missing fields/, ); const root = await mkdtemp(path.join(os.tmpdir(), "camino-component-contract-")); try { for (const file of files) { await mkdir(path.dirname(path.join(root, file.path)), { recursive: true }); await writeFile(path.join(root, file.path), bytes); } await mkdir(path.join(root, "share/quixos"), { recursive: true }); await writeFile(path.join(root, "share/quixos/package-artifacts.json"), JSON.stringify(manifest)); const generator = "a".repeat(64); const { receipt, sources } = expectedArtifactReceipt(plan, manifest, generator); for (const [file, content] of sources) { await mkdir(path.dirname(path.join(root, file)), { recursive: true }); await writeFile(path.join(root, file), content); } await writeFile(path.join(root, "share/quixos/build-receipt.json"), JSON.stringify(receipt)); const installation = await checkExecutionInstallation(plan, { [pkg.revision]: root }, generator); assert.equal(installation.packages[0]!.service, undefined); assert.equal(installation.components[0]!.id, "component:card"); assert.ok(installation.routes[installation.components[0]!.url]); assert.equal(JSON.stringify(installation.components).includes(root), false); assert.equal(JSON.stringify(installation.components).includes("slot:"), false); const catalog = clientContractCatalog(plan, installation, [pkg.revision]); assert.equal(catalog.presentations[0]!.component, "component:card"); assert.equal(catalog.components[0]!.subject, "interface:Data@1"); assert.equal(JSON.stringify(catalog).includes(root), false); assert.equal(JSON.stringify(catalog).includes("bindingSchema"), false); assert.throws(() => clientContractCatalog(plan, installation, ["absent"]), /UNINSTALLED_PACKAGE/); assert.throws( () => clientContractCatalog(plan, { ...installation, plan: "bad" }, [pkg.revision]), /CATALOG_PLAN_MISMATCH/, ); } finally { await rm(root, { recursive: true, force: true }); } });