@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.
@@ -1,1399 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import { getMutatedModels, getReadModels } from '../src/query-analysis';
3
- import { createField, createRelationField, createSchema } from './test-helpers';
4
-
5
- describe('Query Analysis tests', () => {
6
- describe('getReadModels', () => {
7
- it('returns only the root model when no includes/selects', () => {
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 result = getReadModels('User', schema, {});
21
-
22
- expect(result).toEqual(['User']);
23
- });
24
-
25
- it('returns models from include relations', () => {
26
- const schema = createSchema({
27
- User: {
28
- name: 'User',
29
- fields: {
30
- id: createField('id', 'String'),
31
- posts: createRelationField('posts', 'Post'),
32
- },
33
- uniqueFields: {},
34
- idFields: ['id'],
35
- },
36
- Post: {
37
- name: 'Post',
38
- fields: {
39
- id: createField('id', 'String'),
40
- title: createField('title', 'String'),
41
- },
42
- uniqueFields: {},
43
- idFields: ['id'],
44
- },
45
- });
46
-
47
- const result = getReadModels('User', schema, {
48
- include: {
49
- posts: true,
50
- },
51
- });
52
-
53
- expect(result).toContain('User');
54
- expect(result).toContain('Post');
55
- expect(result.length).toBe(2);
56
- });
57
-
58
- it('returns models from nested includes', () => {
59
- const schema = createSchema({
60
- User: {
61
- name: 'User',
62
- fields: {
63
- id: createField('id', 'String'),
64
- posts: createRelationField('posts', 'Post'),
65
- },
66
- uniqueFields: {},
67
- idFields: ['id'],
68
- },
69
- Post: {
70
- name: 'Post',
71
- fields: {
72
- id: createField('id', 'String'),
73
- comments: createRelationField('comments', 'Comment'),
74
- },
75
- uniqueFields: {},
76
- idFields: ['id'],
77
- },
78
- Comment: {
79
- name: 'Comment',
80
- fields: {
81
- id: createField('id', 'String'),
82
- text: createField('text', 'String'),
83
- },
84
- uniqueFields: {},
85
- idFields: ['id'],
86
- },
87
- });
88
-
89
- const result = getReadModels('User', schema, {
90
- include: {
91
- posts: {
92
- include: {
93
- comments: true,
94
- },
95
- },
96
- },
97
- });
98
-
99
- expect(result).toContain('User');
100
- expect(result).toContain('Post');
101
- expect(result).toContain('Comment');
102
- expect(result.length).toBe(3);
103
- });
104
-
105
- it('returns models from select with relations', () => {
106
- const schema = createSchema({
107
- User: {
108
- name: 'User',
109
- fields: {
110
- id: createField('id', 'String'),
111
- posts: createRelationField('posts', 'Post'),
112
- },
113
- uniqueFields: {},
114
- idFields: ['id'],
115
- },
116
- Post: {
117
- name: 'Post',
118
- fields: {
119
- id: createField('id', 'String'),
120
- title: createField('title', 'String'),
121
- },
122
- uniqueFields: {},
123
- idFields: ['id'],
124
- },
125
- });
126
-
127
- const result = getReadModels('User', schema, {
128
- select: {
129
- id: true,
130
- posts: true,
131
- },
132
- });
133
-
134
- // When using select with a relation field, the visitor visits:
135
- // 1. User (root model)
136
- // 2. String (for id field)
137
- // 3. Post (for posts field)
138
- expect(result).toContain('User');
139
- expect(result).toContain('Post');
140
- expect(result.length).toBe(3); // User, String, Post
141
- });
142
-
143
- it('handles select taking precedence over include', () => {
144
- const schema = createSchema({
145
- User: {
146
- name: 'User',
147
- fields: {
148
- id: createField('id', 'String'),
149
- posts: createRelationField('posts', 'Post'),
150
- profile: createRelationField('profile', 'Profile'),
151
- },
152
- uniqueFields: {},
153
- idFields: ['id'],
154
- },
155
- Post: {
156
- name: 'Post',
157
- fields: {
158
- id: createField('id', 'String'),
159
- title: createField('title', 'String'),
160
- },
161
- uniqueFields: {},
162
- idFields: ['id'],
163
- },
164
- Profile: {
165
- name: 'Profile',
166
- fields: {
167
- id: createField('id', 'String'),
168
- bio: createField('bio', 'String'),
169
- },
170
- uniqueFields: {},
171
- idFields: ['id'],
172
- },
173
- });
174
-
175
- // When both select and include are present, select takes precedence
176
- const result = getReadModels('User', schema, {
177
- include: {
178
- posts: true,
179
- },
180
- select: {
181
- profile: true,
182
- },
183
- });
184
-
185
- expect(result).toContain('User');
186
- expect(result).toContain('Profile');
187
- // Posts is not included because select takes precedence
188
- expect(result.length).toBe(2);
189
- });
190
-
191
- it('deduplicates model names', () => {
192
- const schema = createSchema({
193
- User: {
194
- name: 'User',
195
- fields: {
196
- id: createField('id', 'String'),
197
- posts: createRelationField('posts', 'Post'),
198
- comments: createRelationField('comments', 'Comment'),
199
- },
200
- uniqueFields: {},
201
- idFields: ['id'],
202
- },
203
- Post: {
204
- name: 'Post',
205
- fields: {
206
- id: createField('id', 'String'),
207
- comments: createRelationField('comments', 'Comment'),
208
- },
209
- uniqueFields: {},
210
- idFields: ['id'],
211
- },
212
- Comment: {
213
- name: 'Comment',
214
- fields: {
215
- id: createField('id', 'String'),
216
- text: createField('text', 'String'),
217
- },
218
- uniqueFields: {},
219
- idFields: ['id'],
220
- },
221
- });
222
-
223
- const result = getReadModels('User', schema, {
224
- include: {
225
- posts: {
226
- include: {
227
- comments: true,
228
- },
229
- },
230
- comments: true,
231
- },
232
- });
233
-
234
- expect(result).toContain('User');
235
- expect(result).toContain('Post');
236
- expect(result).toContain('Comment');
237
- expect(result.length).toBe(3); // Comment should not be duplicated
238
- });
239
-
240
- it('handles undefined args', () => {
241
- const schema = createSchema({
242
- User: {
243
- name: 'User',
244
- fields: {
245
- id: createField('id', 'String'),
246
- },
247
- uniqueFields: {},
248
- idFields: ['id'],
249
- },
250
- });
251
-
252
- const result = getReadModels('User', schema, undefined);
253
-
254
- expect(result).toEqual(['User']);
255
- });
256
-
257
- it('handles null args', () => {
258
- const schema = createSchema({
259
- User: {
260
- name: 'User',
261
- fields: {
262
- id: createField('id', 'String'),
263
- },
264
- uniqueFields: {},
265
- idFields: ['id'],
266
- },
267
- });
268
-
269
- const result = getReadModels('User', schema, null);
270
-
271
- expect(result).toEqual(['User']);
272
- });
273
- });
274
-
275
- describe('getMutatedModels', () => {
276
- describe('basic mutations', () => {
277
- it('returns only the root model for simple create', async () => {
278
- const schema = createSchema({
279
- User: {
280
- name: 'User',
281
- fields: {
282
- id: createField('id', 'String'),
283
- name: createField('name', 'String'),
284
- },
285
- uniqueFields: {},
286
- idFields: ['id'],
287
- },
288
- });
289
-
290
- const result = await getMutatedModels('User', 'create', { data: { name: 'John' } }, schema);
291
-
292
- expect(result).toEqual(['User']);
293
- });
294
-
295
- it('returns only the root model for simple update', async () => {
296
- const schema = createSchema({
297
- User: {
298
- name: 'User',
299
- fields: {
300
- id: createField('id', 'String'),
301
- name: createField('name', 'String'),
302
- },
303
- uniqueFields: {},
304
- idFields: ['id'],
305
- },
306
- });
307
-
308
- const result = await getMutatedModels(
309
- 'User',
310
- 'update',
311
- { where: { id: '1' }, data: { name: 'Jane' } },
312
- schema,
313
- );
314
-
315
- expect(result).toEqual(['User']);
316
- });
317
-
318
- it('returns only the root model for delete', async () => {
319
- const schema = createSchema({
320
- User: {
321
- name: 'User',
322
- fields: {
323
- id: createField('id', 'String'),
324
- },
325
- uniqueFields: {},
326
- idFields: ['id'],
327
- },
328
- });
329
-
330
- const result = await getMutatedModels('User', 'delete', { where: { id: '1' } }, schema);
331
-
332
- expect(result).toEqual(['User']);
333
- });
334
- });
335
-
336
- describe('nested mutations', () => {
337
- it('includes models from nested create', async () => {
338
- const schema = createSchema({
339
- User: {
340
- name: 'User',
341
- fields: {
342
- id: createField('id', 'String'),
343
- posts: createRelationField('posts', 'Post'),
344
- },
345
- uniqueFields: {},
346
- idFields: ['id'],
347
- },
348
- Post: {
349
- name: 'Post',
350
- fields: {
351
- id: createField('id', 'String'),
352
- title: createField('title', 'String'),
353
- },
354
- uniqueFields: {},
355
- idFields: ['id'],
356
- },
357
- });
358
-
359
- const result = await getMutatedModels(
360
- 'User',
361
- 'create',
362
- {
363
- data: {
364
- name: 'John',
365
- posts: {
366
- create: { title: 'My Post' },
367
- },
368
- },
369
- },
370
- schema,
371
- );
372
-
373
- expect(result).toContain('User');
374
- expect(result).toContain('Post');
375
- expect(result.length).toBe(2);
376
- });
377
-
378
- it('includes models from nested update', async () => {
379
- const schema = createSchema({
380
- User: {
381
- name: 'User',
382
- fields: {
383
- id: createField('id', 'String'),
384
- posts: createRelationField('posts', 'Post'),
385
- },
386
- uniqueFields: {},
387
- idFields: ['id'],
388
- },
389
- Post: {
390
- name: 'Post',
391
- fields: {
392
- id: createField('id', 'String'),
393
- title: createField('title', 'String'),
394
- },
395
- uniqueFields: {},
396
- idFields: ['id'],
397
- },
398
- });
399
-
400
- const result = await getMutatedModels(
401
- 'User',
402
- 'update',
403
- {
404
- where: { id: '1' },
405
- data: {
406
- posts: {
407
- update: {
408
- where: { id: '1' },
409
- data: { title: 'Updated' },
410
- },
411
- },
412
- },
413
- },
414
- schema,
415
- );
416
-
417
- expect(result).toContain('User');
418
- expect(result).toContain('Post');
419
- expect(result.length).toBe(2);
420
- });
421
-
422
- it('includes models from nested connect', async () => {
423
- const schema = createSchema({
424
- User: {
425
- name: 'User',
426
- fields: {
427
- id: createField('id', 'String'),
428
- posts: createRelationField('posts', 'Post'),
429
- },
430
- uniqueFields: {},
431
- idFields: ['id'],
432
- },
433
- Post: {
434
- name: 'Post',
435
- fields: {
436
- id: createField('id', 'String'),
437
- title: createField('title', 'String'),
438
- },
439
- uniqueFields: {},
440
- idFields: ['id'],
441
- },
442
- });
443
-
444
- const result = await getMutatedModels(
445
- 'User',
446
- 'update',
447
- {
448
- where: { id: '1' },
449
- data: {
450
- posts: {
451
- connect: { id: '1' },
452
- },
453
- },
454
- },
455
- schema,
456
- );
457
-
458
- expect(result).toContain('User');
459
- expect(result).toContain('Post');
460
- expect(result.length).toBe(2);
461
- });
462
-
463
- it('includes models from nested disconnect', async () => {
464
- const schema = createSchema({
465
- User: {
466
- name: 'User',
467
- fields: {
468
- id: createField('id', 'String'),
469
- posts: createRelationField('posts', 'Post'),
470
- },
471
- uniqueFields: {},
472
- idFields: ['id'],
473
- },
474
- Post: {
475
- name: 'Post',
476
- fields: {
477
- id: createField('id', 'String'),
478
- title: createField('title', 'String'),
479
- },
480
- uniqueFields: {},
481
- idFields: ['id'],
482
- },
483
- });
484
-
485
- const result = await getMutatedModels(
486
- 'User',
487
- 'update',
488
- {
489
- where: { id: '1' },
490
- data: {
491
- posts: {
492
- disconnect: { id: '1' },
493
- },
494
- },
495
- },
496
- schema,
497
- );
498
-
499
- expect(result).toContain('User');
500
- expect(result).toContain('Post');
501
- expect(result.length).toBe(2);
502
- });
503
-
504
- it('includes models from nested set', async () => {
505
- const schema = createSchema({
506
- User: {
507
- name: 'User',
508
- fields: {
509
- id: createField('id', 'String'),
510
- posts: createRelationField('posts', 'Post'),
511
- },
512
- uniqueFields: {},
513
- idFields: ['id'],
514
- },
515
- Post: {
516
- name: 'Post',
517
- fields: {
518
- id: createField('id', 'String'),
519
- title: createField('title', 'String'),
520
- },
521
- uniqueFields: {},
522
- idFields: ['id'],
523
- },
524
- });
525
-
526
- const result = await getMutatedModels(
527
- 'User',
528
- 'update',
529
- {
530
- where: { id: '1' },
531
- data: {
532
- posts: {
533
- set: [{ id: '1' }],
534
- },
535
- },
536
- },
537
- schema,
538
- );
539
-
540
- expect(result).toContain('User');
541
- expect(result).toContain('Post');
542
- expect(result.length).toBe(2);
543
- });
544
-
545
- it('includes models from nested upsert', async () => {
546
- const schema = createSchema({
547
- User: {
548
- name: 'User',
549
- fields: {
550
- id: createField('id', 'String'),
551
- posts: createRelationField('posts', 'Post'),
552
- },
553
- uniqueFields: {},
554
- idFields: ['id'],
555
- },
556
- Post: {
557
- name: 'Post',
558
- fields: {
559
- id: createField('id', 'String'),
560
- title: createField('title', 'String'),
561
- },
562
- uniqueFields: {},
563
- idFields: ['id'],
564
- },
565
- });
566
-
567
- const result = await getMutatedModels(
568
- 'User',
569
- 'update',
570
- {
571
- where: { id: '1' },
572
- data: {
573
- posts: {
574
- upsert: {
575
- where: { id: '1' },
576
- create: { title: 'New' },
577
- update: { title: 'Updated' },
578
- },
579
- },
580
- },
581
- },
582
- schema,
583
- );
584
-
585
- expect(result).toContain('User');
586
- expect(result).toContain('Post');
587
- expect(result.length).toBe(2);
588
- });
589
-
590
- it('includes models from nested createMany', async () => {
591
- const schema = createSchema({
592
- User: {
593
- name: 'User',
594
- fields: {
595
- id: createField('id', 'String'),
596
- posts: createRelationField('posts', 'Post'),
597
- },
598
- uniqueFields: {},
599
- idFields: ['id'],
600
- },
601
- Post: {
602
- name: 'Post',
603
- fields: {
604
- id: createField('id', 'String'),
605
- title: createField('title', 'String'),
606
- },
607
- uniqueFields: {},
608
- idFields: ['id'],
609
- },
610
- });
611
-
612
- const result = await getMutatedModels(
613
- 'User',
614
- 'create',
615
- {
616
- data: {
617
- name: 'John',
618
- posts: {
619
- createMany: {
620
- data: [{ title: 'Post 1' }, { title: 'Post 2' }],
621
- },
622
- },
623
- },
624
- },
625
- schema,
626
- );
627
-
628
- expect(result).toContain('User');
629
- expect(result).toContain('Post');
630
- expect(result.length).toBe(2);
631
- });
632
-
633
- it('includes models from nested updateMany', async () => {
634
- const schema = createSchema({
635
- User: {
636
- name: 'User',
637
- fields: {
638
- id: createField('id', 'String'),
639
- posts: createRelationField('posts', 'Post'),
640
- },
641
- uniqueFields: {},
642
- idFields: ['id'],
643
- },
644
- Post: {
645
- name: 'Post',
646
- fields: {
647
- id: createField('id', 'String'),
648
- title: createField('title', 'String'),
649
- },
650
- uniqueFields: {},
651
- idFields: ['id'],
652
- },
653
- });
654
-
655
- const result = await getMutatedModels(
656
- 'User',
657
- 'update',
658
- {
659
- where: { id: '1' },
660
- data: {
661
- posts: {
662
- updateMany: {
663
- where: { published: false },
664
- data: { published: true },
665
- },
666
- },
667
- },
668
- },
669
- schema,
670
- );
671
-
672
- expect(result).toContain('User');
673
- expect(result).toContain('Post');
674
- expect(result.length).toBe(2);
675
- });
676
-
677
- it('includes models from nested connectOrCreate', async () => {
678
- const schema = createSchema({
679
- User: {
680
- name: 'User',
681
- fields: {
682
- id: createField('id', 'String'),
683
- posts: createRelationField('posts', 'Post'),
684
- },
685
- uniqueFields: {},
686
- idFields: ['id'],
687
- },
688
- Post: {
689
- name: 'Post',
690
- fields: {
691
- id: createField('id', 'String'),
692
- title: createField('title', 'String'),
693
- },
694
- uniqueFields: {},
695
- idFields: ['id'],
696
- },
697
- });
698
-
699
- const result = await getMutatedModels(
700
- 'User',
701
- 'update',
702
- {
703
- where: { id: '1' },
704
- data: {
705
- posts: {
706
- connectOrCreate: {
707
- where: { id: '1' },
708
- create: { title: 'New Post' },
709
- },
710
- },
711
- },
712
- },
713
- schema,
714
- );
715
-
716
- expect(result).toContain('User');
717
- expect(result).toContain('Post');
718
- expect(result.length).toBe(2);
719
- });
720
-
721
- it('includes models from deeply nested mutations', async () => {
722
- const schema = createSchema({
723
- User: {
724
- name: 'User',
725
- fields: {
726
- id: createField('id', 'String'),
727
- posts: createRelationField('posts', 'Post'),
728
- },
729
- uniqueFields: {},
730
- idFields: ['id'],
731
- },
732
- Post: {
733
- name: 'Post',
734
- fields: {
735
- id: createField('id', 'String'),
736
- comments: createRelationField('comments', 'Comment'),
737
- },
738
- uniqueFields: {},
739
- idFields: ['id'],
740
- },
741
- Comment: {
742
- name: 'Comment',
743
- fields: {
744
- id: createField('id', 'String'),
745
- text: createField('text', 'String'),
746
- },
747
- uniqueFields: {},
748
- idFields: ['id'],
749
- },
750
- });
751
-
752
- const result = await getMutatedModels(
753
- 'User',
754
- 'create',
755
- {
756
- data: {
757
- name: 'John',
758
- posts: {
759
- create: {
760
- title: 'My Post',
761
- comments: {
762
- create: { text: 'Great!' },
763
- },
764
- },
765
- },
766
- },
767
- },
768
- schema,
769
- );
770
-
771
- expect(result).toContain('User');
772
- expect(result).toContain('Post');
773
- expect(result).toContain('Comment');
774
- expect(result.length).toBe(3);
775
- });
776
- });
777
-
778
- describe('cascade deletes', () => {
779
- it('includes cascaded models when deleting', async () => {
780
- const schema = createSchema({
781
- User: {
782
- name: 'User',
783
- fields: {
784
- id: createField('id', 'String'),
785
- },
786
- uniqueFields: {},
787
- idFields: ['id'],
788
- },
789
- Post: {
790
- name: 'Post',
791
- fields: {
792
- id: createField('id', 'String'),
793
- user: {
794
- name: 'user',
795
- type: 'User',
796
- optional: false,
797
- relation: {
798
- opposite: 'posts',
799
- onDelete: 'Cascade',
800
- },
801
- },
802
- },
803
- uniqueFields: {},
804
- idFields: ['id'],
805
- },
806
- });
807
-
808
- const result = await getMutatedModels('User', 'delete', { where: { id: '1' } }, schema);
809
-
810
- expect(result).toContain('User');
811
- expect(result).toContain('Post');
812
- expect(result.length).toBe(2);
813
- });
814
-
815
- it('includes cascaded models when using deleteMany', async () => {
816
- const schema = createSchema({
817
- User: {
818
- name: 'User',
819
- fields: {
820
- id: createField('id', 'String'),
821
- },
822
- uniqueFields: {},
823
- idFields: ['id'],
824
- },
825
- Post: {
826
- name: 'Post',
827
- fields: {
828
- id: createField('id', 'String'),
829
- user: {
830
- name: 'user',
831
- type: 'User',
832
- optional: false,
833
- relation: {
834
- opposite: 'posts',
835
- onDelete: 'Cascade',
836
- },
837
- },
838
- },
839
- uniqueFields: {},
840
- idFields: ['id'],
841
- },
842
- });
843
-
844
- const result = await getMutatedModels('User', 'deleteMany', { where: { active: false } }, schema);
845
-
846
- expect(result).toContain('User');
847
- expect(result).toContain('Post');
848
- expect(result.length).toBe(2);
849
- });
850
-
851
- it('includes multi-level cascade deletes', async () => {
852
- const schema = createSchema({
853
- User: {
854
- name: 'User',
855
- fields: {
856
- id: createField('id', 'String'),
857
- },
858
- uniqueFields: {},
859
- idFields: ['id'],
860
- },
861
- Post: {
862
- name: 'Post',
863
- fields: {
864
- id: createField('id', 'String'),
865
- user: {
866
- name: 'user',
867
- type: 'User',
868
- optional: false,
869
- relation: {
870
- opposite: 'posts',
871
- onDelete: 'Cascade',
872
- },
873
- },
874
- },
875
- uniqueFields: {},
876
- idFields: ['id'],
877
- },
878
- Comment: {
879
- name: 'Comment',
880
- fields: {
881
- id: createField('id', 'String'),
882
- post: {
883
- name: 'post',
884
- type: 'Post',
885
- optional: false,
886
- relation: {
887
- opposite: 'comments',
888
- onDelete: 'Cascade',
889
- },
890
- },
891
- },
892
- uniqueFields: {},
893
- idFields: ['id'],
894
- },
895
- });
896
-
897
- const result = await getMutatedModels('User', 'delete', { where: { id: '1' } }, schema);
898
-
899
- expect(result).toContain('User');
900
- expect(result).toContain('Post');
901
- expect(result).toContain('Comment');
902
- expect(result.length).toBe(3);
903
- });
904
-
905
- it('does not include models without cascade delete', async () => {
906
- const schema = createSchema({
907
- User: {
908
- name: 'User',
909
- fields: {
910
- id: createField('id', 'String'),
911
- },
912
- uniqueFields: {},
913
- idFields: ['id'],
914
- },
915
- Post: {
916
- name: 'Post',
917
- fields: {
918
- id: createField('id', 'String'),
919
- user: {
920
- name: 'user',
921
- type: 'User',
922
- optional: false,
923
- relation: {
924
- opposite: 'posts',
925
- onDelete: 'SetNull',
926
- },
927
- },
928
- },
929
- uniqueFields: {},
930
- idFields: ['id'],
931
- },
932
- });
933
-
934
- const result = await getMutatedModels('User', 'delete', { where: { id: '1' } }, schema);
935
-
936
- expect(result).toEqual(['User']);
937
- });
938
-
939
- it('handles circular cascade relationships', async () => {
940
- const schema = createSchema({
941
- User: {
942
- name: 'User',
943
- fields: {
944
- id: createField('id', 'String'),
945
- },
946
- uniqueFields: {},
947
- idFields: ['id'],
948
- },
949
- Post: {
950
- name: 'Post',
951
- fields: {
952
- id: createField('id', 'String'),
953
- user: {
954
- name: 'user',
955
- type: 'User',
956
- optional: false,
957
- relation: {
958
- opposite: 'posts',
959
- onDelete: 'Cascade',
960
- },
961
- },
962
- },
963
- uniqueFields: {},
964
- idFields: ['id'],
965
- },
966
- Comment: {
967
- name: 'Comment',
968
- fields: {
969
- id: createField('id', 'String'),
970
- post: {
971
- name: 'post',
972
- type: 'Post',
973
- optional: false,
974
- relation: {
975
- opposite: 'comments',
976
- onDelete: 'Cascade',
977
- },
978
- },
979
- // This creates a potential circle: User -> Post -> Comment -> Post
980
- relatedPost: {
981
- name: 'relatedPost',
982
- type: 'Post',
983
- optional: true,
984
- relation: {
985
- opposite: 'relatedComments',
986
- onDelete: 'Cascade',
987
- },
988
- },
989
- },
990
- uniqueFields: {},
991
- idFields: ['id'],
992
- },
993
- });
994
-
995
- const result = await getMutatedModels('User', 'delete', { where: { id: '1' } }, schema);
996
-
997
- expect(result).toContain('User');
998
- expect(result).toContain('Post');
999
- expect(result).toContain('Comment');
1000
- expect(result.length).toBe(3);
1001
- });
1002
- });
1003
-
1004
- describe('delegate base models', () => {
1005
- it('includes base model when mutating child', async () => {
1006
- const schema = createSchema({
1007
- Animal: {
1008
- name: 'Animal',
1009
- fields: {
1010
- id: createField('id', 'String'),
1011
- name: createField('name', 'String'),
1012
- },
1013
- uniqueFields: {},
1014
- idFields: ['id'],
1015
- },
1016
- Dog: {
1017
- name: 'Dog',
1018
- baseModel: 'Animal',
1019
- fields: {
1020
- id: createField('id', 'String'),
1021
- breed: createField('breed', 'String'),
1022
- },
1023
- uniqueFields: {},
1024
- idFields: ['id'],
1025
- },
1026
- });
1027
-
1028
- const result = await getMutatedModels('Dog', 'create', { data: { breed: 'Labrador' } }, schema);
1029
-
1030
- expect(result).toContain('Dog');
1031
- expect(result).toContain('Animal');
1032
- expect(result.length).toBe(2);
1033
- });
1034
-
1035
- it('includes multi-level base models', async () => {
1036
- const schema = createSchema({
1037
- Entity: {
1038
- name: 'Entity',
1039
- fields: {
1040
- id: createField('id', 'String'),
1041
- },
1042
- uniqueFields: {},
1043
- idFields: ['id'],
1044
- },
1045
- Animal: {
1046
- name: 'Animal',
1047
- baseModel: 'Entity',
1048
- fields: {
1049
- id: createField('id', 'String'),
1050
- name: createField('name', 'String'),
1051
- },
1052
- uniqueFields: {},
1053
- idFields: ['id'],
1054
- },
1055
- Dog: {
1056
- name: 'Dog',
1057
- baseModel: 'Animal',
1058
- fields: {
1059
- id: createField('id', 'String'),
1060
- breed: createField('breed', 'String'),
1061
- },
1062
- uniqueFields: {},
1063
- idFields: ['id'],
1064
- },
1065
- });
1066
-
1067
- const result = await getMutatedModels('Dog', 'create', { data: { breed: 'Labrador' } }, schema);
1068
-
1069
- expect(result).toContain('Dog');
1070
- expect(result).toContain('Animal');
1071
- expect(result).toContain('Entity');
1072
- expect(result.length).toBe(3);
1073
- });
1074
-
1075
- it('includes base models for nested mutations', async () => {
1076
- const schema = createSchema({
1077
- User: {
1078
- name: 'User',
1079
- fields: {
1080
- id: createField('id', 'String'),
1081
- pets: createRelationField('pets', 'Dog'),
1082
- },
1083
- uniqueFields: {},
1084
- idFields: ['id'],
1085
- },
1086
- Animal: {
1087
- name: 'Animal',
1088
- fields: {
1089
- id: createField('id', 'String'),
1090
- name: createField('name', 'String'),
1091
- },
1092
- uniqueFields: {},
1093
- idFields: ['id'],
1094
- },
1095
- Dog: {
1096
- name: 'Dog',
1097
- baseModel: 'Animal',
1098
- fields: {
1099
- id: createField('id', 'String'),
1100
- breed: createField('breed', 'String'),
1101
- },
1102
- uniqueFields: {},
1103
- idFields: ['id'],
1104
- },
1105
- });
1106
-
1107
- const result = await getMutatedModels(
1108
- 'User',
1109
- 'create',
1110
- {
1111
- data: {
1112
- name: 'John',
1113
- pets: {
1114
- create: { breed: 'Labrador' },
1115
- },
1116
- },
1117
- },
1118
- schema,
1119
- );
1120
-
1121
- expect(result).toContain('User');
1122
- expect(result).toContain('Dog');
1123
- expect(result).toContain('Animal');
1124
- expect(result.length).toBe(3);
1125
- });
1126
- });
1127
-
1128
- describe('edge cases', () => {
1129
- it('handles undefined args', async () => {
1130
- const schema = createSchema({
1131
- User: {
1132
- name: 'User',
1133
- fields: {
1134
- id: createField('id', 'String'),
1135
- },
1136
- uniqueFields: {},
1137
- idFields: ['id'],
1138
- },
1139
- });
1140
-
1141
- const result = await getMutatedModels('User', 'create', undefined, schema);
1142
-
1143
- expect(result).toEqual(['User']);
1144
- });
1145
-
1146
- it('handles null args', async () => {
1147
- const schema = createSchema({
1148
- User: {
1149
- name: 'User',
1150
- fields: {
1151
- id: createField('id', 'String'),
1152
- },
1153
- uniqueFields: {},
1154
- idFields: ['id'],
1155
- },
1156
- });
1157
-
1158
- const result = await getMutatedModels('User', 'create', null, schema);
1159
-
1160
- expect(result).toEqual(['User']);
1161
- });
1162
-
1163
- it('deduplicates models from multiple sources', async () => {
1164
- const schema = createSchema({
1165
- User: {
1166
- name: 'User',
1167
- fields: {
1168
- id: createField('id', 'String'),
1169
- posts: createRelationField('posts', 'Post'),
1170
- comments: createRelationField('comments', 'Comment'),
1171
- },
1172
- uniqueFields: {},
1173
- idFields: ['id'],
1174
- },
1175
- Post: {
1176
- name: 'Post',
1177
- fields: {
1178
- id: createField('id', 'String'),
1179
- comments: createRelationField('comments', 'Comment'),
1180
- },
1181
- uniqueFields: {},
1182
- idFields: ['id'],
1183
- },
1184
- Comment: {
1185
- name: 'Comment',
1186
- fields: {
1187
- id: createField('id', 'String'),
1188
- text: createField('text', 'String'),
1189
- },
1190
- uniqueFields: {},
1191
- idFields: ['id'],
1192
- },
1193
- });
1194
-
1195
- const result = await getMutatedModels(
1196
- 'User',
1197
- 'create',
1198
- {
1199
- data: {
1200
- name: 'John',
1201
- posts: {
1202
- create: {
1203
- title: 'Post',
1204
- comments: {
1205
- create: { text: 'Comment' },
1206
- },
1207
- },
1208
- },
1209
- comments: {
1210
- create: { text: 'Comment' },
1211
- },
1212
- },
1213
- },
1214
- schema,
1215
- );
1216
-
1217
- expect(result).toContain('User');
1218
- expect(result).toContain('Post');
1219
- expect(result).toContain('Comment');
1220
- expect(result.length).toBe(3); // Comment should not be duplicated
1221
- });
1222
-
1223
- it('handles model not in schema', async () => {
1224
- const schema = createSchema({
1225
- User: {
1226
- name: 'User',
1227
- fields: {
1228
- id: createField('id', 'String'),
1229
- },
1230
- uniqueFields: {},
1231
- idFields: ['id'],
1232
- },
1233
- });
1234
-
1235
- const result = await getMutatedModels('NonExistent', 'create', { data: {} }, schema);
1236
-
1237
- expect(result).toEqual(['NonExistent']);
1238
- });
1239
- });
1240
-
1241
- describe('real-world scenarios', () => {
1242
- it('handles complex nested mutation with cascades and base models', async () => {
1243
- const schema = createSchema({
1244
- Entity: {
1245
- name: 'Entity',
1246
- fields: {
1247
- id: createField('id', 'String'),
1248
- },
1249
- uniqueFields: {},
1250
- idFields: ['id'],
1251
- },
1252
- User: {
1253
- name: 'User',
1254
- baseModel: 'Entity',
1255
- fields: {
1256
- id: createField('id', 'String'),
1257
- posts: createRelationField('posts', 'Post'),
1258
- },
1259
- uniqueFields: {},
1260
- idFields: ['id'],
1261
- },
1262
- Post: {
1263
- name: 'Post',
1264
- fields: {
1265
- id: createField('id', 'String'),
1266
- user: {
1267
- name: 'user',
1268
- type: 'User',
1269
- optional: false,
1270
- relation: {
1271
- opposite: 'posts',
1272
- onDelete: 'Cascade',
1273
- },
1274
- },
1275
- comments: createRelationField('comments', 'Comment'),
1276
- },
1277
- uniqueFields: {},
1278
- idFields: ['id'],
1279
- },
1280
- Comment: {
1281
- name: 'Comment',
1282
- fields: {
1283
- id: createField('id', 'String'),
1284
- post: {
1285
- name: 'post',
1286
- type: 'Post',
1287
- optional: false,
1288
- relation: {
1289
- opposite: 'comments',
1290
- onDelete: 'Cascade',
1291
- },
1292
- },
1293
- },
1294
- uniqueFields: {},
1295
- idFields: ['id'],
1296
- },
1297
- });
1298
-
1299
- const result = await getMutatedModels(
1300
- 'User',
1301
- 'update',
1302
- {
1303
- where: { id: '1' },
1304
- data: {
1305
- posts: {
1306
- create: {
1307
- title: 'New Post',
1308
- comments: {
1309
- create: { text: 'Comment' },
1310
- },
1311
- },
1312
- delete: { id: '2' },
1313
- },
1314
- },
1315
- },
1316
- schema,
1317
- );
1318
-
1319
- expect(result).toContain('User');
1320
- expect(result).toContain('Entity'); // base model
1321
- expect(result).toContain('Post');
1322
- expect(result).toContain('Comment'); // both from create and cascade delete
1323
- expect(result.length).toBe(4);
1324
- });
1325
-
1326
- it('handles blog post creation with author, tags, and categories', async () => {
1327
- const schema = createSchema({
1328
- User: {
1329
- name: 'User',
1330
- fields: {
1331
- id: createField('id', 'String'),
1332
- posts: createRelationField('posts', 'Post'),
1333
- },
1334
- uniqueFields: {},
1335
- idFields: ['id'],
1336
- },
1337
- Post: {
1338
- name: 'Post',
1339
- fields: {
1340
- id: createField('id', 'String'),
1341
- author: createRelationField('author', 'User'),
1342
- tags: createRelationField('tags', 'Tag'),
1343
- categories: createRelationField('categories', 'Category'),
1344
- },
1345
- uniqueFields: {},
1346
- idFields: ['id'],
1347
- },
1348
- Tag: {
1349
- name: 'Tag',
1350
- fields: {
1351
- id: createField('id', 'String'),
1352
- name: createField('name', 'String'),
1353
- },
1354
- uniqueFields: {},
1355
- idFields: ['id'],
1356
- },
1357
- Category: {
1358
- name: 'Category',
1359
- fields: {
1360
- id: createField('id', 'String'),
1361
- name: createField('name', 'String'),
1362
- },
1363
- uniqueFields: {},
1364
- idFields: ['id'],
1365
- },
1366
- });
1367
-
1368
- const result = await getMutatedModels(
1369
- 'Post',
1370
- 'create',
1371
- {
1372
- data: {
1373
- title: 'My Post',
1374
- author: {
1375
- connect: { id: '1' },
1376
- },
1377
- tags: {
1378
- create: [{ name: 'tech' }, { name: 'tutorial' }],
1379
- },
1380
- categories: {
1381
- connectOrCreate: {
1382
- where: { id: '1' },
1383
- create: { name: 'Programming' },
1384
- },
1385
- },
1386
- },
1387
- },
1388
- schema,
1389
- );
1390
-
1391
- expect(result).toContain('Post');
1392
- expect(result).toContain('User');
1393
- expect(result).toContain('Tag');
1394
- expect(result).toContain('Category');
1395
- expect(result.length).toBe(4);
1396
- });
1397
- });
1398
- });
1399
- });