Files
quixos-protocol/test/forwarding.test.ts
T
Timothy J. Aveni 1aa55f7a35 Add composable interface operation forwarding
Support bind member.operation to interface Target<Args>.member.operation.
Check exact signatures and scopes, same-receiver conformances, cycles and
watch pairing. Resolve chains for native queries, editing and dispatch while
retaining the terminal conformance owner and injected package dependencies.
Track forwarded providers in dependency and semantic-major review machinery.

Cover native get/set/watch chains, generic targets, invalid bindings, package
forwarding, runtime owner authority and watch cancellation. Document the
language feature and keep Studio search adoption explicitly pending.
No push or deployment.
2026-09-19 10:18:47 -07:00

150 lines
6.7 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js";
import { compileWorkspaceRevision, resolveOperationPlan } from "../src/capability-model/validation.js";
import { capabilityId, type InterfaceRevision } from "../src/capability-model/types.js";
import { compileQuery } from "../src/query/compile.js";
import { linkQueries } from "../src/query/link.js";
import { compileCapabilityFixture, fixtureId } from "./fixtures/capability-model.js";
const source = { repository: "https://example.test/forwarding.git", commit: "a".repeat(40) };
function fixture(transform = (s: string) => s, generic = false) {
const interfaces = new Map<string, InterfaceRevision>();
for (const name of ["Owner", "Alias", "Relay"]) {
const result = compileCapabilityResourceSource(
`interface ${name}${generic && name === "Owner" ? "<value T>" : ""} id "${name}" revision "${name}@1" {
queryable value title id "${name}:title" : ${generic && name === "Owner" ? "T" : "string"} {
get id "${name}:get"; set id "${name}:set";
watch start id "${name}:start" stop id "${name}:stop";
}
}`,
{ source },
);
assert.ok(result.ok, JSON.stringify(result.diagnostics));
if (result.resource.kind !== "interface") throw new Error("interface expected");
interfaces.set(name, result.resource.revision);
}
const forward = (name: string, target: string) => `conform Item as ${name} id "${name}-conformance" {
bind title.get to interface ${target}.title.get;
bind title.set to interface ${target}.title.set;
bind title.watch-start to interface ${target}.title.watch-start;
bind title.watch-stop to interface ${target}.title.watch-stop;
}`;
const text = `workspace Test id "ws" revision "ws@1" commit "${source.commit}" {
import interface Owner; import interface Alias; import interface Relay;
atom Item id "item";
${forward("Relay", "Alias")}
${forward("Alias", generic ? "Owner<string>" : "Owner")}
conform Item as Owner${generic ? "<string>" : ""} id "owner-conformance" {
private state Title id "title" on Item : string policy optimistic-register default "";
bind title to state Title;
}
}`;
return { result: compileCapabilitySource(transform(text), "workspace.qx", { interfaces }), interfaces };
}
test("bind chains forward native reads, writes and watches without changing storage ownership", async () => {
const { result, interfaces } = fixture();
assert.ok(result.ok, JSON.stringify(result.diagnostics));
const compiled = compileWorkspaceRevision(result.workspace);
assert.ok(compiled.ok);
for (const [operation, primitive] of [
["get", "read"],
["set", "write"],
["start", "watch-start"],
["stop", "watch-stop"],
]) {
const resolved = resolveOperationPlan(
compiled.plan,
capabilityId.atom("item"),
capabilityId.interfaceRevision("Relay@1"),
capabilityId.operation(`Relay:${operation}`),
);
assert.equal(resolved?.binding.kind, "state");
if (resolved?.kind !== "state") throw new Error("state expected");
assert.equal(resolved.binding.primitive, primitive);
assert.deepEqual(resolved.attachment.owner, {
kind: "conformance",
atomId: "item",
interfaceRevisionId: "Owner@1",
});
}
const pkg = compileCapabilityResourceSource(
`import interface Relay;
package Views id "views" revision "views@1" { query Rows id "rows" root workspace Relay document "rows.graphql" operation "Rows" {max rows 10; watch;} }`,
{ source, environment: { interfaces } },
);
assert.ok(pkg.ok, JSON.stringify(pkg.diagnostics));
if (pkg.resource.kind !== "package") throw new Error("package expected");
const revision = pkg.resource.revision;
revision.checkedQueries = [
await compileQuery(
revision.queries![0]!,
[...interfaces.values()],
async () => `query Rows {root {objects(first: 10) {entries {node {title @live}}}}}`,
),
];
result.workspace.packageImports.push(revision);
const linked = linkQueries(result.workspace)[0]!;
assert.equal(linked.fields[0]!.binding.kind, "state");
assert.equal(linked.attachments[0]!.id, "title");
});
test("forwarding rejects cycles, wrong signatures, missing targets, and direct private access", () => {
for (const transform of [
(s: string) => s.replaceAll("interface Owner.title", "interface Relay.title"),
(s: string) => s.replace("interface Owner.title.get", "interface Owner.title.set"),
(s: string) =>
s
.replace("conform Item as Owner", "conform Other as Owner")
.replace('atom Item id "item";', 'atom Item id "item"; atom Other id "other";')
.replace("on Item :", "on Other :"),
(s: string) => s.replace("interface Owner.title.get", "state Title.read"),
(s: string) => s.replace("interface Owner.title.watch-stop", "interface Relay.title.watch-stop"),
])
assert.equal(fixture(transform).result.ok, false);
});
test("forwarding closes generic targets and rejects incompatible applications", () => {
const good = fixture(undefined, true).result;
assert.ok(good.ok, JSON.stringify(good.diagnostics));
const bad = fixture(
(s) => s.replace("interface Owner<string>.title.get", "interface Owner<int32>.title.get"),
true,
).result;
assert.equal(bad.ok, false);
});
test("forwarded package actions retain the provider and its dependency ports", () => {
const fixture = compileCapabilityFixture();
assert.ok(fixture.ok, JSON.stringify(fixture.diagnostics));
const ws = fixture.workspace;
const target = ws.interfaceImports.find((i) => i.revisionId === fixtureId.summaryV1)!;
const alias = {
...structuredClone(target),
interfaceId: capabilityId.interface("alias"),
revisionId: capabilityId.interfaceRevision("alias@1"),
};
ws.interfaceImports.push(alias);
ws.conformances.push({
atomId: fixtureId.project,
interfaceRevisionId: alias.revisionId,
id: capabilityId.conformance("alias-owner"),
privateAttachments: [],
relationshipMaterializations: [],
operationBindings: [
{
operationId: fixtureId.summaryGet,
binding: { kind: "interface", interfaceRevisionId: fixtureId.summaryV1, operationId: fixtureId.summaryGet },
},
],
});
const result = compileWorkspaceRevision(ws);
assert.ok(result.ok, JSON.stringify(result));
const plan = resolveOperationPlan(result.plan, fixtureId.project, alias.revisionId, fixtureId.summaryGet);
assert.equal(plan?.kind, "package");
if (plan?.kind !== "package") throw new Error("package expected");
assert.equal(plan.binding.exportId, fixtureId.summaryGetExport);
assert.ok(plan.dependencies.length > 0);
});