138 lines
7.2 KiB
TypeScript
138 lines
7.2 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import { test } from "node:test";
|
|
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/parser.js";
|
|
import { compileExecutionPlan, compileKernelProgram } from "../src/execution-world/index.js";
|
|
import { generatePortableTypeScriptBindings } from "../src/bindings/portable.js";
|
|
import { formatQx } from "../src/capability-language/source.js";
|
|
const source = { repository: "https://example.test/reference.git", commit: "a".repeat(40) };
|
|
const iface = `interface Data id "data" revision "data@1" {
|
|
value content id "content" : string { get id "content:get" execution snapshot; }
|
|
operation peek id "peek" : interface-ref<Data> -> string { call id "peek:call" execution snapshot; }
|
|
}`;
|
|
const pkg = `symbol JavaScript id "target:org.quixos.execution.javascript@1";
|
|
external atom Item id "item";
|
|
import interface Data;
|
|
package Runtime id "runtime" revision "runtime@1" {
|
|
operation peek id "runtime:peek" : interface-ref<Data> -> string mode call execution snapshot targets [server]
|
|
receiver atom Item requires { interface peers id "peers" : Data using [content.get] from input; };
|
|
}`;
|
|
const workspace = `workspace Test id "workspace:test" revision "workspace:test@1" commit "${"a".repeat(40)}" {
|
|
import interface Data; import package Runtime; atom Item id "item";
|
|
conform Item as Data id "item:data" {
|
|
private state Content id "content" on Item : string policy register default "hello";
|
|
bind content.get to state Content.read;
|
|
bind peek.call to package Runtime.peek with { peers to interface Data from input; };
|
|
}
|
|
}`;
|
|
function compile(packageSource = pkg, workspaceSource = workspace, interfaceSource = iface) {
|
|
const contract = compileCapabilityResourceSource(interfaceSource, { source });
|
|
assert.ok(contract.ok && contract.resource.kind === "interface", JSON.stringify(contract.diagnostics));
|
|
const interfaces = new Map([["Data", contract.resource.revision]]);
|
|
const runtime = compileCapabilityResourceSource(packageSource, { source, environment: { interfaces } });
|
|
if (!runtime.ok) return runtime;
|
|
assert.equal(runtime.resource.kind, "package");
|
|
if (runtime.resource.kind !== "package") throw Error("Expected package");
|
|
return compileCapabilitySource(workspaceSource, "workspace.qx", {
|
|
interfaces,
|
|
packages: new Map([["Runtime", runtime.resource.revision]]),
|
|
});
|
|
}
|
|
test("reference-selected dependencies round trip and do not require a recursive receiver conformance", () => {
|
|
const result = compile(formatQx(pkg), formatQx(workspace));
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
const plan = compileExecutionPlan(result.workspace);
|
|
const kernel = compileKernelProgram(plan) as {
|
|
operations: { operation: string; binding: { ports: { receiver?: string }[] } }[];
|
|
};
|
|
assert.equal(kernel.operations.find((op) => op.operation === "peek:call")!.binding.ports[0]!.receiver, "input");
|
|
const peek = plan.definition.operations.find((op) => op.operationId === "peek:call")!;
|
|
assert.ok(
|
|
peek.dependencies.some(
|
|
(dependency) => dependency.kind === "operation" && dependency.operation.includes("content:get"),
|
|
),
|
|
);
|
|
const generated = generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", {
|
|
target: "server",
|
|
});
|
|
assert.ok(generated.includes('ManagedReference<"interface", "data@1">'));
|
|
assert.ok(generated.includes('"receiver"'));
|
|
});
|
|
test("both sides explicitly grant reference selection and remote execution is excluded", () => {
|
|
assert.equal(compile(pkg, workspace.replace("Data from input", "Data")).ok, false);
|
|
assert.equal(compile(pkg.replace(" from input", ""), workspace).ok, false);
|
|
assert.equal(compile(pkg.replace("execution snapshot targets [server]", "execution remote"), workspace).ok, false);
|
|
assert.equal(compile(pkg.replace("using [content.get]", "")).ok, false);
|
|
});
|
|
test("closed generic specialization preserves reference-selected interface ports", () => {
|
|
const result = compile(
|
|
pkg.replace("operation peek id", "operation peek<value T> id"),
|
|
workspace.replace("Runtime.peek with", "Runtime.peek<string> with"),
|
|
);
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
const plan = compileExecutionPlan(result.workspace);
|
|
const entry = plan.definition.bindingSchema.packages[0]!.exports[0]!;
|
|
assert.ok(
|
|
entry.dependencyPorts.some(
|
|
(port) => port.requirement.kind === "interface" && port.requirement.receiver === "input",
|
|
),
|
|
);
|
|
assert.ok(
|
|
generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", { target: "server" }).includes(
|
|
'"receiver"',
|
|
),
|
|
);
|
|
});
|
|
|
|
test("portable input-selected reads use installed support beyond the public minimum", () => {
|
|
const portable = pkg.replaceAll("targets [server]", "targets [JavaScript, server]");
|
|
const optional = compile(portable);
|
|
assert.ok(optional.ok, JSON.stringify(optional.diagnostics));
|
|
const plan = compileExecutionPlan(optional.workspace);
|
|
assert.deepEqual(plan.definition.operations.find((op) => op.operationId === "peek:call")!.availableTargets, [
|
|
"server",
|
|
"target:org.quixos.execution.javascript@1",
|
|
]);
|
|
const result = compile(portable, workspace, iface);
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
});
|
|
|
|
test("input-selected transactions compose only within a compatible transaction contract", () => {
|
|
const txInterface = iface
|
|
.replace('get id "content:get" execution snapshot', 'get id "content:get" execution transaction')
|
|
.replace('call id "peek:call" execution snapshot', 'call id "peek:call" execution transaction');
|
|
const txPackage = pkg.replace("execution snapshot targets [server]", "execution transaction targets [server]");
|
|
// Use an operation for the selected write; value getters themselves must remain snapshots.
|
|
const contract = txInterface.replace(
|
|
'value content id "content" : string { get id "content:get" execution transaction; }',
|
|
'operation update id "content" : unit -> string { call id "content:get" execution transaction; }',
|
|
);
|
|
const implementation = txPackage.replace("using [content.get]", "using [update.call]");
|
|
const model = workspace.replace(
|
|
"bind content.get to state Content.read;",
|
|
"bind update.call to package Runtime.update with { data to state Content; };",
|
|
);
|
|
const sourcePackage = implementation.replace(
|
|
/\n}$/,
|
|
'\n operation update id "runtime:update" : unit -> string mode call execution transaction targets [server] receiver atom Item requires {state data id "data" : string [read,write];};\n}',
|
|
);
|
|
const result = compile(sourcePackage, model, contract);
|
|
assert.ok(result.ok, JSON.stringify(result.diagnostics));
|
|
const plan = compileExecutionPlan(result.workspace);
|
|
const generated = generatePortableTypeScriptBindings(plan.definition.bindingSchema, "runtime@1", {
|
|
target: "server",
|
|
});
|
|
assert.ok(generated.includes('"receiver"'));
|
|
assert.ok(generated.includes('"update.call"'));
|
|
assert.equal(
|
|
compile(
|
|
sourcePackage.replace(
|
|
'operation peek id "runtime:peek" : interface-ref<Data> -> string mode call execution transaction',
|
|
'operation peek id "runtime:peek" : interface-ref<Data> -> string mode call execution snapshot',
|
|
),
|
|
model,
|
|
contract,
|
|
).ok,
|
|
false,
|
|
);
|
|
});
|