79 lines
3.8 KiB
TypeScript
79 lines
3.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|||
|
|
import { test } from "node:test";
|
||
|
|
import { compileCapabilityResourceSource } from "../src/capability-language/index.js";
|
||
|
|
import { packageBuildContract } from "../src/execution-world/build-contract.js";
|
||
|
|
import { generatePortableTypeScriptBindings } from "../src/bindings/portable.js";
|
||
|
|
import type { BindingSchema } from "../src/bindings/index.js";
|
||
|
|
import type { InterfaceRevision } from "../src/capability-model/types.js";
|
||
|
|
|
||
|
|
function fixture() {
|
||
|
|
const source = { repository: "https://example.test/source", commit: "1".repeat(40) };
|
||
|
|
const interfaces = new Map<string, InterfaceRevision>();
|
||
|
|
for (const name of ["Value", "Unrelated"]) {
|
||
|
|
const result = compileCapabilityResourceSource(
|
||
|
|
`interface ${name} id "${name}" revision "${name}@1" {
|
||
|
|
value text id "text" : string { get id "text:get" execution snapshot; }
|
||
|
|
}`,
|
||
|
|
{ source },
|
||
|
|
);
|
||
|
|
assert.ok(result.ok && result.resource.kind === "interface", JSON.stringify(result.diagnostics));
|
||
|
|
interfaces.set(name, result.resource.revision);
|
||
|
|
}
|
||
|
|
const result = compileCapabilityResourceSource(
|
||
|
|
`import interface Value;
|
||
|
|
package Adapter id "adapter" revision "adapter@1" {
|
||
|
|
operation fetchText id "read" : unit -> string mode call execution snapshot targets [server]
|
||
|
|
receiver any requires { interface data id "value" : Value using [text.get]; };
|
||
|
|
}`,
|
||
|
|
{ source, environment: { interfaces, interfaceClosure: [...interfaces.values()] } },
|
||
|
|
);
|
||
|
|
assert.ok(result.ok && result.resource.kind === "package", JSON.stringify(result.diagnostics));
|
||
|
|
return {
|
||
|
|
format: "quixos-bindings",
|
||
|
|
version: 1,
|
||
|
|
interfaces: [...interfaces.values()],
|
||
|
|
packages: [result.resource.revision],
|
||
|
|
} satisfies BindingSchema;
|
||
|
|
}
|
||
|
|
|
||
|
|
test("package builds ignore exact pins and unrelated declarations, and preserve generated port types", () => {
|
||
|
|
const schema = fixture();
|
||
|
|
const original = packageBuildContract(schema, "adapter@1");
|
||
|
|
const changed = structuredClone(schema);
|
||
|
|
changed.interfaces.forEach((iface) => {
|
||
|
|
iface.source.commit = "2".repeat(40);
|
||
|
|
});
|
||
|
|
changed.packages[0]!.source.commit = "3".repeat(40);
|
||
|
|
changed.interfaces.find((iface) => iface.displayName === "Unrelated")!.members = [];
|
||
|
|
assert.deepEqual(packageBuildContract(changed, "adapter@1"), original);
|
||
|
|
assert.equal(JSON.stringify(original).includes("https://example.test/source"), false);
|
||
|
|
assert.equal(
|
||
|
|
generatePortableTypeScriptBindings(original.definition.bindings, "adapter@1", { target: "server" }),
|
||
|
|
generatePortableTypeScriptBindings(schema, "adapter@1", { target: "server" }),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("changing a consumed interface or an owned port contract invalidates the build", () => {
|
||
|
|
const schema = fixture();
|
||
|
|
const original = packageBuildContract(schema, "adapter@1");
|
||
|
|
const changed = structuredClone(schema);
|
||
|
|
changed.interfaces[0]!.members[0]!.operations[0]!.outputType = { kind: "scalar", name: "bool" };
|
||
|
|
assert.notEqual(packageBuildContract(changed, "adapter@1").digest, original.digest);
|
||
|
|
const own = structuredClone(schema);
|
||
|
|
own.packages[0]!.exports[0]!.outputType = { kind: "scalar", name: "bool" };
|
||
|
|
assert.notEqual(packageBuildContract(own, "adapter@1").digest, original.digest);
|
||
|
|
});
|
||
|
|
|
||
|
|
test("a nominal reference does not import the referenced interface's entire dependency graph", () => {
|
||
|
|
const schema = fixture();
|
||
|
|
schema.packages[0]!.exports[0]!.inputType = {
|
||
|
|
kind: "object-ref",
|
||
|
|
expectation: { kind: "interface", interfaceRevisionId: schema.interfaces[1]!.revisionId },
|
||
|
|
};
|
||
|
|
const original = packageBuildContract(schema, "adapter@1");
|
||
|
|
schema.interfaces[1]!.members = [];
|
||
|
|
assert.deepEqual(packageBuildContract(schema, "adapter@1"), original);
|
||
|
|
schema.packages[0]!.exports[0]!.inputType = { kind: "scalar", name: "string" };
|
||
|
|
assert.notEqual(packageBuildContract(schema, "adapter@1").digest, original.digest);
|
||
|
|
});
|