Files
quixos-protocol/test/query-fragments.test.ts
T

233 lines
9.7 KiB
TypeScript
Raw Normal View History

import assert from "node:assert/strict";
import test from "node:test";
import { compileCapabilityResourceSource } from "../src/capability-language/parser.js";
import { compileQuery } from "../src/query/compile.js";
import type { PackageRevision } from "../src/capability-model/types.js";
const source = { repository: "https://example.test/fragments", commit: "a".repeat(40) };
const compiled = compileCapabilityResourceSource(
`interface Item id "item" revision "item@1" {
queryable value title id "title" : string {get id "title:get" execution snapshot;}
queryable rpc value remote id "remote" : string {get id "remote:get";}
}`,
{ source },
);
if (!compiled.ok || compiled.resource.kind !== "interface") throw Error(JSON.stringify(compiled.diagnostics));
const item = compiled.resource.revision;
function pkg(body: string, imports: PackageRevision[] = [], name = "Views") {
const result = compileCapabilityResourceSource(
`import interface Item; ${imports.map((p) => `import package ${p.displayName};`).join(" ")} package ${name} id "${name}" revision "${name}@1" {${body}}`,
{
source,
environment: {
interfaces: new Map([["Item", item]]),
interfaceClosure: [item],
packages: new Map(imports.map((p) => [p.displayName, p])),
},
},
);
assert.ok(result.ok, JSON.stringify(result.diagnostics));
if (result.resource.kind !== "package") throw Error("package required");
return result.resource.revision;
}
test("exported fragments generate local projection and direct admission", async () => {
const p = pkg('query fragment Row id "row" root Item document "row.graphql" fragment "Row";');
assert.equal(p.queries!.length, 2);
const local = p.queries!.find((q) => !q.fragment?.admission)!;
assert.equal(local.placement?.kind, "local");
const checked = await compileQuery(local, [item], async () => "fragment Row on Item {title}");
assert.equal(checked.output.kind, "record");
assert.match(checked.document, /query QuixosFragment/);
await assert.rejects(
compileQuery(local, [item], async () => "fragment Row on Item {remote}"),
/QUERY_LOCAL_DEPENDENCY/,
);
});
test("imported fragments retain checked occurrences and require exact roots", async () => {
const provider = pkg('query fragment Row id "row" root Item document "row.graphql" fragment "Row";', [], "Provider");
const checked = await compileQuery(
provider.queries!.find((q) => !q.fragment?.admission)!,
[item],
async () => "fragment Row on Item {title}",
);
const consumer = pkg(
'query Screen id "screen" root Item document "screen.graphql" operation "Screen" {replica; fragments Provider.Row;}',
[provider],
);
const result = await compileQuery(
consumer.queries![0]!,
[item],
async () => "query Screen {root {...Row}}",
async () => checked,
);
assert.equal(result.selection[0]!.selection[0]!.fragmentSpread?.digest, checked.definitionDigest);
assert.equal(result.selection[0]!.selection[0]!.fragmentSpread?.root, item.revisionId);
assert.ok(result.effects.some((e) => e.memberId === "title"));
const bound = await compileQuery(
{
...consumer.queries![0]!,
importedFragments: consumer.queries![0]!.importedFragments!.map((entry) => ({ ...entry, mode: "reference" })),
},
[item],
async () => "query Screen {root {...Row}}",
async () => checked,
);
assert.equal(bound.selection[0]!.selection.find((entry) => entry.fragmentSpread)?.fragmentSpread?.mode, "reference");
assert(
!bound.selection[0]!.selection.some((entry) => entry.memberId === "title"),
"A fragment binding does not evaluate its fields in the parent",
);
assert(!JSON.stringify(bound.output).includes('"title"'), "Parent output excludes separately observed fields");
const wrong = {
...checked,
declaration: {
...checked.declaration,
root: { ...checked.declaration.root, interface: "different@1" as typeof item.revisionId },
},
};
await assert.rejects(
compileQuery(
consumer.queries![0]!,
[item],
async () => "query Screen {root {...Row}}",
async () => wrong,
),
/FRAGMENT_ROOT_MISMATCH/,
);
});
test("fragment variable mappings are explicit and checked", async () => {
const provider = pkg(
'query fragment Row id "row" root Item document "row.graphql" fragment "Row" operation "Inputs";',
[],
"Provider",
);
const checked = await compileQuery(
provider.queries!.find((q) => !q.fragment?.admission)!,
[item],
async () =>
"query Inputs($show: Boolean!) {root {...Row}} fragment Row on Item {_camino {ref} title @include(if:$show)}",
);
const build = (mapping: string) =>
pkg(
`query Screen id "screen" root Item document "screen.graphql" operation "Screen" {replica; fragments Provider.Row ${mapping};}`,
[provider],
).queries![0]!;
const document = "query Screen($visible: Boolean!) {root {...Row}}";
await assert.rejects(
compileQuery(
build(""),
[item],
async () => document,
async () => checked,
),
/FRAGMENT_VARIABLE_MAPPING/,
);
const result = await compileQuery(
build("with {show to visible;}"),
[item],
async () => document,
async () => checked,
);
assert.deepEqual(result.selection[0]!.selection[0]!.fragmentSpread?.variables, { show: "visible" });
const referenceDeclaration = build("with {show to visible;}");
referenceDeclaration.importedFragments = referenceDeclaration.importedFragments!.map((entry) => ({
...entry,
mode: "reference",
}));
const binding = await compileQuery(
referenceDeclaration,
[item],
async () => document,
async () => checked,
);
assert.deepEqual(binding.selection[0]!.selection[0]!.fragmentSpread?.variables, { show: "visible" });
assert.notEqual(binding.definitionDigest, result.definitionDigest);
await assert.rejects(
compileQuery(
referenceDeclaration,
[item],
async () => document.replace("$visible: Boolean!", "$visible: Boolean!, $unused: Boolean!"),
async () => checked,
),
/never used/,
);
const changedDigest = await compileQuery(
referenceDeclaration,
[item],
async () => document,
async () => ({ ...checked, definitionDigest: "b".repeat(64) }),
);
assert.notEqual(
changedDigest.definitionDigest,
binding.definitionDigest,
"A bound fragment's identity remains in the parent contract digest",
);
await assert.rejects(
compileQuery(
build("with {show to visible;}"),
[item],
async () => document.replace("Boolean!", "Boolean"),
async () => checked,
),
/FRAGMENT_VARIABLE_TYPE/,
);
});
test("independent fragment package survives execution lowering and typed client generation", async () => {
const { loadReaderWorkspace, readReaderFixture } = await import("./reader-fixture.js");
const { compileExecutionPlan } = await import("../src/execution-world/plan.js");
const { compileQueryProgram } = await import("../src/execution-world/queries.js");
const { generatePackageQueryClientBindings } = await import("../src/execution-world/client-contracts.js");
const workspace = await loadReaderWorkspace();
const comment = workspace.interfaceImports.find((i) => i.displayName === "CommentData")!;
const parsed = compileCapabilityResourceSource(
`import interface CommentData; package CommentRows id "comment-rows" revision "comment-rows@1" {
query fragment Row id "row" root CommentData document "row.graphql" fragment "CommentRow";
}`,
{ source, environment: { interfaces: new Map([["CommentData", comment]]), interfaceClosure: [comment] } },
);
assert.ok(parsed.ok, JSON.stringify(parsed.diagnostics));
if (parsed.resource.kind !== "package") throw Error("expected package");
const provider = parsed.resource.revision;
provider.checkedQueries = await Promise.all(
provider.queries!.map((q) =>
compileQuery(
q,
workspace.interfaceImports,
async () => "fragment CommentRow on CommentData {content {text tags}}",
),
),
);
workspace.packageImports.push(provider);
const consumer = workspace.packageImports.find((p) => p.displayName === "ReaderRuntime")!;
const declaration = consumer.queries!.find((q) => q.displayName === "ReaderScreen")!;
declaration.importedFragments = [
{ packageRevisionId: provider.revisionId, packageAlias: "CommentRows", fragmentId: "row", variables: {} },
];
const document = (await readReaderFixture("queries/reader-screen.graphql")).replace(
"_camino { ref } content { text tags }",
"...CommentRow",
);
const checked = await compileQuery(
declaration,
workspace.interfaceImports,
async () => document,
async () => provider.checkedQueries!.find((q) => q.declaration.id === "row")!,
);
consumer.checkedQueries = consumer.checkedQueries!.map((q) => (q.declaration.id === declaration.id ? checked : q));
const plan = compileExecutionPlan(workspace);
const program = compileQueryProgram(plan) as {
queries: { id: string; fragment?: { package: string; id: string }; selection: unknown }[];
};
assert.deepEqual(program.queries.find((q) => q.fragment)?.fragment, {
package: provider.revisionId,
id: "row",
digest: provider.checkedQueries!.find((q) => q.declaration.id === "row")!.definitionDigest,
});
const screen = program.queries.find((q) => q.id === JSON.stringify([consumer.revisionId, declaration.id]))!;
assert.match(JSON.stringify(screen.selection), /fragmentSpread/);
const generated = generatePackageQueryClientBindings(consumer, undefined, workspace.packageImports);
assert.match(generated, /"CommentRows": \{"Row": definePackageFragment/);
assert.ok(generated.includes(JSON.stringify(comment.revisionId)));
assert.ok(!generated.includes('"Row$inputs": definePackageQuery'));
});