Skip to content

Commit 95d2df1

Browse files
committed
test(objectql): a list-valued flag at the aggregation positions is the shape face's refusal (#21448)
Claude-Session: https://claude.ai/code/session_01DiCSbmJrkzNhuEAier4VoJ Co-authored-by: Claude <noreply@anthropic.com>
1 parent b09820d commit 95d2df1

1 file changed

Lines changed: 22 additions & 3 deletions

File tree

‎packages/objectql/src/engine-aggregate-flag-comparand-refusal.test.ts‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,9 @@ const NON_BOOLEANS: ReadonlyArray<readonly [string, unknown, string]> = [
154154
['"false" (truthy)', 'false', 'string ("false")'],
155155
['0', 0, 'number (0)'],
156156
['null', null, 'null (null)'],
157-
// Not one of the card's five, but reaching the same gate: no earlier door
158-
// refuses a list here, so it met the old `!!target` read like any other value.
159-
['[true] (a list)', [true], 'array ([true])'],
157+
// [#21448] `[true] (a list)` stood here, reaching this gate because no
158+
// earlier door refused a list at a flag. The shared comparand-shape face
159+
// does now, one door earlier, in its own words — pinned in the block below.
160160
];
161161

162162
describe('[#20981] a non-boolean $exists / $null — refused before any driver read, on both positions', () => {
@@ -184,6 +184,25 @@ describe('[#20981] a non-boolean $exists / $null — refused before any driver r
184184
}
185185
}
186186

187+
it('[#21448] a LIST flag is the shared comparand-shape face\'s refusal, one door earlier, at both positions — no read', async () => {
188+
for (const op of OPS) {
189+
const sentence = `Operator "${op}" on field "name" requires a single comparable value, but received an array ([true])`;
190+
const { engine, reads } = await makeEngine('rows', ROWS);
191+
const filtered = await refusalOf(() => engine.aggregate(OBJECT, filterQuery({ name: { [op]: [true] } })));
192+
expect({ code: filtered.code, status: filtered.status }, op).toEqual({ code: 'INVALID_FILTER', status: 400 });
193+
expect(filtered.message, op).toContain(`${sentence} at aggregations[1].filter.name.${op}.`);
194+
expect(filtered.message, op).not.toContain('requires a boolean comparand');
195+
expect(reads, `${op}: no row was read`).toEqual({ aggregate: 0, find: 0 });
196+
for (const path of ['native', 'rows'] as const) {
197+
const grouped = await makeEngine(path, ROWS);
198+
const having = await refusalOf(() => grouped.engine.aggregate(OBJECT, havingQuery(path, { name: { [op]: [true] } })));
199+
expect({ code: having.code, status: having.status }, `${op} ${path}`).toEqual({ code: 'INVALID_FILTER', status: 400 });
200+
expect(having.message, `${op} ${path}`).toContain(`${sentence} at having.name.${op}.`);
201+
expect(grouped.reads, `${op} ${path}: no row was read`).toEqual({ aggregate: 0, find: 0 });
202+
}
203+
}
204+
});
205+
187206
// Where the flag sits must not change the verdict: the walk is row-independent,
188207
// so a branch the per-row walk would short-circuit past is judged too.
189208
const POSITIONS: ReadonlyArray<readonly [string, (flag: Record<string, unknown>) => Record<string, unknown>, string]> = [

0 commit comments

Comments
 (0)