Standardize Camino query references and retire live field sidecars
This commit is contained in:
+14
-32
@@ -4,7 +4,6 @@ import { compileCapabilityResourceSource } from "../src/capability-language/inde
|
||||
import { compileQuery } from "../src/query/compile.js";
|
||||
import { QueryCompileError } from "../src/query/types.js";
|
||||
import { checkQueryTemplate } from "../src/query/templates.js";
|
||||
import { queryPresentation } from "../src/query/presentation.js";
|
||||
import { querySelectionToWire } from "../src/query/proto.js";
|
||||
import type { InterfaceRevision } from "../src/capability-model/types.js";
|
||||
|
||||
@@ -149,9 +148,9 @@ test("workspace roots expose a bounded interface extent, not a root object or wr
|
||||
async () => `query Upcoming {
|
||||
root {
|
||||
objects(first: 3, where: {done: {eq: false}}, orderBy: {title: ASC}) {
|
||||
entries {key node {_qx {ref} title @live}} pageInfo {hasNextPage endCursor}
|
||||
entries {key node {_camino {ref} title}} pageInfo {hasNextPage endCursor}
|
||||
}
|
||||
_qx {relations {objects {aggregate {count sum {due}}}}}
|
||||
_camino {relations {objects {aggregate {count sum {due}}}}}
|
||||
}
|
||||
}`,
|
||||
);
|
||||
@@ -159,7 +158,7 @@ test("workspace roots expose a bounded interface extent, not a root object or wr
|
||||
assert.ok(!checked.effects.some((effect) => effect.memberId.includes("workspace-objects")));
|
||||
assert.ok(JSON.stringify(checked.selection).includes("qx-internal-workspace-objects"));
|
||||
await assert.rejects(
|
||||
compileQuery(declaration, [facts], async () => `query Upcoming {root {_qx {ref}}}`),
|
||||
compileQuery(declaration, [facts], async () => `query Upcoming {root {_camino {ref}}}`),
|
||||
/QUERY_VALIDATION/,
|
||||
);
|
||||
await assert.rejects(
|
||||
@@ -169,51 +168,34 @@ test("workspace roots expose a bounded interface extent, not a root object or wr
|
||||
});
|
||||
const document = `query Upcoming($first: Int!, $before: Int64!) {
|
||||
root { items(first: $first, where: {done: {eq: false}, due: {lt: $before}}, orderBy: [{due: ASC}]) {
|
||||
entries { key node { _qx { ref } ...Row } } pageInfo { hasNextPage endCursor }
|
||||
entries { key node { _camino { ref } ...Row } } pageInfo { hasNextPage endCursor }
|
||||
} }
|
||||
}`;
|
||||
const compile = (query = document, row = "fragment Row on TaskFacts { title due }", clauses = "") =>
|
||||
compileQuery(fixture(clauses), [collection, facts], async (name) => (name.endsWith("row.graphql") ? row : query));
|
||||
|
||||
test("live selections preserve aliases, nullable values, fragment conditions and wire identity", async () => {
|
||||
test("ordinary selections preserve aliases, fragment conditions and wire identity", async () => {
|
||||
const checked = await compile(
|
||||
`query Upcoming($show: Boolean!) {root {items(first: 3) {entries {node {plain: title ...Row @include(if: $show)}}}}}`,
|
||||
`fragment Row on TaskFacts {label: title @live due @live}`,
|
||||
`fragment Row on TaskFacts {label: title due}`,
|
||||
);
|
||||
const fields = queryPresentation(checked, [collection, facts]);
|
||||
const node = checked.selection[0]!.selection[0]!.selection[0]!.selection[0]!;
|
||||
assert.deepEqual(
|
||||
fields.map((f) => f.path),
|
||||
[
|
||||
["root", "items", "entries", "*", "node", "label"],
|
||||
["root", "items", "entries", "*", "node", "due"],
|
||||
],
|
||||
node.selection.map((s) => s.key),
|
||||
["plain", "label", "due"],
|
||||
);
|
||||
assert.equal(fields[0]!.getOperationId, "title:get");
|
||||
assert.equal(fields[0]!.setOperationId, undefined);
|
||||
assert.equal(fields[1]!.valueType.kind, "optional");
|
||||
assert.equal(fields[0]!.conditions.length, 1);
|
||||
assert.ok(JSON.stringify(checked.selection.map(querySelectionToWire)).includes(fields[0]!.selectionId));
|
||||
const defaults = await compile(
|
||||
`query Upcoming($show: Boolean! = true) {root {items(first: 3) {entries {node {...Row @include(if: $show)}}}}}`,
|
||||
"fragment Row on TaskFacts {title @live}",
|
||||
);
|
||||
assert.equal(queryPresentation(defaults, [collection, facts])[0]!.conditions[0]!.defaultValue, true);
|
||||
assert.equal(node.selection[1]!.conditions.length, 1);
|
||||
assert.ok(JSON.stringify(checked.selection.map(querySelectionToWire)).includes("label"));
|
||||
});
|
||||
|
||||
test("live selections reject RPC getters, synthetic selections and conflicting presentations", async () => {
|
||||
for (const [selected, code] of [
|
||||
["score @live", "QUERY_LIVE_UNSUPPORTED"],
|
||||
["_qx @live {ref}", "QUERY_LIVE_UNSUPPORTED"],
|
||||
["title @live title", "QUERY_LIVE_CONFLICT"],
|
||||
["title @live(unchecked: true)", "QUERY_VALIDATION"],
|
||||
])
|
||||
test("retired live syntax fails with migration guidance", async () => {
|
||||
for (const selected of ["score @live", "_camino @live {ref}", "title @live title", "title @live(unchecked: true)"])
|
||||
await assert.rejects(
|
||||
compile(
|
||||
`query Upcoming {root {items(first: 3) {entries {node {...Row}}}}}`,
|
||||
`fragment Row on TaskFacts {${selected}}`,
|
||||
'allow TaskFacts.score select "bounded";',
|
||||
),
|
||||
(error: unknown) => error instanceof QueryCompileError && error.code === code,
|
||||
(error: unknown) => error instanceof QueryCompileError && error.code === "QUERY_RETIRED_DIRECTIVE",
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user