Skip to content

Commit 2e6f6c2

Browse files
committed
wip: fixture triage for canonical spelling
1 parent 18c787d commit 2e6f6c2

4 files changed

Lines changed: 37 additions & 18 deletions

File tree

packages/rest/src/public-form-lookup-picker.test.ts

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -203,16 +203,27 @@ describe('#7467 a spec-valid stored form carrying a publicPicker reaches the loo
203203
// the declared object override, the declared cap, the declared filter
204204
// rows ahead of the visitor's search predicate, id + displayFields
205205
// projection, offset pinned to 0 (no anonymous pagination).
206+
//
207+
// [#16337] The KEYS are the canonical QueryAST ones (`where` / `fields`
208+
// / `orderBy`); until then the route spelled them `filters` / `select` /
209+
// `sort`, wire aliases the normalizer folds onto exactly these. The
210+
// VALUES are byte-identical across that rewrite, which is the point —
211+
// and note what `where` carries: `ViewFilterRule` rows, the dialect
212+
// `FormFieldPublicPickerSchema.filter` declares, NOT a
213+
// `FilterCondition`. `findData` is stubbed in this suite, so it never
214+
// meets the ingress's verdict on that value; the real normalizer
215+
// refuses it. Filed separately — ⛔ do not "repair" it by editing this
216+
// expectation.
206217
expect(findData).toHaveBeenCalledTimes(1);
207218
const call = findData.mock.calls[0][0];
208219
expect(call.object).toBe('sys_user');
209220
expect(call.query.limit).toBe(10);
210221
expect(call.query.offset).toBe(0);
211-
expect(call.query.select).toEqual(['id', 'name', 'email']);
222+
expect(call.query.fields).toEqual(['id', 'name', 'email']);
212223
// [#7485] Ordering is fixed, not authorable: first display field,
213224
// ascending. The route's `picker.sort ??` read is retired.
214-
expect(call.query.sort).toEqual([{ field: 'name', order: 'asc' }]);
215-
expect(call.query.filters).toEqual([
225+
expect(call.query.orderBy).toEqual([{ field: 'name', order: 'asc' }]);
226+
expect(call.query.where).toEqual([
216227
{ field: 'is_active', operator: 'equals', value: true },
217228
{ field: 'name', operator: 'contains', value: 'ad' },
218229
]);
@@ -299,7 +310,7 @@ describe('#7485 publicPicker.sort is retired — not declarable, and not read',
299310
// The stored `{ field: 'email', order: 'desc' }` reaches `findData`
300311
// nowhere: the fixed default is the only ordering the route composes.
301312
expect(findData).toHaveBeenCalledTimes(1);
302-
expect(findData.mock.calls[0][0].query.sort).toEqual([{ field: 'name', order: 'asc' }]);
313+
expect(findData.mock.calls[0][0].query.orderBy).toEqual([{ field: 'name', order: 'asc' }]);
303314
});
304315

305316
it('…and the fixed sort tracks displayFields[0], including the no-displayFields default', async () => {
@@ -310,15 +321,15 @@ describe('#7485 publicPicker.sort is retired — not declarable, and not read',
310321
const stored = await persistedBody(studioForm([{ field: 'owner', publicPicker: { object: 'sys_user' } }]));
311322
const { findData, lookup } = routesOver(stored, []);
312323
await lookup.handler({ params: { slug: 'contact', field: 'owner' }, query: {} } as any, mockRes());
313-
expect(findData.mock.calls[0][0].query.sort).toEqual([{ field: 'name', order: 'asc' }]);
324+
expect(findData.mock.calls[0][0].query.orderBy).toEqual([{ field: 'name', order: 'asc' }]);
314325

315326
const stored2 = await persistedBody(studioForm([{
316327
field: 'owner',
317328
publicPicker: { displayFields: ['email', 'name'], object: 'sys_user' },
318329
}]));
319330
const second = routesOver(stored2, []);
320331
await second.lookup.handler({ params: { slug: 'contact', field: 'owner' }, query: {} } as any, mockRes());
321-
expect(second.findData.mock.calls[0][0].query.sort).toEqual([{ field: 'email', order: 'asc' }]);
332+
expect(second.findData.mock.calls[0][0].query.orderBy).toEqual([{ field: 'email', order: 'asc' }]);
322333
});
323334
});
324335

packages/rest/src/rest-server-canonical-query-ast.test.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,8 +132,10 @@ describe('[#16337] §1 no server-built `query` slot in rest-server.ts is erased
132132
).toEqual([]);
133133
});
134134

135-
it('no `query` slot carries an `as` cast', () => {
136-
const cast = querySlots().filter(({ line }) => /\bas\s+(any|unknown|FindDataRequest)\b/.test(slotBody(line)));
135+
it('no server-built `query` literal carries an `as` cast', () => {
136+
const cast = querySlots()
137+
.filter(({ text }) => text.trim() !== CALLER_SUPPLIED_SLOT)
138+
.filter(({ line }) => /\bas\s+(any|unknown|FindDataRequest)\b/.test(slotBody(line)));
137139
expect(cast.map((c) => `line ${c.line}`)).toEqual([]);
138140
});
139141

packages/rest/src/rest-server-closed-query-params.test.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -303,18 +303,20 @@ describe('#7606 §2 — GET /data/:object/export', () => {
303303
// is what makes this a preservation pin: `limit` silently dropped
304304
// exports the whole table with a perfectly ordinary 200.
305305

306-
// `limit` is the binding cap here (25 < 100), so $top proves IT arrived.
306+
// `limit` is the binding cap here (25 < 100), so the query's `limit`
307+
// proves IT arrived. (Spelled `$top` until #16337 canonicalised the
308+
// door's literal; the assertion is unchanged in what it measures.)
307309
const capped = boot();
308310
const byLimit = await capped.exportRows({ format: 'csv', limit: '25', page: '100' });
309311
expect(byLimit.status).toBe(200);
310-
expect((capped.findData.mock.calls[0][0] as any)?.query?.$top).toBe(25);
312+
expect((capped.findData.mock.calls[0][0] as any)?.query?.limit).toBe(25);
311313

312-
// `page` is the binding cap here (50 < 200), so $top proves IT arrived
313-
// — a default chunk would have read 500.
314+
// `page` is the binding cap here (50 < 200), so the query's `limit`
315+
// proves IT arrived — a default chunk would have read 500.
314316
const chunked = boot();
315317
const byPage = await chunked.exportRows({ format: 'csv', limit: '200', page: '50' });
316318
expect(byPage.status).toBe(200);
317-
expect((chunked.findData.mock.calls[0][0] as any)?.query?.$top).toBe(50);
319+
expect((chunked.findData.mock.calls[0][0] as any)?.query?.limit).toBe(50);
318320
});
319321

320322
it('PRESERVATION: the row-selection axes still narrow the export', async () => {

packages/rest/src/rest.test.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,8 +1119,8 @@ describe('RestServer', () => {
11191119

11201120
// Always return full chunks so the loop is bounded only by `limit`.
11211121
protocol.findData.mockImplementation(async ({ query }: any) => {
1122-
const take = query?.$top ?? 0;
1123-
return { data: Array.from({ length: take }, (_v, i) => ({ id: String((query?.$skip ?? 0) + i) })) };
1122+
const take = query?.limit ?? 0;
1123+
return { data: Array.from({ length: take }, (_v, i) => ({ id: String((query?.offset ?? 0) + i) })) };
11241124
});
11251125

11261126
const { res, chunks } = makeRes();
@@ -1214,7 +1214,7 @@ describe('RestServer', () => {
12141214
expect(lines[0]).toBe('1,写代码,是,高,2026-06-30,张三');
12151215
});
12161216

1217-
it('injects $expand for reference fields into the findData query', async () => {
1217+
it('injects expand for reference fields into the findData query', async () => {
12181218
const p = protocolWithSchema([RAW_TASK_ROW]);
12191219
const rest = new RestServer(server as any, p as any, ANON_API as any);
12201220
(rest as any).resolveExecCtx = async () => ({ userId: 'test-user' });
@@ -1226,7 +1226,11 @@ describe('RestServer', () => {
12261226

12271227
expect(p.findData).toHaveBeenCalled();
12281228
const firstQuery = p.findData.mock.calls[0][0].query;
1229-
expect(firstQuery.$expand).toBe('owner');
1229+
// [#16337] The door builds the canonical relation MAP the QueryAST
1230+
// declares. It used to build the comma list `$expand` accepted, which
1231+
// the normalizer lowered to exactly this map — same value, one fewer
1232+
// dialect between the door and the engine.
1233+
expect(firstQuery.expand).toEqual({ owner: { object: 'owner' } });
12301234
});
12311235

12321236
it('formats values readably in JSON, leaving unknown keys untouched', async () => {
@@ -1326,7 +1330,7 @@ describe('RestServer', () => {
13261330
p.getMetaItem = vi.fn().mockResolvedValue({ type: 'object', name: 'task', item: NAMED_SCHEMA });
13271331
// First page returns one row, subsequent pages are empty (ends the stream).
13281332
p.findData = vi.fn(async ({ query }: any) =>
1329-
(query?.$skip ?? 0) === 0 ? { data: [{ id: '1', title: 'x', done: true }] } : { data: [] },
1333+
(query?.offset ?? 0) === 0 ? { data: [{ id: '1', title: 'x', done: true }] } : { data: [] },
13301334
);
13311335

13321336
// i18nServiceProvider is the 14th constructor arg (after server, protocol,

0 commit comments

Comments
 (0)