import assert from "node:assert/strict"; import { mkdtemp, mkdir, rm, writeFile } from "node:fs/promises"; import os from "node:os"; import path from "node:path"; import test from "node:test"; import { compileCapabilityResourceRepository, compileWorkspaceRepository, } from "../src/capability-language/index.js"; const quixosCommit = "1".repeat(40); const namedCommit = "2".repeat(40); const packageCommit = "3".repeat(40); const lock = (resources = "") => `quixos-lock version 1 { quixos source { repository "https://example.test/quixos.git"; commit "${quixosCommit}"; } ${resources} } `; test("workspace assembly resolves resource-owned dependencies recursively", async (context) => { const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-assembly-")); context.after(() => rm(directory, { recursive: true, force: true })); const root = path.join(directory, "root"); const named = path.join(directory, "named"); const runtime = path.join(directory, "runtime"); await Promise.all([mkdir(root), mkdir(named), mkdir(runtime)]); await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source { repository "https://example.test/package-runtime.git"; commit "${packageCommit}"; }`)); await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { atom Subject id "atom:subject"; import package Runtime; } `); await writeFile(path.join(named, "quixos.lock"), lock()); await writeFile(path.join(named, "interface.qx"), `interface Named id "interface:named" revision "interface:named@1" { value name id "member:named:name" : string { get id "operation:named:name:get"; } } `); await writeFile(path.join(runtime, "quixos.lock"), lock(`interface Named source { repository "https://example.test/interface-named.git"; commit "${namedCommit}"; }`)); await writeFile(path.join(runtime, "package.qx"), `import interface Named; package Runtime id "package:runtime" revision "package:runtime@1" { function describe id "export:runtime:describe" : interface-ref -> string; } `); const directories = new Map([ [`interface\0https://example.test/interface-named.git\0${namedCommit}`, named], [`package\0https://example.test/package-runtime.git\0${packageCommit}`, runtime], ]); const result = await compileWorkspaceRepository({ rootDirectory: root, resolveResource: async (source, kind) => { const resolved = directories.get(`${kind}\0${source.repository}\0${source.commit}`); if (!resolved) throw new Error(`Unexpected source ${source.repository}`); return { directory: resolved }; }, }); assert.equal(result.resources.length, 2); assert.deepEqual( result.workspace.interfaceImports.map((entry) => entry.revisionId), ["interface:named@1"], ); assert.deepEqual( result.workspace.packageImports.map((entry) => entry.revisionId), ["package:runtime@1"], ); assert.equal(result.directResources.size, 1); const resource = await compileCapabilityResourceRepository({ rootDirectory: runtime, kind: "package", source: { resolver: "git", repository: "https://example.test/package-runtime.git", commit: packageCommit, }, resolveResource: async (source, kind) => { const resolved = directories.get(`${kind}\0${source.repository}\0${source.commit}`); if (!resolved) throw new Error(`Unexpected source ${source.repository}`); return { directory: resolved }; }, }); assert.equal(resource.resource.kind, "package"); assert.equal(resource.resources.length, 2); assert.equal(resource.directResources.size, 1); }); test("resource manifests cannot hide lock dependencies", async (context) => { const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-assembly-mismatch-")); context.after(() => rm(directory, { recursive: true, force: true })); const root = path.join(directory, "root"); const runtime = path.join(directory, "runtime"); await Promise.all([mkdir(root), mkdir(runtime)]); await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source { repository "https://example.test/package-runtime.git"; commit "${packageCommit}"; }`)); await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { atom Subject id "atom:subject"; import package Runtime; } `); await writeFile(path.join(runtime, "quixos.lock"), lock()); await writeFile(path.join(runtime, "package.qx"), `import interface Hidden; package Runtime id "package:runtime" revision "package:runtime@1" { } `); await assert.rejects( compileWorkspaceRepository({ rootDirectory: root, resolveResource: async () => ({ directory: runtime }), }), /No resolved interface is available for lock binding Hidden/, ); }); test("workspace assembly must satisfy nominal external interfaces", async (context) => { const directory = await mkdtemp(path.join(os.tmpdir(), "quixos-assembly-external-")); context.after(() => rm(directory, { recursive: true, force: true })); const root = path.join(directory, "root"); const runtime = path.join(directory, "runtime"); await Promise.all([mkdir(root), mkdir(runtime)]); await writeFile(path.join(root, "quixos.lock"), lock(`package Runtime source { repository "https://example.test/package-runtime.git"; commit "${packageCommit}"; }`)); await writeFile(path.join(root, "workspace.qx"), `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"4".repeat(40)}" { atom Subject id "atom:subject"; import package Runtime; } `); await writeFile(path.join(runtime, "quixos.lock"), lock()); await writeFile(path.join(runtime, "package.qx"), `external interface Named revision "interface:named@1"; package Runtime id "package:runtime" revision "package:runtime@1" { } `); await assert.rejects( compileWorkspaceRepository({ rootDirectory: root, resolveResource: async () => ({ directory: runtime }), }), /requires external interface Named \(interface:named@1\)/, ); });