Files
quixos-protocol/test/prepared-presentations.test.ts
T

184 lines
9.0 KiB
TypeScript

import assert from "node:assert/strict";
import { test } from "node:test";
import { compileCapabilityResourceSource, compileCapabilitySource } from "../src/capability-language/index.js";
import type { InterfaceRevision, PackageRevision } from "../src/capability-model/types.js";
import { compileQuery } from "../src/query/compile.js";
import { compileQueryComposition } from "../src/query/composition.js";
import { defaultQueryBudgets, type CheckedQuery } from "../src/query/types.js";
import {
presentationDeclarations,
preparationArtifact,
type ReactPreparationArtifact,
} from "../src/react-build/declarations.js";
import { completeWorkspace } from "../src/react-build/workspace.js";
const source = { repository: "https://example.test/prepared", commit: "a".repeat(40) };
async function fixture(replacement = "First", change?: (artifact: ReactPreparationArtifact) => void) {
const interfaces = new Map<string, InterfaceRevision>();
const resource = (text: string) => {
const result = compileCapabilityResourceSource(text, {
source,
environment: { interfaces, interfaceClosure: [...interfaces.values()] },
});
assert.ok(result.ok, JSON.stringify(result.diagnostics));
return result.resource;
};
for (const text of [
'interface Row id "row" revision "row@1" { queryable value title id "title" : string {get id "title:get" execution snapshot targets [browser, server];} queryable value detail id "detail" : string {get id "detail:get" execution snapshot targets [browser, server];} }',
'import interface Row; interface Library id "library" revision "library@1" { queryable relation selected id "selected" : optional-one interface Row {resolve id "selected:get" execution snapshot targets [browser, server];} }',
'import interface Row; interface RowRole id "row-role" revision "row-role@1" { module render id "row-render" contract "org.quixos.react.subject-only/1" subject interface Row; }',
'import interface Library; interface LibraryRole id "library-role" revision "library-role@1" { module render id "library-render" contract "org.quixos.react.subject-only/1" subject interface Library; }',
]) {
const result = resource(text);
assert.equal(result.kind, "interface");
if (result.kind === "interface") interfaces.set(result.revision.displayName, result.revision);
}
const result = resource(
'import interface Row; import interface Library; import interface RowRole; import interface LibraryRole; package UI id "ui" revision "ui@1" { module LibraryCard id "library-card" implements LibraryRole.render; module First id "first" implements RowRole.render; module Second id "second" implements RowRole.render; }',
);
assert.equal(result.kind, "package");
if (result.kind !== "package") throw Error("package expected");
const pkg: PackageRevision = result.revision;
const checked: CheckedQuery[] = [];
for (const [name, iface, fields] of [
["LibraryData", "library@1", "selected {_camino {ref atom}}"],
["Title", "row@1", "title"],
["Detail", "row@1", "detail"],
]) {
for (const local of [false, true]) {
const id = name + (local ? "" : "Inputs");
checked.push(
await compileQuery(
{
id,
displayName: id,
root: {
kind: "object",
interface: [...interfaces.values()].find((entry) => entry.revisionId === iface)!.revisionId,
},
document: id + ".graphql",
operation: id,
fragments: [],
views: [],
allowances: [],
budgets: { ...defaultQueryBudgets },
watch: local,
placement: local ? { kind: "local", sourceQueryId: name + "Inputs" } : { kind: "replica" },
},
[...interfaces.values()],
async () => `query ${id} {root {${fields}}}`,
),
);
}
}
pkg.checkedQueries = checked;
pkg.queries = checked.map((query) => query.declaration);
const input = (query: string) => ({
name: "data",
query,
root: { from: "input", path: ["subject"] },
arguments: { record: {} },
});
const authored = {
modules: {
LibraryCard: {
inputs: [input("LibraryData")],
children: [
{
name: "selected",
subject: "Row",
slot: "selected",
with: { from: "result", name: "data", path: ["root", "selected"] },
root: { from: "item", path: ["_camino", "ref"] },
role: { interface: "RowRole", member: "render" },
},
],
},
First: { inputs: [input("Title")] },
Second: { inputs: [input("Detail")] },
},
};
const declarations = presentationDeclarations(JSON.stringify(authored), pkg, [...interfaces.values()]);
change?.(declarations.artifact);
pkg.buildArtifacts = { [preparationArtifact]: declarations.artifact };
pkg.queryCompositions = declarations.compositions.map((composition) =>
compileQueryComposition(composition, new Map(checked.map((query) => [query.declaration.id, query]))),
);
const workspace = compileCapabilitySource(
`workspace Example id "example" revision "example@1" commit "${source.commit}" {
import interface Row; import interface Library; import interface RowRole; import interface LibraryRole; import package UI;
atom A id "a"; atom B id "b";
conform A as Row id "a-row" {private state TitleA id "title-A" on A : string policy register default "title"; private state DetailA id "detail-A" on A : string policy register default "detail"; bind title.get to state TitleA.read; bind detail.get to state DetailA.read;}
conform B as Row id "b-row" {private state TitleB id "title-B" on B : string policy register default "title"; private state DetailB id "detail-B" on B : string policy register default "detail"; bind title.get to state TitleB.read; bind detail.get to state DetailB.read;}
conform A as RowRole id "a-role" {bind render to UI.${replacement};}
conform B as RowRole id "b-role" {bind render to UI.Second;}
}`,
"workspace.qx",
{ interfaces, interfaceClosure: [...interfaces.values()], packages: new Map([["UI", pkg]]) },
);
assert.ok(workspace.ok, JSON.stringify(workspace.diagnostics));
await completeWorkspace(workspace.workspace);
return { workspace: workspace.workspace, pkg: workspace.workspace.packageImports[0]!, authored, interfaces };
}
test("workspace expands distinct providers' private inputs without parent field declarations", async () => {
const { pkg } = await fixture();
const parent = pkg.checkedQueries!.find((query) => query.declaration.id === "react:input:library-card:data")!;
const acquisition = pkg.checkedQueries!.find(
(query) =>
query.declaration.id ===
(parent.declaration.placement?.kind === "local" ? parent.declaration.placement.sourceQueryId : ""),
)!;
assert.match(acquisition.document, /title/);
assert.match(acquisition.document, /detail/);
const plan = pkg.queryCompositions!.find((composition) => composition.id === "react:inputs:library-card")!;
assert.equal(plan.steps[0]!.query, parent.declaration.id);
assert.equal(Object.keys(parent.fragmentContracts ?? {}).length, 2);
assert(!pkg.queryCompositions!.some((composition) => composition.id.endsWith(":discovery")));
const original = pkg.checkedQueries!.find((query) => query.declaration.id === "LibraryData")!;
assert.deepEqual(parent.output, original.output, "Acquisition-only child fields are not parent requirements");
assert(Object.values(parent.fragmentContracts!).every((entry) => entry.mode === "reference"));
});
test("eager cycles and wrongly typed child bindings fail before runtime", async () => {
await assert.rejects(
fixture("First", (artifact) => {
artifact.modules
.find((module) => module.module === "first")!
.children.push({
name: "again",
subject: "row@1",
slot: "again",
root: { from: "input", path: ["subject"] },
role: { interface: "row-role@1", member: "render" },
});
}),
/Eager React preparation cycle/,
);
await assert.rejects(
fixture("First", (artifact) => {
artifact.modules.find((module) => module.module === "library-card")!.children[0]!.root = {
literal: "not a reference",
};
}),
/child root must be/,
);
});
test("workspace replacement changes acquisition requirements without changing the parent", async () => {
const { pkg } = await fixture("Second");
const parent = pkg.checkedQueries!.find((query) => query.declaration.id === "react:input:library-card:data")!;
const acquisition = pkg.checkedQueries!.find(
(query) =>
query.declaration.id ===
(parent.declaration.placement?.kind === "local" ? parent.declaration.placement.sourceQueryId : ""),
)!;
assert.doesNotMatch(acquisition.document, /\btitle\b/);
assert.match(acquisition.document, /detail/);
assert.equal(Object.keys(parent.fragmentContracts ?? {}).length, 1);
const previous = (await fixture()).pkg.checkedQueries!.find(
(query) => query.declaration.id === parent.declaration.id,
)!;
assert.deepEqual(parent.output, previous.output);
});