@zenstackhq/client-helpers 3.1.1 → 3.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +12 -9
- package/.turbo/turbo-build.log +0 -24
- package/eslint.config.js +0 -4
- package/src/constants.ts +0 -4
- package/src/fetch.ts +0 -107
- package/src/index.ts +0 -9
- package/src/invalidation.ts +0 -89
- package/src/logging.ts +0 -15
- package/src/mutator.ts +0 -449
- package/src/nested-read-visitor.ts +0 -68
- package/src/nested-write-visitor.ts +0 -359
- package/src/optimistic.ts +0 -139
- package/src/query-analysis.ts +0 -111
- package/src/types.ts +0 -82
- package/test/fetch.test.ts +0 -423
- package/test/invalidation.test.ts +0 -602
- package/test/mutator.test.ts +0 -1533
- package/test/nested-read-visitor.test.ts +0 -949
- package/test/nested-write-visitor.test.ts +0 -1244
- package/test/optimistic.test.ts +0 -743
- package/test/query-analysis.test.ts +0 -1399
- package/test/test-helpers.ts +0 -37
- package/tsconfig.json +0 -4
- package/tsconfig.test.json +0 -7
- package/tsup.config.ts +0 -14
- package/vitest.config.ts +0 -4
|
@@ -1,949 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from 'vitest';
|
|
2
|
-
import { NestedReadVisitor, type NestedReadVisitorCallback } from '../src/nested-read-visitor';
|
|
3
|
-
import { createField, createRelationField, createSchema } from './test-helpers';
|
|
4
|
-
|
|
5
|
-
describe('NestedReadVisitor tests', () => {
|
|
6
|
-
describe('basic visiting', () => {
|
|
7
|
-
it('visits simple model without select or include', () => {
|
|
8
|
-
const schema = createSchema({
|
|
9
|
-
User: {
|
|
10
|
-
name: 'User',
|
|
11
|
-
fields: {
|
|
12
|
-
id: createField('id', 'String'),
|
|
13
|
-
name: createField('name', 'String'),
|
|
14
|
-
},
|
|
15
|
-
uniqueFields: {},
|
|
16
|
-
idFields: ['id'],
|
|
17
|
-
},
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const callback = vi.fn();
|
|
21
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
22
|
-
|
|
23
|
-
visitor.visit('User', { where: { id: '1' } });
|
|
24
|
-
|
|
25
|
-
// Should be called once for the root with undefined field
|
|
26
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
27
|
-
expect(callback).toHaveBeenCalledWith('User', undefined, undefined, { where: { id: '1' } });
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
it('handles null or undefined args', () => {
|
|
31
|
-
const schema = createSchema({
|
|
32
|
-
User: {
|
|
33
|
-
name: 'User',
|
|
34
|
-
fields: {},
|
|
35
|
-
uniqueFields: {},
|
|
36
|
-
idFields: ['id'],
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
const callback = vi.fn();
|
|
41
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
42
|
-
|
|
43
|
-
visitor.visit('User', null);
|
|
44
|
-
expect(callback).toHaveBeenCalledWith('User', undefined, undefined, null);
|
|
45
|
-
|
|
46
|
-
visitor.visit('User', undefined);
|
|
47
|
-
expect(callback).toHaveBeenCalledWith('User', undefined, undefined, undefined);
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
describe('include visits', () => {
|
|
52
|
-
it('visits fields with include', () => {
|
|
53
|
-
const schema = createSchema({
|
|
54
|
-
User: {
|
|
55
|
-
name: 'User',
|
|
56
|
-
fields: {
|
|
57
|
-
id: createField('id', 'String'),
|
|
58
|
-
posts: createRelationField('posts', 'Post'),
|
|
59
|
-
},
|
|
60
|
-
uniqueFields: {},
|
|
61
|
-
idFields: ['id'],
|
|
62
|
-
},
|
|
63
|
-
Post: {
|
|
64
|
-
name: 'Post',
|
|
65
|
-
fields: {
|
|
66
|
-
id: createField('id', 'String'),
|
|
67
|
-
title: createField('title', 'String'),
|
|
68
|
-
},
|
|
69
|
-
uniqueFields: {},
|
|
70
|
-
idFields: ['id'],
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const callback = vi.fn();
|
|
75
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
76
|
-
|
|
77
|
-
visitor.visit('User', {
|
|
78
|
-
include: {
|
|
79
|
-
posts: true,
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
expect(callback).toHaveBeenCalledTimes(2);
|
|
84
|
-
expect(callback).toHaveBeenNthCalledWith(1, 'User', undefined, undefined, {
|
|
85
|
-
include: { posts: true },
|
|
86
|
-
});
|
|
87
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
88
|
-
2,
|
|
89
|
-
'Post',
|
|
90
|
-
expect.objectContaining({ name: 'posts' }),
|
|
91
|
-
'include',
|
|
92
|
-
true,
|
|
93
|
-
);
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
it('visits nested includes', () => {
|
|
97
|
-
const schema = createSchema({
|
|
98
|
-
User: {
|
|
99
|
-
name: 'User',
|
|
100
|
-
fields: {
|
|
101
|
-
id: createField('id', 'String'),
|
|
102
|
-
posts: createRelationField('posts', 'Post'),
|
|
103
|
-
},
|
|
104
|
-
uniqueFields: {},
|
|
105
|
-
idFields: ['id'],
|
|
106
|
-
},
|
|
107
|
-
Post: {
|
|
108
|
-
name: 'Post',
|
|
109
|
-
fields: {
|
|
110
|
-
id: createField('id', 'String'),
|
|
111
|
-
title: createField('title', 'String'),
|
|
112
|
-
comments: createRelationField('comments', 'Comment'),
|
|
113
|
-
},
|
|
114
|
-
uniqueFields: {},
|
|
115
|
-
idFields: ['id'],
|
|
116
|
-
},
|
|
117
|
-
Comment: {
|
|
118
|
-
name: 'Comment',
|
|
119
|
-
fields: {
|
|
120
|
-
id: createField('id', 'String'),
|
|
121
|
-
text: createField('text', 'String'),
|
|
122
|
-
},
|
|
123
|
-
uniqueFields: {},
|
|
124
|
-
idFields: ['id'],
|
|
125
|
-
},
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
const callback = vi.fn();
|
|
129
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
130
|
-
|
|
131
|
-
visitor.visit('User', {
|
|
132
|
-
include: {
|
|
133
|
-
posts: {
|
|
134
|
-
include: {
|
|
135
|
-
comments: true,
|
|
136
|
-
},
|
|
137
|
-
},
|
|
138
|
-
},
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
expect(callback).toHaveBeenCalledTimes(3);
|
|
142
|
-
expect(callback).toHaveBeenNthCalledWith(1, 'User', undefined, undefined, expect.any(Object));
|
|
143
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
144
|
-
2,
|
|
145
|
-
'Post',
|
|
146
|
-
expect.objectContaining({ name: 'posts' }),
|
|
147
|
-
'include',
|
|
148
|
-
expect.any(Object),
|
|
149
|
-
);
|
|
150
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
151
|
-
3,
|
|
152
|
-
'Comment',
|
|
153
|
-
expect.objectContaining({ name: 'comments' }),
|
|
154
|
-
'include',
|
|
155
|
-
true,
|
|
156
|
-
);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('visits multiple includes at same level', () => {
|
|
160
|
-
const schema = createSchema({
|
|
161
|
-
User: {
|
|
162
|
-
name: 'User',
|
|
163
|
-
fields: {
|
|
164
|
-
id: createField('id', 'String'),
|
|
165
|
-
posts: createRelationField('posts', 'Post'),
|
|
166
|
-
profile: createRelationField('profile', 'Profile'),
|
|
167
|
-
},
|
|
168
|
-
uniqueFields: {},
|
|
169
|
-
idFields: ['id'],
|
|
170
|
-
},
|
|
171
|
-
Post: {
|
|
172
|
-
name: 'Post',
|
|
173
|
-
fields: {
|
|
174
|
-
id: createField('id', 'String'),
|
|
175
|
-
},
|
|
176
|
-
uniqueFields: {},
|
|
177
|
-
idFields: ['id'],
|
|
178
|
-
},
|
|
179
|
-
Profile: {
|
|
180
|
-
name: 'Profile',
|
|
181
|
-
fields: {
|
|
182
|
-
id: createField('id', 'String'),
|
|
183
|
-
},
|
|
184
|
-
uniqueFields: {},
|
|
185
|
-
idFields: ['id'],
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const callback = vi.fn();
|
|
190
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
191
|
-
|
|
192
|
-
visitor.visit('User', {
|
|
193
|
-
include: {
|
|
194
|
-
posts: true,
|
|
195
|
-
profile: true,
|
|
196
|
-
},
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
expect(callback).toHaveBeenCalledTimes(3);
|
|
200
|
-
expect(callback).toHaveBeenNthCalledWith(1, 'User', undefined, undefined, expect.any(Object));
|
|
201
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
202
|
-
2,
|
|
203
|
-
'Post',
|
|
204
|
-
expect.objectContaining({ name: 'posts' }),
|
|
205
|
-
'include',
|
|
206
|
-
true,
|
|
207
|
-
);
|
|
208
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
209
|
-
3,
|
|
210
|
-
'Profile',
|
|
211
|
-
expect.objectContaining({ name: 'profile' }),
|
|
212
|
-
'include',
|
|
213
|
-
true,
|
|
214
|
-
);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe('select visits', () => {
|
|
219
|
-
it('visits fields with select', () => {
|
|
220
|
-
const schema = createSchema({
|
|
221
|
-
User: {
|
|
222
|
-
name: 'User',
|
|
223
|
-
fields: {
|
|
224
|
-
id: createField('id', 'String'),
|
|
225
|
-
posts: createRelationField('posts', 'Post'),
|
|
226
|
-
},
|
|
227
|
-
uniqueFields: {},
|
|
228
|
-
idFields: ['id'],
|
|
229
|
-
},
|
|
230
|
-
Post: {
|
|
231
|
-
name: 'Post',
|
|
232
|
-
fields: {
|
|
233
|
-
id: createField('id', 'String'),
|
|
234
|
-
title: createField('title', 'String'),
|
|
235
|
-
},
|
|
236
|
-
uniqueFields: {},
|
|
237
|
-
idFields: ['id'],
|
|
238
|
-
},
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
const callback = vi.fn();
|
|
242
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
243
|
-
|
|
244
|
-
visitor.visit('User', {
|
|
245
|
-
select: {
|
|
246
|
-
posts: true,
|
|
247
|
-
},
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
expect(callback).toHaveBeenCalledTimes(2);
|
|
251
|
-
expect(callback).toHaveBeenNthCalledWith(1, 'User', undefined, undefined, {
|
|
252
|
-
select: { posts: true },
|
|
253
|
-
});
|
|
254
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
255
|
-
2,
|
|
256
|
-
'Post',
|
|
257
|
-
expect.objectContaining({ name: 'posts' }),
|
|
258
|
-
'select',
|
|
259
|
-
true,
|
|
260
|
-
);
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
it('visits nested selects', () => {
|
|
264
|
-
const schema = createSchema({
|
|
265
|
-
User: {
|
|
266
|
-
name: 'User',
|
|
267
|
-
fields: {
|
|
268
|
-
id: createField('id', 'String'),
|
|
269
|
-
posts: createRelationField('posts', 'Post'),
|
|
270
|
-
},
|
|
271
|
-
uniqueFields: {},
|
|
272
|
-
idFields: ['id'],
|
|
273
|
-
},
|
|
274
|
-
Post: {
|
|
275
|
-
name: 'Post',
|
|
276
|
-
fields: {
|
|
277
|
-
id: createField('id', 'String'),
|
|
278
|
-
title: createField('title', 'String'),
|
|
279
|
-
comments: createRelationField('comments', 'Comment'),
|
|
280
|
-
},
|
|
281
|
-
uniqueFields: {},
|
|
282
|
-
idFields: ['id'],
|
|
283
|
-
},
|
|
284
|
-
Comment: {
|
|
285
|
-
name: 'Comment',
|
|
286
|
-
fields: {
|
|
287
|
-
id: createField('id', 'String'),
|
|
288
|
-
text: createField('text', 'String'),
|
|
289
|
-
},
|
|
290
|
-
uniqueFields: {},
|
|
291
|
-
idFields: ['id'],
|
|
292
|
-
},
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
const callback = vi.fn();
|
|
296
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
297
|
-
|
|
298
|
-
visitor.visit('User', {
|
|
299
|
-
select: {
|
|
300
|
-
posts: {
|
|
301
|
-
select: {
|
|
302
|
-
comments: true,
|
|
303
|
-
},
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
expect(callback).toHaveBeenCalledTimes(3);
|
|
309
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
310
|
-
3,
|
|
311
|
-
'Comment',
|
|
312
|
-
expect.objectContaining({ name: 'comments' }),
|
|
313
|
-
'select',
|
|
314
|
-
true,
|
|
315
|
-
);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
it('visits scalar fields in select', () => {
|
|
319
|
-
const schema = createSchema({
|
|
320
|
-
User: {
|
|
321
|
-
name: 'User',
|
|
322
|
-
fields: {
|
|
323
|
-
id: createField('id', 'String'),
|
|
324
|
-
name: createField('name', 'String'),
|
|
325
|
-
email: createField('email', 'String'),
|
|
326
|
-
},
|
|
327
|
-
uniqueFields: {},
|
|
328
|
-
idFields: ['id'],
|
|
329
|
-
},
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
const callback = vi.fn();
|
|
333
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
334
|
-
|
|
335
|
-
visitor.visit('User', {
|
|
336
|
-
select: {
|
|
337
|
-
id: true,
|
|
338
|
-
name: true,
|
|
339
|
-
},
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
expect(callback).toHaveBeenCalledTimes(3);
|
|
343
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
344
|
-
2,
|
|
345
|
-
'String',
|
|
346
|
-
expect.objectContaining({ name: 'id' }),
|
|
347
|
-
'select',
|
|
348
|
-
true,
|
|
349
|
-
);
|
|
350
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
351
|
-
3,
|
|
352
|
-
'String',
|
|
353
|
-
expect.objectContaining({ name: 'name' }),
|
|
354
|
-
'select',
|
|
355
|
-
true,
|
|
356
|
-
);
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
|
|
360
|
-
describe('_count handling', () => {
|
|
361
|
-
it('visits _count field', () => {
|
|
362
|
-
const schema = createSchema({
|
|
363
|
-
User: {
|
|
364
|
-
name: 'User',
|
|
365
|
-
fields: {
|
|
366
|
-
id: createField('id', 'String'),
|
|
367
|
-
posts: createRelationField('posts', 'Post'),
|
|
368
|
-
},
|
|
369
|
-
uniqueFields: {},
|
|
370
|
-
idFields: ['id'],
|
|
371
|
-
},
|
|
372
|
-
Post: {
|
|
373
|
-
name: 'Post',
|
|
374
|
-
fields: {
|
|
375
|
-
id: createField('id', 'String'),
|
|
376
|
-
},
|
|
377
|
-
uniqueFields: {},
|
|
378
|
-
idFields: ['id'],
|
|
379
|
-
},
|
|
380
|
-
});
|
|
381
|
-
|
|
382
|
-
const callback = vi.fn();
|
|
383
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
384
|
-
|
|
385
|
-
visitor.visit('User', {
|
|
386
|
-
include: {
|
|
387
|
-
_count: {
|
|
388
|
-
select: {
|
|
389
|
-
posts: true,
|
|
390
|
-
},
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
});
|
|
394
|
-
|
|
395
|
-
// Should visit root, _count recursion (same model, undefined kind), and posts within _count select
|
|
396
|
-
expect(callback).toHaveBeenCalledTimes(3);
|
|
397
|
-
expect(callback).toHaveBeenNthCalledWith(1, 'User', undefined, undefined, expect.any(Object));
|
|
398
|
-
// _count causes recursion on same model with undefined kind
|
|
399
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
400
|
-
2,
|
|
401
|
-
'User',
|
|
402
|
-
undefined,
|
|
403
|
-
undefined,
|
|
404
|
-
expect.objectContaining({ select: { posts: true } }),
|
|
405
|
-
);
|
|
406
|
-
// Then visits posts field
|
|
407
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
408
|
-
3,
|
|
409
|
-
'Post',
|
|
410
|
-
expect.objectContaining({ name: 'posts' }),
|
|
411
|
-
'select',
|
|
412
|
-
true,
|
|
413
|
-
);
|
|
414
|
-
});
|
|
415
|
-
|
|
416
|
-
it('handles _count with nested structure', () => {
|
|
417
|
-
const schema = createSchema({
|
|
418
|
-
User: {
|
|
419
|
-
name: 'User',
|
|
420
|
-
fields: {
|
|
421
|
-
id: createField('id', 'String'),
|
|
422
|
-
posts: createRelationField('posts', 'Post'),
|
|
423
|
-
comments: createRelationField('comments', 'Comment'),
|
|
424
|
-
},
|
|
425
|
-
uniqueFields: {},
|
|
426
|
-
idFields: ['id'],
|
|
427
|
-
},
|
|
428
|
-
Post: {
|
|
429
|
-
name: 'Post',
|
|
430
|
-
fields: {
|
|
431
|
-
id: createField('id', 'String'),
|
|
432
|
-
},
|
|
433
|
-
uniqueFields: {},
|
|
434
|
-
idFields: ['id'],
|
|
435
|
-
},
|
|
436
|
-
Comment: {
|
|
437
|
-
name: 'Comment',
|
|
438
|
-
fields: {
|
|
439
|
-
id: createField('id', 'String'),
|
|
440
|
-
},
|
|
441
|
-
uniqueFields: {},
|
|
442
|
-
idFields: ['id'],
|
|
443
|
-
},
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
const callback = vi.fn();
|
|
447
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
448
|
-
|
|
449
|
-
visitor.visit('User', {
|
|
450
|
-
select: {
|
|
451
|
-
_count: {
|
|
452
|
-
select: {
|
|
453
|
-
posts: true,
|
|
454
|
-
comments: true,
|
|
455
|
-
},
|
|
456
|
-
},
|
|
457
|
-
},
|
|
458
|
-
});
|
|
459
|
-
|
|
460
|
-
expect(callback).toHaveBeenCalled();
|
|
461
|
-
});
|
|
462
|
-
});
|
|
463
|
-
|
|
464
|
-
describe('callback return value handling', () => {
|
|
465
|
-
it('stops visiting when callback returns false', () => {
|
|
466
|
-
const schema = createSchema({
|
|
467
|
-
User: {
|
|
468
|
-
name: 'User',
|
|
469
|
-
fields: {
|
|
470
|
-
id: createField('id', 'String'),
|
|
471
|
-
posts: createRelationField('posts', 'Post'),
|
|
472
|
-
},
|
|
473
|
-
uniqueFields: {},
|
|
474
|
-
idFields: ['id'],
|
|
475
|
-
},
|
|
476
|
-
Post: {
|
|
477
|
-
name: 'Post',
|
|
478
|
-
fields: {
|
|
479
|
-
id: createField('id', 'String'),
|
|
480
|
-
comments: createRelationField('comments', 'Comment'),
|
|
481
|
-
},
|
|
482
|
-
uniqueFields: {},
|
|
483
|
-
idFields: ['id'],
|
|
484
|
-
},
|
|
485
|
-
Comment: {
|
|
486
|
-
name: 'Comment',
|
|
487
|
-
fields: {
|
|
488
|
-
id: createField('id', 'String'),
|
|
489
|
-
},
|
|
490
|
-
uniqueFields: {},
|
|
491
|
-
idFields: ['id'],
|
|
492
|
-
},
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
const callback = vi.fn((_model, field) => {
|
|
496
|
-
// Return false when visiting posts to stop recursion
|
|
497
|
-
if (field?.name === 'posts') {
|
|
498
|
-
return false;
|
|
499
|
-
}
|
|
500
|
-
return true;
|
|
501
|
-
});
|
|
502
|
-
|
|
503
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
504
|
-
|
|
505
|
-
visitor.visit('User', {
|
|
506
|
-
include: {
|
|
507
|
-
posts: {
|
|
508
|
-
include: {
|
|
509
|
-
comments: true,
|
|
510
|
-
},
|
|
511
|
-
},
|
|
512
|
-
},
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
// Should visit User and posts, but not comments (stopped by returning false)
|
|
516
|
-
expect(callback).toHaveBeenCalledTimes(2);
|
|
517
|
-
expect(callback).toHaveBeenNthCalledWith(1, 'User', undefined, undefined, expect.any(Object));
|
|
518
|
-
expect(callback).toHaveBeenNthCalledWith(
|
|
519
|
-
2,
|
|
520
|
-
'Post',
|
|
521
|
-
expect.objectContaining({ name: 'posts' }),
|
|
522
|
-
'include',
|
|
523
|
-
expect.any(Object),
|
|
524
|
-
);
|
|
525
|
-
});
|
|
526
|
-
|
|
527
|
-
it('continues visiting when callback returns undefined or true', () => {
|
|
528
|
-
const schema = createSchema({
|
|
529
|
-
User: {
|
|
530
|
-
name: 'User',
|
|
531
|
-
fields: {
|
|
532
|
-
id: createField('id', 'String'),
|
|
533
|
-
posts: createRelationField('posts', 'Post'),
|
|
534
|
-
},
|
|
535
|
-
uniqueFields: {},
|
|
536
|
-
idFields: ['id'],
|
|
537
|
-
},
|
|
538
|
-
Post: {
|
|
539
|
-
name: 'Post',
|
|
540
|
-
fields: {
|
|
541
|
-
id: createField('id', 'String'),
|
|
542
|
-
comments: createRelationField('comments', 'Comment'),
|
|
543
|
-
},
|
|
544
|
-
uniqueFields: {},
|
|
545
|
-
idFields: ['id'],
|
|
546
|
-
},
|
|
547
|
-
Comment: {
|
|
548
|
-
name: 'Comment',
|
|
549
|
-
fields: {
|
|
550
|
-
id: createField('id', 'String'),
|
|
551
|
-
},
|
|
552
|
-
uniqueFields: {},
|
|
553
|
-
idFields: ['id'],
|
|
554
|
-
},
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
const callback = vi.fn((_model, field) => {
|
|
558
|
-
if (field?.name === 'posts') {
|
|
559
|
-
return true; // Explicitly continue
|
|
560
|
-
}
|
|
561
|
-
return undefined;
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
565
|
-
|
|
566
|
-
visitor.visit('User', {
|
|
567
|
-
include: {
|
|
568
|
-
posts: {
|
|
569
|
-
include: {
|
|
570
|
-
comments: true,
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
},
|
|
574
|
-
});
|
|
575
|
-
|
|
576
|
-
// Should visit all three levels
|
|
577
|
-
expect(callback).toHaveBeenCalledTimes(3);
|
|
578
|
-
});
|
|
579
|
-
});
|
|
580
|
-
|
|
581
|
-
describe('mixed include and select', () => {
|
|
582
|
-
it('handles select inside include', () => {
|
|
583
|
-
const schema = createSchema({
|
|
584
|
-
User: {
|
|
585
|
-
name: 'User',
|
|
586
|
-
fields: {
|
|
587
|
-
id: createField('id', 'String'),
|
|
588
|
-
posts: createRelationField('posts', 'Post'),
|
|
589
|
-
},
|
|
590
|
-
uniqueFields: {},
|
|
591
|
-
idFields: ['id'],
|
|
592
|
-
},
|
|
593
|
-
Post: {
|
|
594
|
-
name: 'Post',
|
|
595
|
-
fields: {
|
|
596
|
-
id: createField('id', 'String'),
|
|
597
|
-
title: createField('title', 'String'),
|
|
598
|
-
content: createField('content', 'String'),
|
|
599
|
-
},
|
|
600
|
-
uniqueFields: {},
|
|
601
|
-
idFields: ['id'],
|
|
602
|
-
},
|
|
603
|
-
});
|
|
604
|
-
|
|
605
|
-
const callback = vi.fn();
|
|
606
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
607
|
-
|
|
608
|
-
visitor.visit('User', {
|
|
609
|
-
include: {
|
|
610
|
-
posts: {
|
|
611
|
-
select: {
|
|
612
|
-
title: true,
|
|
613
|
-
},
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
});
|
|
617
|
-
|
|
618
|
-
expect(callback).toHaveBeenCalledWith('User', undefined, undefined, expect.any(Object));
|
|
619
|
-
expect(callback).toHaveBeenCalledWith(
|
|
620
|
-
'Post',
|
|
621
|
-
expect.objectContaining({ name: 'posts' }),
|
|
622
|
-
'include',
|
|
623
|
-
expect.any(Object),
|
|
624
|
-
);
|
|
625
|
-
expect(callback).toHaveBeenCalledWith('String', expect.objectContaining({ name: 'title' }), 'select', true);
|
|
626
|
-
});
|
|
627
|
-
|
|
628
|
-
it('handles include inside select', () => {
|
|
629
|
-
const schema = createSchema({
|
|
630
|
-
User: {
|
|
631
|
-
name: 'User',
|
|
632
|
-
fields: {
|
|
633
|
-
id: createField('id', 'String'),
|
|
634
|
-
posts: createRelationField('posts', 'Post'),
|
|
635
|
-
},
|
|
636
|
-
uniqueFields: {},
|
|
637
|
-
idFields: ['id'],
|
|
638
|
-
},
|
|
639
|
-
Post: {
|
|
640
|
-
name: 'Post',
|
|
641
|
-
fields: {
|
|
642
|
-
id: createField('id', 'String'),
|
|
643
|
-
comments: createRelationField('comments', 'Comment'),
|
|
644
|
-
},
|
|
645
|
-
uniqueFields: {},
|
|
646
|
-
idFields: ['id'],
|
|
647
|
-
},
|
|
648
|
-
Comment: {
|
|
649
|
-
name: 'Comment',
|
|
650
|
-
fields: {
|
|
651
|
-
id: createField('id', 'String'),
|
|
652
|
-
},
|
|
653
|
-
uniqueFields: {},
|
|
654
|
-
idFields: ['id'],
|
|
655
|
-
},
|
|
656
|
-
});
|
|
657
|
-
|
|
658
|
-
const callback = vi.fn();
|
|
659
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
660
|
-
|
|
661
|
-
visitor.visit('User', {
|
|
662
|
-
select: {
|
|
663
|
-
posts: {
|
|
664
|
-
include: {
|
|
665
|
-
comments: true,
|
|
666
|
-
},
|
|
667
|
-
},
|
|
668
|
-
},
|
|
669
|
-
});
|
|
670
|
-
|
|
671
|
-
expect(callback).toHaveBeenCalledWith('User', undefined, undefined, expect.any(Object));
|
|
672
|
-
expect(callback).toHaveBeenCalledWith(
|
|
673
|
-
'Post',
|
|
674
|
-
expect.objectContaining({ name: 'posts' }),
|
|
675
|
-
'select',
|
|
676
|
-
expect.any(Object),
|
|
677
|
-
);
|
|
678
|
-
expect(callback).toHaveBeenCalledWith(
|
|
679
|
-
'Comment',
|
|
680
|
-
expect.objectContaining({ name: 'comments' }),
|
|
681
|
-
'include',
|
|
682
|
-
true,
|
|
683
|
-
);
|
|
684
|
-
});
|
|
685
|
-
});
|
|
686
|
-
|
|
687
|
-
describe('edge cases', () => {
|
|
688
|
-
it('handles fields not in schema gracefully', () => {
|
|
689
|
-
const schema = createSchema({
|
|
690
|
-
User: {
|
|
691
|
-
name: 'User',
|
|
692
|
-
fields: {
|
|
693
|
-
id: createField('id', 'String'),
|
|
694
|
-
},
|
|
695
|
-
uniqueFields: {},
|
|
696
|
-
idFields: ['id'],
|
|
697
|
-
},
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
const callback = vi.fn();
|
|
701
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
702
|
-
|
|
703
|
-
// Try to include a field that doesn't exist
|
|
704
|
-
visitor.visit('User', {
|
|
705
|
-
include: {
|
|
706
|
-
nonExistentField: true,
|
|
707
|
-
},
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
// Should only visit the root, not the non-existent field
|
|
711
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
712
|
-
});
|
|
713
|
-
|
|
714
|
-
it('handles empty include object', () => {
|
|
715
|
-
const schema = createSchema({
|
|
716
|
-
User: {
|
|
717
|
-
name: 'User',
|
|
718
|
-
fields: {
|
|
719
|
-
id: createField('id', 'String'),
|
|
720
|
-
},
|
|
721
|
-
uniqueFields: {},
|
|
722
|
-
idFields: ['id'],
|
|
723
|
-
},
|
|
724
|
-
});
|
|
725
|
-
|
|
726
|
-
const callback = vi.fn();
|
|
727
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
728
|
-
|
|
729
|
-
visitor.visit('User', {
|
|
730
|
-
include: {},
|
|
731
|
-
});
|
|
732
|
-
|
|
733
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
734
|
-
});
|
|
735
|
-
|
|
736
|
-
it('handles empty select object', () => {
|
|
737
|
-
const schema = createSchema({
|
|
738
|
-
User: {
|
|
739
|
-
name: 'User',
|
|
740
|
-
fields: {
|
|
741
|
-
id: createField('id', 'String'),
|
|
742
|
-
},
|
|
743
|
-
uniqueFields: {},
|
|
744
|
-
idFields: ['id'],
|
|
745
|
-
},
|
|
746
|
-
});
|
|
747
|
-
|
|
748
|
-
const callback = vi.fn();
|
|
749
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
750
|
-
|
|
751
|
-
visitor.visit('User', {
|
|
752
|
-
select: {},
|
|
753
|
-
});
|
|
754
|
-
|
|
755
|
-
expect(callback).toHaveBeenCalledTimes(1);
|
|
756
|
-
});
|
|
757
|
-
|
|
758
|
-
it('handles visitor with no callback', () => {
|
|
759
|
-
const schema = createSchema({
|
|
760
|
-
User: {
|
|
761
|
-
name: 'User',
|
|
762
|
-
fields: {
|
|
763
|
-
id: createField('id', 'String'),
|
|
764
|
-
posts: createRelationField('posts', 'Post'),
|
|
765
|
-
},
|
|
766
|
-
uniqueFields: {},
|
|
767
|
-
idFields: ['id'],
|
|
768
|
-
},
|
|
769
|
-
Post: {
|
|
770
|
-
name: 'Post',
|
|
771
|
-
fields: {
|
|
772
|
-
id: createField('id', 'String'),
|
|
773
|
-
},
|
|
774
|
-
uniqueFields: {},
|
|
775
|
-
idFields: ['id'],
|
|
776
|
-
},
|
|
777
|
-
});
|
|
778
|
-
|
|
779
|
-
const visitor = new NestedReadVisitor(schema, {});
|
|
780
|
-
|
|
781
|
-
// Should not throw
|
|
782
|
-
expect(() => {
|
|
783
|
-
visitor.visit('User', {
|
|
784
|
-
include: {
|
|
785
|
-
posts: true,
|
|
786
|
-
},
|
|
787
|
-
});
|
|
788
|
-
}).not.toThrow();
|
|
789
|
-
});
|
|
790
|
-
|
|
791
|
-
it('handles non-object select/include values', () => {
|
|
792
|
-
const schema = createSchema({
|
|
793
|
-
User: {
|
|
794
|
-
name: 'User',
|
|
795
|
-
fields: {
|
|
796
|
-
id: createField('id', 'String'),
|
|
797
|
-
},
|
|
798
|
-
uniqueFields: {},
|
|
799
|
-
idFields: ['id'],
|
|
800
|
-
},
|
|
801
|
-
});
|
|
802
|
-
|
|
803
|
-
const callback = vi.fn();
|
|
804
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
805
|
-
|
|
806
|
-
visitor.visit('User', {
|
|
807
|
-
include: 'not an object',
|
|
808
|
-
});
|
|
809
|
-
|
|
810
|
-
visitor.visit('User', {
|
|
811
|
-
select: null,
|
|
812
|
-
});
|
|
813
|
-
|
|
814
|
-
// Should handle gracefully
|
|
815
|
-
expect(callback).toHaveBeenCalledTimes(2);
|
|
816
|
-
});
|
|
817
|
-
});
|
|
818
|
-
|
|
819
|
-
describe('complex real-world scenarios', () => {
|
|
820
|
-
it('handles deeply nested blog post structure', () => {
|
|
821
|
-
const schema = createSchema({
|
|
822
|
-
User: {
|
|
823
|
-
name: 'User',
|
|
824
|
-
fields: {
|
|
825
|
-
id: createField('id', 'String'),
|
|
826
|
-
name: createField('name', 'String'),
|
|
827
|
-
posts: createRelationField('posts', 'Post'),
|
|
828
|
-
profile: createRelationField('profile', 'Profile'),
|
|
829
|
-
},
|
|
830
|
-
uniqueFields: {},
|
|
831
|
-
idFields: ['id'],
|
|
832
|
-
},
|
|
833
|
-
Post: {
|
|
834
|
-
name: 'Post',
|
|
835
|
-
fields: {
|
|
836
|
-
id: createField('id', 'String'),
|
|
837
|
-
title: createField('title', 'String'),
|
|
838
|
-
comments: createRelationField('comments', 'Comment'),
|
|
839
|
-
author: createRelationField('author', 'User'),
|
|
840
|
-
},
|
|
841
|
-
uniqueFields: {},
|
|
842
|
-
idFields: ['id'],
|
|
843
|
-
},
|
|
844
|
-
Comment: {
|
|
845
|
-
name: 'Comment',
|
|
846
|
-
fields: {
|
|
847
|
-
id: createField('id', 'String'),
|
|
848
|
-
text: createField('text', 'String'),
|
|
849
|
-
author: createRelationField('author', 'User'),
|
|
850
|
-
},
|
|
851
|
-
uniqueFields: {},
|
|
852
|
-
idFields: ['id'],
|
|
853
|
-
},
|
|
854
|
-
Profile: {
|
|
855
|
-
name: 'Profile',
|
|
856
|
-
fields: {
|
|
857
|
-
id: createField('id', 'String'),
|
|
858
|
-
bio: createField('bio', 'String'),
|
|
859
|
-
},
|
|
860
|
-
uniqueFields: {},
|
|
861
|
-
idFields: ['id'],
|
|
862
|
-
},
|
|
863
|
-
});
|
|
864
|
-
|
|
865
|
-
const visitedModels: string[] = [];
|
|
866
|
-
const callback: NestedReadVisitorCallback['field'] = (model) => {
|
|
867
|
-
visitedModels.push(model);
|
|
868
|
-
};
|
|
869
|
-
|
|
870
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
871
|
-
|
|
872
|
-
visitor.visit('User', {
|
|
873
|
-
include: {
|
|
874
|
-
posts: {
|
|
875
|
-
include: {
|
|
876
|
-
comments: {
|
|
877
|
-
include: {
|
|
878
|
-
author: {
|
|
879
|
-
select: {
|
|
880
|
-
name: true,
|
|
881
|
-
},
|
|
882
|
-
},
|
|
883
|
-
},
|
|
884
|
-
},
|
|
885
|
-
},
|
|
886
|
-
},
|
|
887
|
-
profile: true,
|
|
888
|
-
},
|
|
889
|
-
});
|
|
890
|
-
|
|
891
|
-
expect(visitedModels).toContain('User');
|
|
892
|
-
expect(visitedModels).toContain('Post');
|
|
893
|
-
expect(visitedModels).toContain('Comment');
|
|
894
|
-
expect(visitedModels).toContain('Profile');
|
|
895
|
-
expect(visitedModels.filter((m) => m === 'User').length).toBeGreaterThan(1); // User visited multiple times
|
|
896
|
-
});
|
|
897
|
-
|
|
898
|
-
it('collects all visited field names', () => {
|
|
899
|
-
const schema = createSchema({
|
|
900
|
-
User: {
|
|
901
|
-
name: 'User',
|
|
902
|
-
fields: {
|
|
903
|
-
id: createField('id', 'String'),
|
|
904
|
-
email: createField('email', 'String'),
|
|
905
|
-
posts: createRelationField('posts', 'Post'),
|
|
906
|
-
},
|
|
907
|
-
uniqueFields: {},
|
|
908
|
-
idFields: ['id'],
|
|
909
|
-
},
|
|
910
|
-
Post: {
|
|
911
|
-
name: 'Post',
|
|
912
|
-
fields: {
|
|
913
|
-
id: createField('id', 'String'),
|
|
914
|
-
title: createField('title', 'String'),
|
|
915
|
-
published: createField('published', 'Boolean'),
|
|
916
|
-
},
|
|
917
|
-
uniqueFields: {},
|
|
918
|
-
idFields: ['id'],
|
|
919
|
-
},
|
|
920
|
-
});
|
|
921
|
-
|
|
922
|
-
const fieldNames: string[] = [];
|
|
923
|
-
const callback: NestedReadVisitorCallback['field'] = (_model, field) => {
|
|
924
|
-
if (field) {
|
|
925
|
-
fieldNames.push(field.name);
|
|
926
|
-
}
|
|
927
|
-
};
|
|
928
|
-
|
|
929
|
-
const visitor = new NestedReadVisitor(schema, { field: callback });
|
|
930
|
-
|
|
931
|
-
visitor.visit('User', {
|
|
932
|
-
select: {
|
|
933
|
-
email: true,
|
|
934
|
-
posts: {
|
|
935
|
-
select: {
|
|
936
|
-
title: true,
|
|
937
|
-
published: true,
|
|
938
|
-
},
|
|
939
|
-
},
|
|
940
|
-
},
|
|
941
|
-
});
|
|
942
|
-
|
|
943
|
-
expect(fieldNames).toContain('email');
|
|
944
|
-
expect(fieldNames).toContain('posts');
|
|
945
|
-
expect(fieldNames).toContain('title');
|
|
946
|
-
expect(fieldNames).toContain('published');
|
|
947
|
-
});
|
|
948
|
-
});
|
|
949
|
-
});
|