83 lines
3.4 KiB
TypeScript
83 lines
3.4 KiB
TypeScript
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 } },
|
||
|
|
},
|
||
|
|
},
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
});
|