import { readFileSync } from "node:fs"; import assert from "node:assert/strict"; import { test } from "node:test"; import { compileCapabilityResourceSource } from "../src/capability-language/index.js"; import { compileQuery } from "../src/query/compile.js"; import { defaultQueryBudgets } from "../src/query/types.js"; import { portableValueSchema, tsType } from "../src/bindings/portable.js"; import { isStorableType, TypeSubstitution } from "../src/capability-model/index.js"; const source = { repository: "https://example.test/variants.git", commit: "a".repeat(40) }; const declaration = readFileSync("test/fixtures/canvas-content.qx", "utf8"); const result = compileCapabilityResourceSource(declaration, { source }); assert.ok(result.ok, JSON.stringify(result.diagnostics)); if (result.resource.kind !== "interface") throw Error("Expected interface"); const iface = result.resource.revision; const entries = iface.members[0]; if (entries.kind !== "value") throw Error("Expected value"); test("native variant aliases lower into nominal cases with typed payloads", () => { assert.equal(entries.valueType.kind, "list"); assert.ok(isStorableType(entries.valueType)); const schema = portableValueSchema(entries.valueType) as any; assert.equal(schema.value.id, "canvas:entry@1"); assert.equal(schema.value.cases.stroke.id, "canvas:stroke"); assert.match(tsType(entries.valueType), /kind: "stroke"/); assert.match(tsType(entries.valueType), /ReadonlyArray<.*"x": number/); const substitution = new TypeSubstitution({ arguments: new Map([["T", { kind: "value", type: { kind: "scalar", name: "string" } }]]), applyInterface: () => { throw Error("unused"); }, implementsInterface: () => false, }); assert.deepEqual( substitution.value({ kind: "variant", id: "box", cases: { some: { id: "box:some", value: { kind: "parameter", parameterId: "T" } } }, }), { kind: "variant", id: "box", cases: { some: { id: "box:some", value: { kind: "scalar", name: "string" } } } }, ); }); test("variant query scalars keep their checked payload type and reject branch selection", async () => { const query = { id: "canvas-query", displayName: "Canvas", root: { kind: "object" as const, interface: iface.revisionId }, document: "canvas.graphql", operation: "Canvas", fragments: [], views: [], allowances: [], budgets: { ...defaultQueryBudgets }, watch: true, }; const checked = await compileQuery(query, [iface], async () => `query Canvas { root { entries } }`); assert.equal(checked.output.kind, "record"); if (checked.output.kind !== "record") return; const root = checked.output.fields.root; assert.equal(root.kind, "record"); if (root.kind !== "record") return; assert.deepEqual(root.fields.entries, entries.valueType); await assert.rejects(compileQuery(query, [iface], async () => `query Canvas { root { entries { kind value } } }`)); }); test("unknown and duplicate cases fail; reference payloads remain nonstorable", () => { const duplicate = compileCapabilityResourceSource(declaration.replace('"canvas:image"', '"canvas:stroke"'), { source, }); assert.equal(duplicate.ok, false); assert.ok( !isStorableType({ kind: "variant", id: "transport", cases: { ref: { id: "ref", value: { kind: "object-ref", expectation: { kind: "interface", interfaceRevisionId: iface.revisionId } }, }, }, }), ); });