Implement implicit object references and capability query foundations
This commit is contained in:
@@ -38,11 +38,11 @@ test("controller discovery requires complete bounded accepted-state membership",
|
||||
.length,
|
||||
1,
|
||||
);
|
||||
assert.equal((await check("query Enabled {root {objects(all:64) {entries {node {enabled}}}}}")).length, 1);
|
||||
for (const document of [
|
||||
"query Enabled {root {objects(first:64) {entries {node {_camino {ref}}}}}}",
|
||||
"query Enabled {root {objects(all:65) {entries {node {_camino {ref}}}}}}",
|
||||
"query Enabled {root {devices:objects(all:64) {entries {node {_camino {ref}}}}}}",
|
||||
"query Enabled {root {objects(all:64) {entries {node {enabled}}}}}",
|
||||
"query Enabled($enabled:Boolean!) {root {objects(all:64,where:{enabled:{eq:$enabled}}) {entries {node {_camino {ref}}}}}}",
|
||||
])
|
||||
await assert.rejects(check(document), /registry|Controller/);
|
||||
|
||||
@@ -182,11 +182,36 @@ test("ordinary selections preserve aliases, fragment conditions and wire identit
|
||||
const node = checked.selection[0]!.selection[0]!.selection[0]!.selection[0]!;
|
||||
assert.deepEqual(
|
||||
node.selection.map((s) => s.key),
|
||||
["plain", "label", "due"],
|
||||
["plain", "label", "due", "_camino"],
|
||||
);
|
||||
assert.equal(node.selection[1]!.conditions.length, 1);
|
||||
assert.ok(JSON.stringify(checked.selection.map(querySelectionToWire)).includes("label"));
|
||||
});
|
||||
test("object references are unconditional, canonical and idempotent through fragments", async () => {
|
||||
const checked = await compile(
|
||||
`query Upcoming($show: Boolean!) {root {items(first: 3) {entries {node {...Row @include(if: $show)}}}}}`,
|
||||
`fragment Row on TaskFacts {label: title identity: _camino {alias: ref} _camino @include(if: $show) {ref}}`,
|
||||
);
|
||||
const root = checked.selection[0]!;
|
||||
const node = root.selection.find((s) => s.name === "items")!.selection[0]!.selection[0]!;
|
||||
for (const object of [root, node]) {
|
||||
const metadata = object.selection.filter((s) => s.key === "_camino");
|
||||
assert.equal(metadata.length, 1);
|
||||
assert.deepEqual(metadata[0]!.conditions, []);
|
||||
assert.deepEqual(
|
||||
metadata[0]!.selection.filter((s) => s.key === "ref").map((s) => s.conditions),
|
||||
[[]],
|
||||
);
|
||||
}
|
||||
assert.equal(node.selection.find((s) => s.key === "identity")!.conditions.length, 1);
|
||||
const recompiled = await compileQuery(
|
||||
{ ...checked.declaration, fragments: [] },
|
||||
[collection, facts],
|
||||
async () => checked.document,
|
||||
);
|
||||
assert.deepEqual(recompiled.selection, checked.selection);
|
||||
assert.deepEqual(recompiled.output, checked.output);
|
||||
});
|
||||
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(
|
||||
@@ -199,6 +224,68 @@ test("retired live syntax fails with migration guidance", async () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("implicit references reject aliases which occupy the canonical metadata path", async () => {
|
||||
for (const invalid of ["_camino: title", "_camino {ref: __typename}"])
|
||||
await assert.rejects(compile(`query Upcoming {root {items(first: 3) {entries {node {...Row}}}}}`, `fragment Row on TaskFacts {${invalid}}`), /QUERY_VALIDATION|Introspection/);
|
||||
});
|
||||
|
||||
test("implicit reference merges with conditional aggregate metadata", async () => {
|
||||
const checked = await compileQuery({ ...fixture(""), fragments: [] }, [collection, facts], async () =>
|
||||
`query Upcoming($show: Boolean!) {root {_camino @include(if: $show) {relations {items {aggregate {count}}}}}}`);
|
||||
const metadata = checked.selection[0]!.selection.filter((field) => field.key === "_camino");
|
||||
assert.equal(metadata.length, 1);
|
||||
assert.deepEqual(metadata[0]!.conditions, []);
|
||||
assert.equal(metadata[0]!.selection.find((field) => field.name === "relations")!.conditions.length, 1);
|
||||
assert.deepEqual(metadata[0]!.selection.find((field) => field.name === "ref")!.conditions, []);
|
||||
assert.equal(metadata[0]!.selection.find((field) => field.name === "relations")!.selection[0]!.selection[0]!.selection.some((field) => field.name === "_camino"), false);
|
||||
});
|
||||
|
||||
test("qualified interface predicates and guaranteed inline projections retain nominal reads", async () => {
|
||||
const owner = iface(
|
||||
`interface Owner id "owner" revision "owner@1" { queryable value name id "name" : string {get id "name:get";} }`,
|
||||
);
|
||||
const owned = iface(
|
||||
`import interface Owner;
|
||||
interface Owned id "owned" revision "owned@1" {
|
||||
queryable relation owner id "owner" : optional-one interface Owner {resolve id "owner:resolve";}
|
||||
queryable value label id "owned:label" : string {get id "owned:label:get";}
|
||||
}`,
|
||||
[owner],
|
||||
);
|
||||
const item = iface(
|
||||
`import interface Owned;
|
||||
interface Item id "item" revision "item@1" requires Owned {
|
||||
queryable value label id "item:label" : string {get id "item:label:get";}
|
||||
}`,
|
||||
[owned, owner],
|
||||
);
|
||||
const declaration = {
|
||||
...fixture(""),
|
||||
root: { kind: "workspace-interface" as const, interface: item.revisionId },
|
||||
fragments: [],
|
||||
};
|
||||
const document = `query Upcoming($owner: QxRef_Owner!) {root {objects(all: 10, where: {_camino: {as: {Owned: {owner: {eq: $owner}}}}}) {entries {node {label ... on Owned {owner {name} ownerLabel: label}}}}}}`;
|
||||
const checked = await compileQuery(declaration, [item, owned, owner], async () => document);
|
||||
assert.ok(
|
||||
checked.effects.some(
|
||||
(effect) =>
|
||||
effect.interfaceRevisionId === owned.revisionId &&
|
||||
effect.memberId === "owner" &&
|
||||
effect.uses.includes("predicate"),
|
||||
),
|
||||
);
|
||||
const objects = checked.selection[0]!.selection[0]!;
|
||||
assert.equal(objects.predicate?.kind, "and");
|
||||
assert.ok(JSON.stringify(objects.predicate).includes('"kind":"as"'));
|
||||
const node = objects.selection[0]!.selection[0]!;
|
||||
assert.equal(node.selection.find((s) => s.key === "ownerLabel")!.interfaceRevisionId, owned.revisionId);
|
||||
assert.equal(node.selection.filter((s) => s.key === "_camino").length, 1);
|
||||
await assert.rejects(
|
||||
compileQuery(declaration, [item, owned, owner], async () => document.replace("ownerLabel: label", "label")),
|
||||
/QUERY_PROJECTION_CONFLICT|QUERY_VALIDATION/,
|
||||
);
|
||||
});
|
||||
|
||||
test("query dependency ports resolve exact exports without declaration ordering constraints", () => {
|
||||
const result = compileCapabilityResourceSource(
|
||||
`import interface Tasks;
|
||||
|
||||
@@ -61,6 +61,17 @@ test("record/list query values preserve nested projections and leaf predicates",
|
||||
}
|
||||
}
|
||||
const raw = JSON.stringify(checked.output);
|
||||
const node = extent.selection[0]!.selection[0]!;
|
||||
assert.equal(node.selection.filter((field) => field.key === "_camino").length, 1);
|
||||
const assertValuesOnly = (fields: typeof node.selection): void => {
|
||||
for (const field of fields) {
|
||||
assert.notEqual(field.name, "_camino", "Structured values do not acquire graph identity");
|
||||
assertValuesOnly(field.selection);
|
||||
}
|
||||
};
|
||||
assertValuesOnly(node.selection.filter((field) => field.key !== "_camino"));
|
||||
assert.equal(checked.selection[0]!.selection.some((field) => field.key === "_camino"), false);
|
||||
assert.equal(extent.selection.some((field) => field.key === "_camino"), false);
|
||||
assert.match(raw, /"begin"/);
|
||||
assert.match(raw, /"tags":\{"kind":"list"/);
|
||||
assert.equal(checked.effects.filter((e) => e.memberId === "range").length, 1);
|
||||
|
||||
Reference in New Issue
Block a user