@mastra/pg 0.14.6-alpha.0 → 0.14.6-alpha.2

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,967 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
-
3
- import { PGFilterTranslator } from './filter';
4
-
5
- describe('PGFilterTranslator', () => {
6
- const translator = new PGFilterTranslator();
7
-
8
- // Basic Filter Translation
9
- describe('Basic Filter Translation', () => {
10
- it('handles empty filter', () => {
11
- expect(translator.translate({})).toEqual({});
12
- });
13
-
14
- it('translates primitive to $eq', () => {
15
- expect(translator.translate({ field: 'value' })).toEqual({
16
- field: { $eq: 'value' },
17
- });
18
- });
19
-
20
- it('translates array to $in', () => {
21
- expect(translator.translate({ field: ['a', 'b'] })).toEqual({
22
- field: { $in: ['a', 'b'] },
23
- });
24
- });
25
-
26
- it('preserves comparison operators', () => {
27
- const filter = {
28
- field1: { $eq: 'value' },
29
- field2: { $ne: 'value' },
30
- field3: { $gt: 5 },
31
- field4: { $gte: 5 },
32
- field5: { $lt: 5 },
33
- field6: { $lte: 5 },
34
- };
35
- expect(translator.translate(filter)).toEqual(filter);
36
- });
37
-
38
- it('preserves multiple comparison operators', () => {
39
- expect(translator.translate({ field: { $gt: 5, $lt: 10 } })).toEqual({ field: { $gt: 5, $lt: 10 } });
40
- });
41
-
42
- it('handles nested paths', () => {
43
- expect(
44
- translator.translate({
45
- 'nested.field': { $eq: 'value' },
46
- }),
47
- ).toEqual({
48
- 'nested.field': { $eq: 'value' },
49
- });
50
- });
51
-
52
- it('handles nested objects', () => {
53
- expect(
54
- translator.translate({
55
- nested: {
56
- field: 'value',
57
- } as any,
58
- }),
59
- ).toEqual({
60
- 'nested.field': { $eq: 'value' },
61
- });
62
- });
63
- });
64
-
65
- // Array Operations
66
- describe('array operations', () => {
67
- it('translates array to $in', () => {
68
- expect(translator.translate({ field: ['a', 'b'] })).toEqual({
69
- field: { $in: ['a', 'b'] },
70
- });
71
- });
72
-
73
- it('translates arrays to JSON for basic operators', () => {
74
- expect(translator.translate({ field: { $eq: ['a', 'b'] } })).toEqual({
75
- field: { $eq: JSON.stringify(['a', 'b']) },
76
- });
77
- expect(translator.translate({ field: { $ne: ['a', 'b'] } })).toEqual({
78
- field: { $ne: JSON.stringify(['a', 'b']) },
79
- });
80
- });
81
-
82
- it('handles empty arrays', () => {
83
- expect(
84
- translator.translate({
85
- field: [],
86
- }),
87
- ).toEqual({
88
- field: { $in: [] },
89
- });
90
- });
91
-
92
- it('validates $elemMatch translation', () => {
93
- expect(
94
- translator.translate({
95
- field: {
96
- $elemMatch: {
97
- $eq: 'value',
98
- },
99
- },
100
- }),
101
- ).toEqual({
102
- field: {
103
- $elemMatch: {
104
- $eq: 'value',
105
- },
106
- },
107
- });
108
-
109
- // Test multiple conditions
110
- expect(
111
- translator.translate({
112
- field: {
113
- $elemMatch: {
114
- $gt: 5,
115
- $lt: 10,
116
- },
117
- },
118
- }),
119
- ).toEqual({
120
- field: {
121
- $elemMatch: {
122
- $gt: 5,
123
- $lt: 10,
124
- },
125
- },
126
- });
127
- });
128
- });
129
-
130
- // Array Operator Normalization
131
- describe('array operator normalization', () => {
132
- it('normalizes single values for $all', () => {
133
- expect(
134
- translator.translate({
135
- field: { $all: 'value' },
136
- }),
137
- ).toEqual({
138
- field: { $all: ['value'] },
139
- });
140
- });
141
-
142
- it('normalizes single values for $in', () => {
143
- expect(
144
- translator.translate({
145
- field: { $in: 'value' },
146
- }),
147
- ).toEqual({
148
- field: { $in: ['value'] },
149
- });
150
- });
151
-
152
- it('normalizes single values for $nin', () => {
153
- expect(
154
- translator.translate({
155
- field: { $nin: 'value' },
156
- }),
157
- ).toEqual({
158
- field: { $nin: ['value'] },
159
- });
160
- });
161
-
162
- it('preserves arrays for array operators', () => {
163
- expect(
164
- translator.translate({
165
- field: { $all: ['value1', 'value2'] },
166
- }),
167
- ).toEqual({
168
- field: { $all: ['value1', 'value2'] },
169
- });
170
- });
171
- });
172
-
173
- // Logical Operators
174
- describe('Logical Operators', () => {
175
- it('handles logical operators', () => {
176
- const filter = {
177
- $and: [{ field1: { $eq: 'value1' } }, { field2: { $eq: 'value2' } }],
178
- $or: [{ field3: { $eq: 'value3' } }, { field4: { $eq: 'value4' } }],
179
- };
180
- expect(translator.translate(filter)).toEqual(filter);
181
- });
182
-
183
- it('handles empty logical operators', () => {
184
- expect(
185
- translator.translate({
186
- $and: [],
187
- $or: [],
188
- }),
189
- ).toEqual({
190
- $and: [],
191
- $or: [],
192
- });
193
- });
194
-
195
- it('handles multiple logical operators at root level', () => {
196
- expect(
197
- translator.translate({
198
- $and: [{ category: 'electronics' }],
199
- $or: [{ price: { $lt: 100 } }, { price: { $gt: 20 } }],
200
- }),
201
- ).toEqual({
202
- $and: [{ category: { $eq: 'electronics' } }],
203
- $or: [{ price: { $lt: 100 } }, { price: { $gt: 20 } }],
204
- });
205
- });
206
-
207
- it('handles empty conditions in logical operators', () => {
208
- expect(
209
- translator.translate({
210
- $and: [],
211
- category: 'electronics',
212
- }),
213
- ).toEqual({
214
- $and: [],
215
- category: { $eq: 'electronics' },
216
- });
217
- });
218
-
219
- it('handles $nor operator', () => {
220
- expect(
221
- translator.translate({
222
- $nor: [{ field1: 'value1' }, { field2: { $gt: 100 } }],
223
- }),
224
- ).toEqual({
225
- $nor: [{ field1: { $eq: 'value1' } }, { field2: { $gt: 100 } }],
226
- });
227
- });
228
-
229
- it('handles $not operator with comparison', () => {
230
- expect(
231
- translator.translate({
232
- field: { $not: { $eq: 'value' } },
233
- }),
234
- ).toEqual({
235
- field: { $not: { $eq: 'value' } },
236
- });
237
- });
238
-
239
- it('handles $not operator with regex', () => {
240
- expect(
241
- translator.translate({
242
- field: { $not: { $regex: 'pattern' } },
243
- }),
244
- ).toEqual({
245
- field: { $not: { $regex: 'pattern' } },
246
- });
247
- });
248
-
249
- it('handles nested logical operators', () => {
250
- expect(
251
- translator.translate({
252
- $and: [
253
- {
254
- $or: [{ field1: 'value1' }, { field2: 'value2' }],
255
- },
256
- {
257
- $nor: [{ field3: 'value3' }, { field4: 'value4' }],
258
- },
259
- ],
260
- }),
261
- ).toEqual({
262
- $and: [
263
- {
264
- $or: [{ field1: { $eq: 'value1' } }, { field2: { $eq: 'value2' } }],
265
- },
266
- {
267
- $nor: [{ field3: { $eq: 'value3' } }, { field4: { $eq: 'value4' } }],
268
- },
269
- ],
270
- });
271
- });
272
-
273
- it('handles logical operators with empty arrays and primitives', () => {
274
- expect(
275
- translator.translate({
276
- $and: [],
277
- $or: [{ field1: 'value1' }],
278
- field2: true,
279
- $nor: [],
280
- }),
281
- ).toEqual({
282
- $and: [],
283
- $or: [{ field1: { $eq: 'value1' } }],
284
- field2: { $eq: true },
285
- $nor: [],
286
- });
287
- });
288
-
289
- it('handles $not operator with comparison', () => {
290
- expect(
291
- translator.translate({
292
- field: { $not: { $eq: 'value' } },
293
- }),
294
- ).toEqual({
295
- field: { $not: { $eq: 'value' } },
296
- });
297
- });
298
-
299
- it('handles $not operator with array operators', () => {
300
- expect(
301
- translator.translate({
302
- field: { $not: { $in: ['value1', 'value2'] } },
303
- }),
304
- ).toEqual({
305
- field: { $not: { $in: ['value1', 'value2'] } },
306
- });
307
- });
308
-
309
- it('handles multiple $not conditions', () => {
310
- expect(
311
- translator.translate({
312
- $and: [{ field1: { $not: { $eq: 'value1' } } }, { field2: { $not: { $in: ['value2', 'value3'] } } }],
313
- }),
314
- ).toEqual({
315
- $and: [{ field1: { $not: { $eq: 'value1' } } }, { field2: { $not: { $in: ['value2', 'value3'] } } }],
316
- });
317
- });
318
-
319
- it('handles combination of all logical operators', () => {
320
- expect(
321
- translator.translate({
322
- $and: [
323
- {
324
- $or: [{ field1: 'value1' }, { field2: { $gt: 100 } }],
325
- },
326
- {
327
- $nor: [{ field3: { $lt: 50 } }, { field4: { $eq: 'value4' } }],
328
- },
329
- {
330
- field5: { $not: { $in: ['value5', 'value6'] } },
331
- },
332
- ],
333
- }),
334
- ).toEqual({
335
- $and: [
336
- {
337
- $or: [{ field1: { $eq: 'value1' } }, { field2: { $gt: 100 } }],
338
- },
339
- {
340
- $nor: [{ field3: { $lt: 50 } }, { field4: { $eq: 'value4' } }],
341
- },
342
- {
343
- field5: { $not: { $in: ['value5', 'value6'] } },
344
- },
345
- ],
346
- });
347
- });
348
- });
349
-
350
- // Complex Filter Structures
351
- describe('complex filter structures', () => {
352
- it('handles nested logical operators with mixed conditions', () => {
353
- expect(
354
- translator.translate({
355
- $or: [
356
- {
357
- $and: [{ category: 'value1' }, { price: { $gt: 90 } }, { active: true }],
358
- },
359
- {
360
- $and: [{ category: 'value2' }, { tags: { $exists: true } }, { price: { $lt: 30 } }],
361
- },
362
- ],
363
- }),
364
- ).toEqual({
365
- $or: [
366
- {
367
- $and: [{ category: { $eq: 'value1' } }, { price: { $gt: 90 } }, { active: { $eq: true } }],
368
- },
369
- {
370
- $and: [{ category: { $eq: 'value2' } }, { tags: { $exists: true } }, { price: { $lt: 30 } }],
371
- },
372
- ],
373
- });
374
- });
375
-
376
- it('handles deeply nested metadata paths', () => {
377
- expect(
378
- translator.translate({
379
- 'level1.level2.level3': 'deep value',
380
- }),
381
- ).toEqual({
382
- 'level1.level2.level3': { $eq: 'deep value' },
383
- });
384
- });
385
-
386
- it('handles non-existent nested paths', () => {
387
- expect(
388
- translator.translate({
389
- 'nonexistent.path': 'value',
390
- }),
391
- ).toEqual({
392
- 'nonexistent.path': { $eq: 'value' },
393
- });
394
- });
395
-
396
- it('handles logical operators with empty arrays and primitives', () => {
397
- expect(
398
- translator.translate({
399
- $and: [],
400
- $or: [{ field1: 'value1' }],
401
- field2: true,
402
- $nor: [],
403
- }),
404
- ).toEqual({
405
- $and: [],
406
- $or: [{ field1: { $eq: 'value1' } }],
407
- field2: { $eq: true },
408
- $nor: [],
409
- });
410
- });
411
- });
412
-
413
- // Edge Cases and Special Values
414
- describe('Edge Cases and Special Values', () => {
415
- it('handles null values', () => {
416
- expect(
417
- translator.translate({
418
- field: null,
419
- }),
420
- ).toEqual({
421
- field: { $eq: null },
422
- });
423
- });
424
-
425
- it('handles boolean values', () => {
426
- expect(
427
- translator.translate({
428
- field1: true,
429
- field2: false,
430
- }),
431
- ).toEqual({
432
- field1: { $eq: true },
433
- field2: { $eq: false },
434
- });
435
- });
436
-
437
- it('handles numeric values', () => {
438
- expect(
439
- translator.translate({
440
- int: 42,
441
- float: 42.42,
442
- negative: -42,
443
- }),
444
- ).toEqual({
445
- int: { $eq: 42 },
446
- float: { $eq: 42.42 },
447
- negative: { $eq: -42 },
448
- });
449
- });
450
-
451
- it('handles empty strings', () => {
452
- expect(
453
- translator.translate({
454
- field: '',
455
- }),
456
- ).toEqual({
457
- field: { $eq: '' },
458
- });
459
- });
460
-
461
- it('handles empty arrays', () => {
462
- expect(
463
- translator.translate({
464
- field: [],
465
- }),
466
- ).toEqual({
467
- field: { $in: [] },
468
- });
469
- });
470
- });
471
-
472
- // Complex Filter Structures
473
- describe('Complex Filter Structures', () => {
474
- it('handles nested logical operators with mixed conditions', () => {
475
- expect(
476
- translator.translate({
477
- $or: [
478
- {
479
- $and: [{ category: 'value1' }, { price: { $gt: 90 } }, { active: true }],
480
- },
481
- {
482
- $and: [{ category: 'value2' }, { tags: { $exists: true } }, { price: { $lt: 30 } }],
483
- },
484
- ],
485
- }),
486
- ).toEqual({
487
- $or: [
488
- {
489
- $and: [{ category: { $eq: 'value1' } }, { price: { $gt: 90 } }, { active: { $eq: true } }],
490
- },
491
- {
492
- $and: [{ category: { $eq: 'value2' } }, { tags: { $exists: true } }, { price: { $lt: 30 } }],
493
- },
494
- ],
495
- });
496
- });
497
-
498
- it('handles multiple logical operators at root level', () => {
499
- expect(
500
- translator.translate({
501
- $and: [{ category: 'electronics' }],
502
- $or: [{ price: { $lt: 100 } }, { price: { $gt: 20 } }],
503
- }),
504
- ).toEqual({
505
- $and: [{ category: { $eq: 'electronics' } }],
506
- $or: [{ price: { $lt: 100 } }, { price: { $gt: 20 } }],
507
- });
508
- });
509
-
510
- it('handles empty conditions in logical operators', () => {
511
- expect(
512
- translator.translate({
513
- $and: [],
514
- category: 'electronics',
515
- }),
516
- ).toEqual({
517
- $and: [],
518
- category: { $eq: 'electronics' },
519
- });
520
- });
521
-
522
- it('handles deeply nested metadata paths', () => {
523
- expect(
524
- translator.translate({
525
- 'level1.level2.level3': 'deep value',
526
- }),
527
- ).toEqual({
528
- 'level1.level2.level3': { $eq: 'deep value' },
529
- });
530
- });
531
-
532
- it('handles non-existent nested paths', () => {
533
- expect(
534
- translator.translate({
535
- 'nonexistent.path': 'value',
536
- }),
537
- ).toEqual({
538
- 'nonexistent.path': { $eq: 'value' },
539
- });
540
- });
541
- });
542
-
543
- // Array Operator Normalization
544
- describe('Array Operator Normalization', () => {
545
- it('normalizes single values for $all', () => {
546
- expect(
547
- translator.translate({
548
- field: { $all: 'value' },
549
- }),
550
- ).toEqual({
551
- field: { $all: ['value'] },
552
- });
553
- });
554
-
555
- it('normalizes single values for $in', () => {
556
- expect(
557
- translator.translate({
558
- field: { $in: 'value' },
559
- }),
560
- ).toEqual({
561
- field: { $in: ['value'] },
562
- });
563
- });
564
-
565
- it('normalizes single values for $nin', () => {
566
- expect(
567
- translator.translate({
568
- field: { $nin: 'value' },
569
- }),
570
- ).toEqual({
571
- field: { $nin: ['value'] },
572
- });
573
- });
574
-
575
- it('preserves arrays for array operators', () => {
576
- expect(
577
- translator.translate({
578
- field: { $all: ['value1', 'value2'] },
579
- }),
580
- ).toEqual({
581
- field: { $all: ['value1', 'value2'] },
582
- });
583
- });
584
- });
585
-
586
- // Operator Support Validation
587
- describe('Operator Support Validation', () => {
588
- it('ensure all operator filters are supported', () => {
589
- const supportedFilters = [
590
- // Basic comparison operators
591
- { field: { $eq: 'value' } },
592
- { field: { $ne: 'value' } },
593
- { field: { $gt: 'value' } },
594
- { field: { $gte: 'value' } },
595
- { field: { $lt: 'value' } },
596
- { field: { $lte: 'value' } },
597
-
598
- // Array operators
599
- { field: { $in: ['value'] } },
600
- { field: { $nin: ['value'] } },
601
- { field: { $all: ['value'] } },
602
- { field: { $elemMatch: { $gt: 5 } } },
603
-
604
- // Pattern matching
605
- { field: { $regex: 'value' } },
606
- { field: { $contains: 'value' } },
607
-
608
- // Existence
609
- { field: { $exists: true } },
610
-
611
- { $and: [{ field1: 'value1' }, { field2: 'value2' }] },
612
- { $or: [{ field1: 'value1' }, { field2: 'value2' }] },
613
- { $nor: [{ field1: 'value1' }, { field2: 'value2' }] },
614
-
615
- { $and: { field: 'value' } },
616
- { $or: { field: 'value' } },
617
- { $nor: { field: 'value' } },
618
- { $not: { field: 'value' } },
619
-
620
- { $or: [{ $and: { field1: 'value1' } }, { $not: { field2: 'value2' } }] },
621
-
622
- { field: { $not: { $eq: 'value' } } },
623
- { field: { $not: { $in: ['value1', 'value2'] } } },
624
- { field: { $not: { $gt: 100 } } },
625
- { field: { $not: { $lt: 50 } } },
626
-
627
- { field: { $size: 1 } },
628
- ];
629
-
630
- supportedFilters.forEach(filter => {
631
- expect(() => translator.translate(filter)).not.toThrow();
632
- });
633
- });
634
-
635
- it('throws error for $not if not an object', () => {
636
- expect(() => translator.translate({ $not: 'value' })).toThrow();
637
- expect(() => translator.translate({ $not: [{ field: 'value' }] } as any)).toThrow();
638
- });
639
- it('throws error for $not if empty', () => {
640
- expect(() => translator.translate({ $not: {} })).toThrow();
641
- });
642
-
643
- // Add tests for logical operator placement restrictions
644
- it('throws error when logical operators are used in field-level conditions', () => {
645
- // $and cannot be used in field conditions
646
- expect(() =>
647
- translator.translate({
648
- field: { $and: [{ $eq: 'value1' }, { $eq: 'value2' }] } as any,
649
- }),
650
- ).toThrow();
651
-
652
- // $or cannot be used in field conditions
653
- expect(() =>
654
- translator.translate({
655
- field: { $or: [{ $eq: 'value1' }, { $eq: 'value2' }] } as any,
656
- }),
657
- ).toThrow();
658
-
659
- // $nor cannot be used in field conditions
660
- expect(() =>
661
- translator.translate({
662
- field: { $nor: [{ $eq: 'value1' }, { $eq: 'value2' }] } as any,
663
- }),
664
- ).toThrow();
665
- });
666
-
667
- it('allows logical operators at root level', () => {
668
- // Valid root level usage
669
- expect(() =>
670
- translator.translate({
671
- $and: [{ field1: 'value1' }, { field2: 'value2' }],
672
- $or: [{ field3: 'value3' }, { field4: 'value4' }],
673
- $nor: [{ field5: 'value5' }, { field6: 'value6' }],
674
- }),
675
- ).not.toThrow();
676
- });
677
-
678
- it('allows logical operators nested within other logical operators', () => {
679
- // Valid nested usage
680
- expect(() =>
681
- translator.translate({
682
- $and: [
683
- {
684
- $or: [{ field1: 'value1' }, { field2: 'value2' }],
685
- },
686
- {
687
- $nor: [{ field3: 'value3' }, { field4: 'value4' }],
688
- },
689
- ],
690
- }),
691
- ).not.toThrow();
692
- });
693
-
694
- it('allows $not in field-level conditions', () => {
695
- // $not is allowed in field conditions
696
- expect(() =>
697
- translator.translate({
698
- field1: { $not: { $eq: 'value1' } },
699
- field2: { $not: { $in: ['value2', 'value3'] } },
700
- field3: { $not: { $regex: 'pattern' } },
701
- }),
702
- ).not.toThrow();
703
- });
704
-
705
- it('throws error for deeply nested logical operators in non-logical contexts', () => {
706
- // Invalid deep nesting in comparison operators
707
- expect(() =>
708
- translator.translate({
709
- field: {
710
- $gt: {
711
- $or: [{ subfield: 'value1' }, { subfield: 'value2' }],
712
- },
713
- } as any,
714
- }),
715
- ).toThrow();
716
-
717
- // Invalid deep nesting in array operators
718
- expect(() =>
719
- translator.translate({
720
- field: {
721
- $in: [
722
- {
723
- $and: [{ subfield: 'value1' }, { subfield: 'value2' }],
724
- } as any,
725
- ],
726
- },
727
- }),
728
- ).toThrow();
729
- });
730
-
731
- it('validates $not operator structure', () => {
732
- // $not must be an object
733
- expect(() => translator.translate({ field: { $not: 'value' } } as any)).toThrow();
734
- expect(() => translator.translate({ field: { $not: ['value'] } })).toThrow();
735
- expect(() => translator.translate({ $not: 'value' })).toThrow();
736
- expect(() => translator.translate({ $not: ['value'] })).toThrow();
737
- });
738
-
739
- it('validates $not operator nesting', () => {
740
- // $not can be nested
741
- expect(() =>
742
- translator.translate({
743
- field: { $not: { $not: { $eq: 'value' } } },
744
- }),
745
- ).not.toThrow();
746
-
747
- // $not can be deeply nested
748
- expect(() =>
749
- translator.translate({
750
- field: { $not: { $not: { $not: { $eq: 'value' } } } },
751
- }),
752
- ).not.toThrow();
753
-
754
- // Cannot use $not with logical operators
755
- expect(() =>
756
- translator.translate({
757
- field: { $not: { $and: [{ $eq: 'value1' }, { $eq: 'value2' }] } },
758
- }),
759
- ).not.toThrow();
760
-
761
- expect(() =>
762
- translator.translate({
763
- field: { $not: { $or: [{ $eq: 'value1' }, { $eq: 'value2' }] } },
764
- }),
765
- ).not.toThrow();
766
-
767
- // $not can be used with comparison operators at any nesting level
768
- expect(() =>
769
- translator.translate({
770
- field: { $not: { $not: { $gt: 100 } } },
771
- field2: { $not: { $not: { $lt: 50 } } },
772
- }),
773
- ).not.toThrow();
774
- });
775
-
776
- it('allows $not with comparison operators', () => {
777
- expect(() =>
778
- translator.translate({
779
- field: { $not: { $eq: 'value' } },
780
- field2: { $not: { $gt: 100 } },
781
- field3: { $not: { $lt: 50 } },
782
- field4: { $not: { $in: ['value1', 'value2'] } },
783
- }),
784
- ).not.toThrow();
785
- });
786
-
787
- it('validates empty $not conditions', () => {
788
- expect(() => translator.translate({ field: { $not: {} } })).toThrow();
789
- expect(() => translator.translate({ $not: {} })).toThrow();
790
- });
791
-
792
- it('throws error for non-logical operators at top level', () => {
793
- const invalidFilters: any = [{ $gt: 100 }, { $in: ['value1', 'value2'] }, { $eq: true }];
794
-
795
- invalidFilters.forEach(filter => {
796
- expect(() => translator.translate(filter)).toThrow(/Invalid top-level operator/);
797
- });
798
- });
799
- it('allows logical operators at top level', () => {
800
- const validFilters = [{ $and: [{ field: 'value' }] }, { $or: [{ field: 'value' }] }];
801
-
802
- validFilters.forEach(filter => {
803
- expect(() => translator.translate(filter)).not.toThrow();
804
- });
805
- });
806
-
807
- it('validates $elemMatch requires an object with conditions', () => {
808
- // Should throw for non-object values
809
- expect(() =>
810
- translator.translate({
811
- field: { $elemMatch: 'value' } as any,
812
- }),
813
- ).toThrow('$elemMatch requires an object with conditions');
814
-
815
- expect(() =>
816
- translator.translate({
817
- field: { $elemMatch: ['value'] } as any,
818
- }),
819
- ).toThrow('$elemMatch requires an object with conditions');
820
- });
821
- });
822
-
823
- // Regular Expression Support
824
- describe('Regular Expression Support', () => {
825
- it('translates string pattern', () => {
826
- expect(
827
- translator.translate({
828
- field: { $regex: 'pattern' },
829
- }),
830
- ).toEqual({
831
- field: { $regex: 'pattern' },
832
- });
833
- });
834
-
835
- it('translates regex with options', () => {
836
- expect(
837
- translator.translate({
838
- field: { $regex: 'pattern', $options: 'i' },
839
- }),
840
- ).toEqual({
841
- field: { $regex: '(?i)pattern' },
842
- });
843
- });
844
-
845
- it('translates regex literal', () => {
846
- expect(
847
- translator.translate({
848
- field: /pattern/i,
849
- }),
850
- ).toEqual({
851
- field: { $regex: '(?i)pattern' },
852
- });
853
- });
854
-
855
- it('translates starts with pattern', () => {
856
- expect(
857
- translator.translate({
858
- field: { $regex: '^start' },
859
- }),
860
- ).toEqual({
861
- field: { $regex: '^start' },
862
- });
863
- });
864
-
865
- it('translates ends with pattern', () => {
866
- expect(
867
- translator.translate({
868
- field: { $regex: 'end$' },
869
- }),
870
- ).toEqual({
871
- field: { $regex: 'end$' },
872
- });
873
- });
874
-
875
- it('translates exact match pattern', () => {
876
- expect(
877
- translator.translate({
878
- field: { $regex: '^exact$' },
879
- }),
880
- ).toEqual({
881
- field: { $regex: '^exact$' },
882
- });
883
- });
884
-
885
- it('translates contains pattern', () => {
886
- expect(
887
- translator.translate({
888
- field: { $regex: 'contains' },
889
- }),
890
- ).toEqual({
891
- field: { $regex: 'contains' },
892
- });
893
- });
894
-
895
- it('supports complex patterns', () => {
896
- expect(
897
- translator.translate({
898
- field: { $regex: 'pat.*ern' },
899
- }),
900
- ).toEqual({
901
- field: { $regex: 'pat.*ern' },
902
- });
903
- });
904
-
905
- it('combines multiple regex flags', () => {
906
- expect(
907
- translator.translate({
908
- field: { $regex: 'pattern', $options: 'imsx' },
909
- }),
910
- ).toEqual({
911
- field: { $regex: '(?imsx)pattern' },
912
- });
913
- });
914
-
915
- it('handles nested regex patterns', () => {
916
- expect(
917
- translator.translate({
918
- nested: {
919
- field: { $regex: 'pattern', $options: 'i' },
920
- } as any,
921
- }),
922
- ).toEqual({
923
- 'nested.field': { $regex: '(?i)pattern' },
924
- });
925
- });
926
-
927
- it('handles multiple regex patterns', () => {
928
- expect(
929
- translator.translate({
930
- field1: { $regex: 'pattern1' },
931
- field2: { $regex: 'pattern2', $options: 'i' },
932
- }),
933
- ).toEqual({
934
- field1: { $regex: 'pattern1' },
935
- field2: { $regex: '(?i)pattern2' },
936
- });
937
- });
938
-
939
- it('handles regex in logical operators', () => {
940
- expect(
941
- translator.translate({
942
- $or: [{ field: { $regex: 'pattern1' } }, { field: { $regex: 'pattern2', $options: 'i' } }],
943
- }),
944
- ).toEqual({
945
- $or: [{ field: { $regex: 'pattern1' } }, { field: { $regex: '(?i)pattern2' } }],
946
- });
947
- });
948
-
949
- it('handles deeply nested paths with regex', () => {
950
- expect(
951
- translator.translate({
952
- 'level1.level2.level3': { $regex: 'pattern', $options: 'i' },
953
- }),
954
- ).toEqual({
955
- 'level1.level2.level3': { $regex: '(?i)pattern' },
956
- });
957
- });
958
-
959
- it('throws on $options without $regex', () => {
960
- expect(() =>
961
- translator.translate({
962
- field: { $options: 'i' },
963
- }),
964
- ).toThrow();
965
- });
966
- });
967
- });