269 lines
12 KiB
TypeScript
269 lines
12 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
|
|
|
|
const source = { repository: "https://example.test/execution.git", commit: "a".repeat(40) };
|
|
const data = (execution = "snapshot") =>
|
|
compileCapabilityResourceSource(
|
|
`
|
|
interface Data id "interface:data" revision "interface:data@1" {
|
|
queryable portable value label id "member:label" : string {
|
|
get id "operation:label:get" execution ${execution};
|
|
}
|
|
operation refresh id "member:refresh" : unit -> unit { call id "operation:refresh"; }
|
|
}`,
|
|
{ source },
|
|
);
|
|
const program = (selection: string, execution = "snapshot targets [JavaScript, server]") => {
|
|
const contract = data();
|
|
assert.ok(contract.ok, JSON.stringify(contract.diagnostics));
|
|
assert.equal(contract.resource.kind, "interface");
|
|
if (contract.resource.kind !== "interface") throw new Error("Expected interface");
|
|
return compileCapabilityResourceSource(
|
|
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
|
|
|
|
import interface Data;
|
|
package Reader id "package:reader" revision "package:reader@1" {
|
|
function label id "export:label" : unit -> string execution ${execution} requires {
|
|
interface data id "port:data" : Data ${selection};
|
|
};
|
|
}`,
|
|
{
|
|
source,
|
|
environment: {
|
|
interfaces: new Map([["Data", contract.resource.revision]]),
|
|
interfaceClosure: [contract.resource.revision],
|
|
},
|
|
},
|
|
);
|
|
};
|
|
|
|
test("portable contracts preserve exact selected operations and targets", () => {
|
|
const result = program("using [label.get]");
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
assert.equal(result.resource.kind, "package");
|
|
if (result.resource.kind !== "package") return;
|
|
const entry = result.resource.revision.exports[0]!;
|
|
assert.deepEqual(entry.execution, {
|
|
kind: "snapshot",
|
|
targets: ["target:org.quixos.execution.javascript@1", "server"],
|
|
});
|
|
assert.deepEqual(entry.dependencyPorts[0]!.requirement, {
|
|
kind: "interface",
|
|
interfaceRevisionId: "interface:data@1",
|
|
operations: ["operation:label:get"],
|
|
});
|
|
});
|
|
|
|
test("portable dependency diagnostics reject remote defaults and broad acquisition", () => {
|
|
for (const [selection, execution, message] of [
|
|
["using [refresh.call]", "snapshot targets [JavaScript, server]", "remote-only"],
|
|
["", "snapshot targets [JavaScript, server]", "select"],
|
|
["using [label.get, label.get]", "snapshot targets [JavaScript, server]", "duplicates"],
|
|
["using [label.set]", "snapshot targets [JavaScript, server]", "Unknown"],
|
|
]) {
|
|
const result = program(selection!, execution);
|
|
assert.equal(result.ok, false);
|
|
assert.match(JSON.stringify(result.diagnostics), new RegExp(message!));
|
|
}
|
|
});
|
|
|
|
test("execution targets must resolve nominal declarations", () => {
|
|
const result = program("using [label.get]", "snapshot targets [arduino]");
|
|
assert.equal(result.ok, false);
|
|
assert.match(JSON.stringify(result.diagnostics), /Unknown nominal execution target arduino/);
|
|
});
|
|
|
|
test("nominal targets are identified by symbols rather than platform spellings or interfaces", () => {
|
|
const compile = (alias: string, identity: string) =>
|
|
compileCapabilityResourceSource(
|
|
`symbol ${alias} id "${identity}"; package P id "p" revision "p@1" { function f id "f" : unit -> unit execution transaction targets [${alias},server]; }`,
|
|
{ source },
|
|
);
|
|
const device = compile("ESP32", "target:example.device@1");
|
|
const renamed = compile("MyController", "target:example.device@1");
|
|
assert.ok(device.ok && device.resource.kind === "package");
|
|
assert.ok(renamed.ok && renamed.resource.kind === "package");
|
|
assert.deepEqual(device.resource.revision.exports[0]!.execution, renamed.resource.revision.exports[0]!.execution);
|
|
assert.deepEqual(device.resource.externalInterfaces, [], "A target identity has no interface definition dependency");
|
|
assert.equal(compile("server", "target:example.device@1").ok, false);
|
|
assert.equal(compile("Alias", "server").ok, false);
|
|
});
|
|
|
|
test("portable query getters and target declarations are checked", () => {
|
|
assert.equal(data("remote").ok, false);
|
|
assert.equal(data("transaction targets [browser]").ok, false);
|
|
assert.equal(data("snapshot targets [browser, browser]").ok, false);
|
|
});
|
|
|
|
test("snapshot exports reject mutable state ports but transactions admit them", () => {
|
|
const compile = (effect: string) =>
|
|
compileCapabilityResourceSource(
|
|
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
|
|
|
|
package Reader id "package:reader" revision "package:reader@1" {
|
|
function update id "export:update" : string -> unit execution ${effect} targets [JavaScript, server] requires {
|
|
state storage id "port:value" : string [read, write];
|
|
};
|
|
}`,
|
|
{ source },
|
|
);
|
|
const snapshot = compile("snapshot");
|
|
assert.equal(snapshot.ok, false);
|
|
assert.match(JSON.stringify(snapshot.diagnostics), /write primitives/);
|
|
const transaction = compile("transaction");
|
|
assert.ok(transaction.ok, JSON.stringify(transaction.diagnostics));
|
|
});
|
|
|
|
test("query placement distinguishes confirmed windows from local candidate extents", async () => {
|
|
const { compileQuery } = await import("../src/query/compile.js");
|
|
const { linkQueries } = await import("../src/query/link.js");
|
|
const { toBinary, fromBinary, fromJson } = await import("@bufbuild/protobuf");
|
|
const { InstalledQuerySchema } = await import("../src/gen/camino/schema_pb.js");
|
|
const { formatQx, parseQx, walkSyntax, applySourceEdits } = await import("../src/capability-language/index.js");
|
|
const contract = data();
|
|
assert.ok(contract.ok && contract.resource.kind === "interface");
|
|
const iface = contract.resource.revision;
|
|
const text = `import interface Data;
|
|
package Reader id "package:reader" revision "package:reader@1" {
|
|
query Visible id "visible" root workspace Data document "visible.graphql" operation "Visible" { replica; }
|
|
query Presentation id "presentation" root workspace Data document "presentation.graphql" operation "Presentation" { local from Visible; }
|
|
}`;
|
|
const environment = { interfaces: new Map([["Data", iface]]), interfaceClosure: [iface] };
|
|
const compiled = compileCapabilityResourceSource(formatQx(text), { source, environment });
|
|
assert.ok(compiled.ok && compiled.resource.kind === "package", JSON.stringify(compiled.diagnostics));
|
|
const pkg = compiled.resource.revision;
|
|
assert.deepEqual(
|
|
pkg.queries!.map((q) => q.placement),
|
|
[{ kind: "replica" }, { kind: "local", sourceQueryId: "visible" }],
|
|
);
|
|
const parsed = parseQx(text);
|
|
const clause = [...walkSyntax(parsed.root)].find(
|
|
(node) => node.kind === "queryClause" && text.slice(node.start, node.end).startsWith("local"),
|
|
)!;
|
|
const edited = applySourceEdits(text, [{ ...clause, text: "local from Missing;" }]);
|
|
assert.equal(compileCapabilityResourceSource(edited, { source, environment }).ok, false);
|
|
const admission = pkg.queries![0]!,
|
|
local = pkg.queries![1]!;
|
|
const document = (name: string, field: string) =>
|
|
`query ${name} { root { ${field}(first: 10) { entries { node { label } } } } }`;
|
|
pkg.checkedQueries = [
|
|
await compileQuery(admission, [iface], async () => document("Visible", "objects")),
|
|
await compileQuery(local, [iface], async () => document("Presentation", "availableObjects")),
|
|
];
|
|
const confirmed = await compileQuery(local, [iface], async () => document("Presentation", "objects"));
|
|
assert.equal(confirmed.selection[0]!.selection[0]!.name, "objects");
|
|
const remote = structuredClone(iface);
|
|
delete remote.members[0]!.operations[0]!.execution;
|
|
await assert.rejects(
|
|
compileQuery(local, [remote], async () => document("Presentation", "availableObjects")),
|
|
/graph snapshot getter/,
|
|
);
|
|
const { capabilityId } = await import("../src/capability-model/index.js");
|
|
const linked = linkQueries({
|
|
id: capabilityId.workspaceRevision("w@1"),
|
|
workspaceId: capabilityId.workspace("w"),
|
|
sourceRootCommit: "a".repeat(40),
|
|
parentRevisionIds: [],
|
|
atoms: [],
|
|
sharedAttachments: [],
|
|
interfaceImports: [iface],
|
|
packageImports: [pkg],
|
|
conformances: [],
|
|
constructors: [],
|
|
});
|
|
const wire = fromJson(InstalledQuerySchema, linked[1]!.runtime!);
|
|
const roundtrip = fromBinary(InstalledQuerySchema, toBinary(InstalledQuerySchema, wire));
|
|
assert.deepEqual(roundtrip.placement?.kind, { case: "localSourceQueryId", value: "package:reader@1:visible" });
|
|
});
|
|
|
|
test("generated interface ports expose only selected operations", async () => {
|
|
const { generateTypeScriptBindings } = await import("../src/bindings/index.js");
|
|
const contract = data();
|
|
const result = program("using [label.get]");
|
|
assert.ok(contract.ok && contract.resource.kind === "interface");
|
|
assert.ok(result.ok && result.resource.kind === "package");
|
|
const generated = generateTypeScriptBindings(
|
|
{
|
|
format: "quixos-bindings",
|
|
version: 1,
|
|
interfaces: [contract.resource.revision],
|
|
packages: [result.resource.revision],
|
|
},
|
|
"package:reader@1",
|
|
);
|
|
assert.match(generated, /label\.get/);
|
|
assert.doesNotMatch(generated.split("export type Results")[0]!, /refresh\.call/);
|
|
assert.doesNotMatch(generated.split("const specs")[1]!, /refresh\.call/);
|
|
});
|
|
|
|
test("workspace validation checks concrete package and storage effects", async () => {
|
|
const { makeValidCapabilityWorkspace, fixtureId } = await import("./fixtures/capability-model.js");
|
|
const { validateWorkspaceRevision } = await import("../src/capability-model/index.js");
|
|
const workspace = makeValidCapabilityWorkspace();
|
|
const named = workspace.interfaceImports.find((i) => i.revisionId === fixtureId.namedV1)!;
|
|
const setter = named.members.flatMap((m) => m.operations).find((o) => o.id === fixtureId.namedSet)!;
|
|
setter.execution = { kind: "snapshot", targets: ["browser"] };
|
|
assert.ok(validateWorkspaceRevision(workspace).some((i) => i.message.includes("bind write primitives")));
|
|
setter.execution.kind = "transaction";
|
|
assert.deepEqual(validateWorkspaceRevision(workspace), []);
|
|
const summary = workspace.interfaceImports.find((i) => i.revisionId === fixtureId.summaryV1)!;
|
|
summary.members[0]!.operations[0]!.execution = { kind: "snapshot", targets: ["browser"] };
|
|
assert.ok(
|
|
validateWorkspaceRevision(workspace).some(
|
|
(i) => i.path.includes(fixtureId.summaryGet) && i.message.includes("remote-only"),
|
|
),
|
|
);
|
|
});
|
|
|
|
test("generic selected ports preserve operation identities and execution through specialization", async () => {
|
|
const { specializePackageExport, capabilityId } = await import("../src/capability-model/index.js");
|
|
const iface = compileCapabilityResourceSource(
|
|
`interface Cell<value T> id "cell" revision "cell@1" {
|
|
value content id "content" : T { get id "content:get" execution snapshot; }
|
|
}`,
|
|
{ source },
|
|
);
|
|
assert.ok(iface.ok && iface.resource.kind === "interface", JSON.stringify(iface.diagnostics));
|
|
const pkg = compileCapabilityResourceSource(
|
|
`symbol JavaScript id "target:org.quixos.execution.javascript@1";
|
|
import interface Cell;
|
|
package P id "p" revision "p@1" {
|
|
function load<value T> id "read" : unit -> T execution snapshot targets [JavaScript, server] requires {
|
|
interface cell id "cell" : Cell<T> using [content.get];
|
|
};
|
|
}`,
|
|
{
|
|
source,
|
|
environment: {
|
|
interfaces: new Map([["Cell", iface.resource.revision]]),
|
|
interfaceClosure: [iface.resource.revision],
|
|
},
|
|
},
|
|
);
|
|
assert.ok(pkg.ok && pkg.resource.kind === "package", JSON.stringify(pkg.diagnostics));
|
|
const definition = pkg.resource.revision.genericExports![0]!;
|
|
const specialized = specializePackageExport(
|
|
pkg.resource.revision,
|
|
definition,
|
|
[{ kind: "value", type: { kind: "scalar", name: "string" } }],
|
|
{
|
|
arguments: new Map(),
|
|
applyInterface: (id, args) => {
|
|
assert.equal(id, "cell@1");
|
|
assert.deepEqual(args, [{ kind: "value", type: { kind: "scalar", name: "string" } }]);
|
|
return capabilityId.interfaceRevision("cell:string");
|
|
},
|
|
implementsInterface: () => false,
|
|
},
|
|
);
|
|
assert.deepEqual(specialized.execution, {
|
|
kind: "snapshot",
|
|
targets: ["target:org.quixos.execution.javascript@1", "server"],
|
|
});
|
|
assert.deepEqual(specialized.dependencyPorts[0]!.requirement.kind, "interface");
|
|
const requirement = specialized.dependencyPorts[0]!.requirement;
|
|
if (requirement.kind === "interface")
|
|
assert.deepEqual(requirement.operations, [capabilityId.operation("content:get")]);
|
|
});
|