@better-auth/test-utils 1.5.5 → 1.5.7-beta.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.
@@ -0,0 +1,2923 @@
1
+ import { createTestSuite } from "../create-test-suite.mjs";
2
+ import { expect } from "vitest";
3
+ import { organization } from "better-auth/plugins/organization";
4
+ //#region src/adapter/suites/basic.ts
5
+ /**
6
+ * This test suite tests the basic CRUD operations of the adapter.
7
+ */
8
+ const normalTestSuite = createTestSuite("normal", {}, (helpers, debugTools) => {
9
+ const tests = getNormalTestSuiteTests(helpers, debugTools);
10
+ return {
11
+ "init - tests": async () => {
12
+ expect(helpers.getBetterAuthOptions().advanced?.database?.generateId !== "serial").toBeTruthy();
13
+ },
14
+ ...tests
15
+ };
16
+ });
17
+ const getNormalTestSuiteTests = ({ adapter, generate, insertRandom, modifyBetterAuthOptions, sortModels, customIdGenerator, getBetterAuthOptions, transformGeneratedModel, transformIdOutput }, debugTools) => {
18
+ return {
19
+ "create - should create a model": async () => {
20
+ const user = await generate("user");
21
+ const result = await adapter.create({
22
+ model: "user",
23
+ data: user,
24
+ forceAllowId: true
25
+ });
26
+ const options = getBetterAuthOptions();
27
+ if (options.advanced?.database?.generateId === "serial" || options.advanced?.database?.generateId === "uuid") user.id = result.id;
28
+ expect(typeof result.id).toEqual("string");
29
+ const transformed = transformGeneratedModel(user);
30
+ expect(result).toEqual(transformed);
31
+ },
32
+ "create - should always return an id": async () => {
33
+ const { id: _, ...user } = await generate("user");
34
+ const res = await adapter.create({
35
+ model: "user",
36
+ data: user
37
+ });
38
+ expect(res).toHaveProperty("id");
39
+ expect(typeof res.id).toEqual("string");
40
+ },
41
+ "create - should use generateId if provided": async () => {
42
+ const ID = await customIdGenerator?.() || "MOCK-ID";
43
+ await modifyBetterAuthOptions({ advanced: { database: { generateId: () => ID } } }, false);
44
+ const { id: _, ...user } = await generate("user");
45
+ const res = await adapter.create({
46
+ model: "user",
47
+ data: user
48
+ });
49
+ expect(res.id).toEqual(transformIdOutput ? transformIdOutput(ID) : ID);
50
+ expect(await adapter.findOne({
51
+ model: "user",
52
+ where: [{
53
+ field: "id",
54
+ value: res.id
55
+ }]
56
+ })).toEqual(res);
57
+ },
58
+ "create - should return null for nullable foreign keys": {
59
+ migrateBetterAuth: { plugins: [{
60
+ id: "nullable-test",
61
+ schema: { testModel: { fields: { nullableReference: {
62
+ type: "string",
63
+ references: {
64
+ field: "id",
65
+ model: "user"
66
+ },
67
+ required: false
68
+ } } } }
69
+ }] },
70
+ test: async () => {
71
+ const { nullableReference } = await adapter.create({
72
+ model: "testModel",
73
+ data: { nullableReference: null },
74
+ forceAllowId: true
75
+ });
76
+ expect(nullableReference).toBeNull();
77
+ }
78
+ },
79
+ "create - should apply default values to fields": async () => {
80
+ await modifyBetterAuthOptions({
81
+ user: { additionalFields: {
82
+ testField: {
83
+ type: "string",
84
+ defaultValue: "test-value"
85
+ },
86
+ cbDefaultValueField: {
87
+ type: "string",
88
+ defaultValue: () => {
89
+ return "advanced-test-value";
90
+ }
91
+ }
92
+ } },
93
+ plugins: [{
94
+ id: "default-fields-test",
95
+ schema: { testModel: { fields: {
96
+ testField: {
97
+ type: "string",
98
+ defaultValue: "test-value"
99
+ },
100
+ cbDefaultValueField: {
101
+ type: "string",
102
+ defaultValue: () => {
103
+ return "advanced-test-value";
104
+ }
105
+ }
106
+ } } }
107
+ }]
108
+ }, true);
109
+ const result = await adapter.create({
110
+ model: "testModel",
111
+ data: {}
112
+ });
113
+ expect(result.id).toBeDefined();
114
+ expect(result.id).toBeTypeOf("string");
115
+ expect(result.testField).toBe("test-value");
116
+ expect(result.cbDefaultValueField).toBe("advanced-test-value");
117
+ const userResult = await adapter.create({
118
+ model: "user",
119
+ data: { ...await generate("user") },
120
+ forceAllowId: true
121
+ });
122
+ expect(userResult).toBeDefined();
123
+ expect(userResult?.testField).toBe("test-value");
124
+ expect(userResult?.cbDefaultValueField).toBe("advanced-test-value");
125
+ },
126
+ "findOne - should find a model": async () => {
127
+ const [user] = await insertRandom("user");
128
+ expect(await adapter.findOne({
129
+ model: "user",
130
+ where: [{
131
+ field: "id",
132
+ value: user.id
133
+ }]
134
+ })).toEqual(user);
135
+ },
136
+ "findOne - should not apply defaultValue if value not found": async () => {
137
+ await modifyBetterAuthOptions({
138
+ user: { additionalFields: {
139
+ testField: {
140
+ type: "string",
141
+ required: false,
142
+ defaultValue: "test-value"
143
+ },
144
+ cbDefaultValueField: {
145
+ type: "string",
146
+ required: false,
147
+ defaultValue: () => {
148
+ return "advanced-test-value";
149
+ }
150
+ }
151
+ } },
152
+ plugins: [{
153
+ id: "default-fields-test",
154
+ schema: { testModel: { fields: {
155
+ testField: {
156
+ type: "string",
157
+ required: false,
158
+ defaultValue: "test-value"
159
+ },
160
+ cbDefaultValueField: {
161
+ type: "string",
162
+ required: false,
163
+ defaultValue: () => {
164
+ return "advanced-test-value";
165
+ }
166
+ }
167
+ } } }
168
+ }]
169
+ }, true);
170
+ const first = await adapter.create({
171
+ model: "testModel",
172
+ data: {
173
+ testField: null,
174
+ cbDefaultValueField: null
175
+ }
176
+ });
177
+ const second = await adapter.create({
178
+ model: "user",
179
+ data: {
180
+ ...await generate("user"),
181
+ testField: null,
182
+ cbDefaultValueField: null
183
+ },
184
+ forceAllowId: true
185
+ });
186
+ const result = await adapter.findOne({
187
+ model: "testModel",
188
+ where: [{
189
+ field: "id",
190
+ value: first.id
191
+ }]
192
+ });
193
+ expect(result).not.toBeNull();
194
+ expect(result?.testField).toBeNull();
195
+ expect(result?.cbDefaultValueField).toBeNull();
196
+ const resultTwo = await adapter.findMany({
197
+ model: "user",
198
+ where: [{
199
+ field: "id",
200
+ value: second.id
201
+ }]
202
+ });
203
+ expect(resultTwo).not.toBeNull();
204
+ expect(resultTwo.length).toBe(1);
205
+ expect(resultTwo[0]?.testField).toBeNull();
206
+ expect(resultTwo[0]?.cbDefaultValueField).toBeNull();
207
+ },
208
+ "findOne - should find a model using a reference field": async () => {
209
+ const [user, session] = await insertRandom("session");
210
+ expect(await adapter.findOne({
211
+ model: "session",
212
+ where: [{
213
+ field: "userId",
214
+ value: user.id
215
+ }]
216
+ })).toEqual(session);
217
+ },
218
+ "findOne - should not throw on record not found": async () => {
219
+ const useUUIDs = getBetterAuthOptions().advanced?.database?.generateId === "uuid";
220
+ expect(await adapter.findOne({
221
+ model: "user",
222
+ where: [{
223
+ field: "id",
224
+ value: useUUIDs ? crypto.randomUUID() : "100000"
225
+ }]
226
+ })).toBeNull();
227
+ },
228
+ "findOne - should find a model without id": async () => {
229
+ const [user] = await insertRandom("user");
230
+ expect(await adapter.findOne({
231
+ model: "user",
232
+ where: [{
233
+ field: "email",
234
+ value: user.email
235
+ }]
236
+ })).toEqual(user);
237
+ },
238
+ "findOne - should find a model with join": async () => {
239
+ const users = [];
240
+ const sessions = [];
241
+ const accounts = [];
242
+ for (const _ of Array.from({ length: 3 })) {
243
+ const user = await adapter.create({
244
+ model: "user",
245
+ data: { ...await generate("user") },
246
+ forceAllowId: true
247
+ });
248
+ users.push(user);
249
+ const userId = users[0].id;
250
+ const session = await adapter.create({
251
+ model: "session",
252
+ data: {
253
+ ...await generate("session"),
254
+ userId
255
+ },
256
+ forceAllowId: true
257
+ });
258
+ sessions.push(session);
259
+ const account = await adapter.create({
260
+ model: "account",
261
+ data: {
262
+ ...await generate("account"),
263
+ userId
264
+ },
265
+ forceAllowId: true
266
+ });
267
+ accounts.push(account);
268
+ }
269
+ const result = await adapter.findOne({
270
+ model: "user",
271
+ where: [{
272
+ field: "id",
273
+ value: users[0].id
274
+ }],
275
+ join: {
276
+ session: true,
277
+ account: true
278
+ }
279
+ });
280
+ expect({
281
+ ...result,
282
+ session: result?.session.sort((a, b) => a.id.localeCompare(b.id)),
283
+ account: result?.account.sort((a, b) => a.id.localeCompare(b.id))
284
+ }).toEqual({
285
+ ...users[0],
286
+ session: sessions.sort((a, b) => a.id.localeCompare(b.id)),
287
+ account: accounts.sort((a, b) => a.id.localeCompare(b.id))
288
+ });
289
+ },
290
+ "findOne - should find a model with modified field name": async () => {
291
+ await modifyBetterAuthOptions({ user: { fields: { email: "email_address" } } }, true);
292
+ const [user] = await insertRandom("user");
293
+ const result = await adapter.findOne({
294
+ model: "user",
295
+ where: [{
296
+ field: "email",
297
+ value: user.email
298
+ }]
299
+ });
300
+ expect(result).toEqual(user);
301
+ expect(result?.email).toEqual(user.email);
302
+ expect(true).toEqual(true);
303
+ },
304
+ "findOne - should find a model with modified model name": async () => {
305
+ await modifyBetterAuthOptions({ user: { modelName: "user_custom" } }, true);
306
+ const [user] = await insertRandom("user");
307
+ expect(user).toBeDefined();
308
+ expect(user).toHaveProperty("id");
309
+ expect(user).toHaveProperty("name");
310
+ const result = await adapter.findOne({
311
+ model: "user",
312
+ where: [{
313
+ field: "email",
314
+ value: user.email
315
+ }]
316
+ });
317
+ expect(result).toEqual(user);
318
+ expect(result?.email).toEqual(user.email);
319
+ expect(true).toEqual(true);
320
+ },
321
+ "findOne - should find a model with additional fields": async () => {
322
+ await modifyBetterAuthOptions({ user: { additionalFields: { customField: {
323
+ type: "string",
324
+ input: false,
325
+ required: true,
326
+ defaultValue: "default-value"
327
+ } } } }, true);
328
+ const [user_] = await insertRandom("user");
329
+ const user = user_;
330
+ expect(user).toHaveProperty("customField");
331
+ expect(user.customField).toBe("default-value");
332
+ const result = await adapter.findOne({
333
+ model: "user",
334
+ where: [{
335
+ field: "customField",
336
+ value: user.customField
337
+ }]
338
+ });
339
+ expect(result).toEqual(user);
340
+ expect(result?.customField).toEqual("default-value");
341
+ },
342
+ "findOne - should select fields": async () => {
343
+ const [user] = await insertRandom("user");
344
+ expect(await adapter.findOne({
345
+ model: "user",
346
+ where: [{
347
+ field: "id",
348
+ value: user.id
349
+ }],
350
+ select: ["email", "name"]
351
+ })).toEqual({
352
+ email: user.email,
353
+ name: user.name
354
+ });
355
+ },
356
+ "findOne - should select fields with one-to-many join": async () => {
357
+ const user = await adapter.create({
358
+ model: "user",
359
+ data: { ...await generate("user") },
360
+ forceAllowId: true
361
+ });
362
+ const session = await adapter.create({
363
+ model: "session",
364
+ data: {
365
+ ...await generate("session"),
366
+ userId: user.id
367
+ },
368
+ forceAllowId: true
369
+ });
370
+ const result = await adapter.findOne({
371
+ model: "user",
372
+ where: [{
373
+ field: "id",
374
+ value: user.id
375
+ }],
376
+ select: ["email", "name"],
377
+ join: { session: true }
378
+ });
379
+ expect(result).toBeDefined();
380
+ expect(result?.email).toEqual(user.email);
381
+ expect(result?.name).toEqual(user.name);
382
+ expect(result?.session).toBeDefined();
383
+ expect(Array.isArray(result?.session)).toBe(true);
384
+ expect(result?.session).toHaveLength(1);
385
+ expect(result?.session[0]).toEqual(session);
386
+ },
387
+ "findOne - should select fields with one-to-one join": async () => {
388
+ await modifyBetterAuthOptions({ plugins: [{
389
+ id: "one-to-one-test",
390
+ schema: { oneToOneTable: { fields: { oneToOne: {
391
+ type: "string",
392
+ required: true,
393
+ references: {
394
+ field: "id",
395
+ model: "user"
396
+ },
397
+ unique: true
398
+ } } } }
399
+ }] }, true);
400
+ const user = await adapter.create({
401
+ model: "user",
402
+ data: { ...await generate("user") },
403
+ forceAllowId: true
404
+ });
405
+ const oneToOne = await adapter.create({
406
+ model: "oneToOneTable",
407
+ data: { oneToOne: user.id }
408
+ });
409
+ const result = await adapter.findOne({
410
+ model: "user",
411
+ where: [{
412
+ field: "id",
413
+ value: user.id
414
+ }],
415
+ select: ["email", "name"],
416
+ join: { oneToOneTable: true }
417
+ });
418
+ expect(result).toBeDefined();
419
+ expect(result?.email).toEqual(user.email);
420
+ expect(result?.name).toEqual(user.name);
421
+ expect(result?.oneToOneTable).toBeDefined();
422
+ expect(result?.oneToOneTable).toEqual(oneToOne);
423
+ },
424
+ "findOne - should select fields with multiple joins": async () => {
425
+ const user = await adapter.create({
426
+ model: "user",
427
+ data: { ...await generate("user") },
428
+ forceAllowId: true
429
+ });
430
+ const session = await adapter.create({
431
+ model: "session",
432
+ data: {
433
+ ...await generate("session"),
434
+ userId: user.id
435
+ },
436
+ forceAllowId: true
437
+ });
438
+ const account = await adapter.create({
439
+ model: "account",
440
+ data: {
441
+ ...await generate("account"),
442
+ userId: user.id
443
+ },
444
+ forceAllowId: true
445
+ });
446
+ const result = await adapter.findOne({
447
+ model: "user",
448
+ where: [{
449
+ field: "id",
450
+ value: user.id
451
+ }],
452
+ select: ["email", "name"],
453
+ join: {
454
+ session: true,
455
+ account: true
456
+ }
457
+ });
458
+ expect(result).toBeDefined();
459
+ expect(result?.email).toEqual(user.email);
460
+ expect(result?.name).toEqual(user.name);
461
+ expect(result?.session).toBeDefined();
462
+ expect(Array.isArray(result?.session)).toBe(true);
463
+ expect(result?.session).toHaveLength(1);
464
+ expect(result?.session[0]).toEqual(session);
465
+ expect(result?.account).toBeDefined();
466
+ expect(Array.isArray(result?.account)).toBe(true);
467
+ expect(result?.account).toHaveLength(1);
468
+ expect(result?.account[0]).toEqual(account);
469
+ },
470
+ "findOne - should find model with date field": async () => {
471
+ const [user] = await insertRandom("user");
472
+ const result = await adapter.findOne({
473
+ model: "user",
474
+ where: [{
475
+ field: "createdAt",
476
+ value: user.createdAt,
477
+ operator: "eq"
478
+ }]
479
+ });
480
+ expect(result).toEqual(user);
481
+ expect(result?.createdAt).toBeInstanceOf(Date);
482
+ expect(result?.createdAt).toEqual(user.createdAt);
483
+ },
484
+ "findOne - should perform backwards joins": async () => {
485
+ const user = await adapter.create({
486
+ model: "user",
487
+ data: { ...await generate("user") },
488
+ forceAllowId: true
489
+ });
490
+ const session = await adapter.create({
491
+ model: "session",
492
+ data: {
493
+ ...await generate("session"),
494
+ userId: user.id
495
+ },
496
+ forceAllowId: true
497
+ });
498
+ expect(await adapter.findOne({
499
+ model: "session",
500
+ where: [{
501
+ field: "token",
502
+ value: session.token
503
+ }],
504
+ join: { user: true }
505
+ })).toEqual({
506
+ ...session,
507
+ user
508
+ });
509
+ },
510
+ "findOne - should return an object for one-to-one joins": async () => {
511
+ await modifyBetterAuthOptions({ plugins: [{
512
+ id: "one-to-one-test",
513
+ schema: { oneToOneTable: { fields: { oneToOne: {
514
+ type: "string",
515
+ required: true,
516
+ references: {
517
+ field: "id",
518
+ model: "user"
519
+ },
520
+ unique: true
521
+ } } } }
522
+ }] }, true);
523
+ const users = (await insertRandom("user", 2)).map((x) => x[0]);
524
+ const oneToOne = await adapter.create({
525
+ model: "oneToOneTable",
526
+ data: { oneToOne: users[0].id }
527
+ });
528
+ await adapter.create({
529
+ model: "oneToOneTable",
530
+ data: { oneToOne: users[1].id }
531
+ });
532
+ expect(await adapter.findOne({
533
+ model: "user",
534
+ where: [{
535
+ field: "id",
536
+ value: users[0].id
537
+ }],
538
+ join: { oneToOneTable: true }
539
+ })).toEqual({
540
+ ...users[0],
541
+ oneToOneTable: oneToOne
542
+ });
543
+ },
544
+ "findOne - should return an array for one-to-many joins": async () => {
545
+ const user = await adapter.create({
546
+ model: "user",
547
+ data: { ...await generate("user") },
548
+ forceAllowId: true
549
+ });
550
+ const session = await adapter.create({
551
+ model: "session",
552
+ data: {
553
+ ...await generate("session"),
554
+ userId: user.id
555
+ },
556
+ forceAllowId: true
557
+ });
558
+ expect(await adapter.findOne({
559
+ model: "user",
560
+ where: [{
561
+ field: "id",
562
+ value: user.id
563
+ }],
564
+ join: { session: true }
565
+ })).toEqual({
566
+ ...user,
567
+ session: [session]
568
+ });
569
+ },
570
+ "findOne - should work with both one-to-one and one-to-many joins": async () => {
571
+ await modifyBetterAuthOptions({ plugins: [{
572
+ id: "one-to-one-test",
573
+ schema: { oneToOneTable: { fields: { oneToOne: {
574
+ type: "string",
575
+ required: true,
576
+ references: {
577
+ field: "id",
578
+ model: "user"
579
+ },
580
+ unique: true
581
+ } } } }
582
+ }] }, true);
583
+ const users = (await insertRandom("user", 2)).map((x) => x[0]);
584
+ const oneToOne = await adapter.create({
585
+ model: "oneToOneTable",
586
+ data: { oneToOne: users[0].id }
587
+ });
588
+ const session1 = await adapter.create({
589
+ model: "session",
590
+ data: {
591
+ ...await generate("session"),
592
+ userId: users[0].id,
593
+ createdAt: /* @__PURE__ */ new Date(Date.now() - 3e3)
594
+ },
595
+ forceAllowId: true
596
+ });
597
+ const session2 = await adapter.create({
598
+ model: "session",
599
+ data: {
600
+ ...await generate("session"),
601
+ userId: users[0].id,
602
+ createdAt: /* @__PURE__ */ new Date(Date.now() - 1e3)
603
+ },
604
+ forceAllowId: true
605
+ });
606
+ const result = await adapter.findOne({
607
+ model: "user",
608
+ where: [{
609
+ field: "id",
610
+ value: users[0].id
611
+ }],
612
+ join: {
613
+ oneToOneTable: true,
614
+ session: true
615
+ }
616
+ });
617
+ if (result?.session?.length) result.session = result.session.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
618
+ expect(result).toEqual({
619
+ ...users[0],
620
+ oneToOneTable: oneToOne,
621
+ session: [session1, session2]
622
+ });
623
+ },
624
+ "findOne - should return null for failed base model lookup that has joins": async () => {
625
+ await modifyBetterAuthOptions({ plugins: [{
626
+ id: "one-to-one-test",
627
+ schema: { oneToOneTable: {
628
+ modelName: "one_to_one_table",
629
+ fields: { oneToOne: {
630
+ type: "string",
631
+ required: true,
632
+ references: {
633
+ field: "id",
634
+ model: "user"
635
+ },
636
+ unique: true
637
+ } }
638
+ } }
639
+ }] }, true);
640
+ const useUUIDs = getBetterAuthOptions().advanced?.database?.generateId === "uuid";
641
+ expect(await adapter.findOne({
642
+ model: "user",
643
+ where: [{
644
+ field: "id",
645
+ value: useUUIDs ? crypto.randomUUID() : "100000"
646
+ }],
647
+ join: {
648
+ session: true,
649
+ account: true,
650
+ oneToOneTable: true
651
+ }
652
+ })).toBeNull();
653
+ },
654
+ "findOne - should join a model with modified field name": async () => {
655
+ await modifyBetterAuthOptions({
656
+ user: { fields: { email: "email_address" } },
657
+ plugins: [{
658
+ id: "one-to-one-test",
659
+ schema: { oneToOneTable: {
660
+ modelName: "one_to_one_table",
661
+ fields: { oneToOne: {
662
+ type: "string",
663
+ required: true,
664
+ references: {
665
+ field: "email",
666
+ model: "user"
667
+ },
668
+ unique: true,
669
+ fieldName: "one_to_one"
670
+ } }
671
+ } }
672
+ }]
673
+ }, true);
674
+ const user = await adapter.create({
675
+ model: "user",
676
+ data: { ...await generate("user") },
677
+ forceAllowId: true
678
+ });
679
+ const oneToOne = await adapter.create({
680
+ model: "oneToOneTable",
681
+ data: { oneToOne: user.email }
682
+ });
683
+ expect(await adapter.findOne({
684
+ model: "user",
685
+ where: [{
686
+ field: "email",
687
+ value: user.email
688
+ }],
689
+ join: { oneToOneTable: true }
690
+ })).toEqual({
691
+ ...user,
692
+ oneToOneTable: oneToOne
693
+ });
694
+ },
695
+ "findMany - should find many models": async () => {
696
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
697
+ expect(sortModels(await adapter.findMany({ model: "user" }))).toEqual(sortModels(users));
698
+ },
699
+ "findMany - should find many models with date fields": async () => {
700
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
701
+ const youngestUser = users.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0];
702
+ expect(sortModels(await adapter.findMany({
703
+ model: "user",
704
+ where: [{
705
+ field: "createdAt",
706
+ value: youngestUser.createdAt,
707
+ operator: "lt"
708
+ }]
709
+ }))).toEqual(sortModels(users.filter((user) => user.createdAt < youngestUser.createdAt)));
710
+ },
711
+ "findMany - should find many models with join": async () => {
712
+ let expectedResult = [];
713
+ for (let i = 0; i < 10; i++) {
714
+ const user = await adapter.create({
715
+ model: "user",
716
+ data: {
717
+ ...await generate("user"),
718
+ ...i < 3 ? { name: `join-user-${i}` } : {}
719
+ },
720
+ forceAllowId: true
721
+ });
722
+ const sessions = [];
723
+ for (let index = 0; index < 3; index++) {
724
+ const session = await adapter.create({
725
+ model: "session",
726
+ data: {
727
+ ...await generate("session"),
728
+ userId: user.id
729
+ },
730
+ forceAllowId: true
731
+ });
732
+ sessions.push(session);
733
+ }
734
+ const accounts = [];
735
+ for (let index = 0; index < 3; index++) {
736
+ const account = await adapter.create({
737
+ model: "account",
738
+ data: {
739
+ ...await generate("account"),
740
+ userId: user.id
741
+ },
742
+ forceAllowId: true
743
+ });
744
+ accounts.push(account);
745
+ }
746
+ if (i < 3) expectedResult.push({
747
+ ...user,
748
+ session: sessions,
749
+ account: accounts
750
+ });
751
+ }
752
+ let result = await adapter.findMany({
753
+ model: "user",
754
+ where: [{
755
+ field: "name",
756
+ value: "join-user",
757
+ operator: "starts_with"
758
+ }],
759
+ join: {
760
+ session: true,
761
+ account: true
762
+ }
763
+ });
764
+ const sort = (a, b) => a.id.localeCompare(b.id);
765
+ result = result.sort(sort);
766
+ result = result.map((x) => ({
767
+ ...x,
768
+ session: x.session.sort((a, b) => a.id.localeCompare(b.id)),
769
+ account: x.account.sort((a, b) => a.id.localeCompare(b.id))
770
+ }));
771
+ expectedResult = expectedResult.sort(sort);
772
+ expectedResult = expectedResult.map((x) => ({
773
+ ...x,
774
+ session: x.session.sort((a, b) => a.id.localeCompare(b.id)),
775
+ account: x.account.sort((a, b) => a.id.localeCompare(b.id))
776
+ }));
777
+ expect(result).toEqual(expectedResult);
778
+ },
779
+ "findMany - should find many with join and limit": async () => {
780
+ const users = [];
781
+ const sessionsByUser = /* @__PURE__ */ new Map();
782
+ for (let i = 0; i < 5; i++) {
783
+ const user = await adapter.create({
784
+ model: "user",
785
+ data: { ...await generate("user") },
786
+ forceAllowId: true
787
+ });
788
+ users.push(user);
789
+ sessionsByUser.set(user.id, []);
790
+ for (let j = 0; j < 3; j++) {
791
+ const session = await adapter.create({
792
+ model: "session",
793
+ data: {
794
+ ...await generate("session"),
795
+ userId: user.id
796
+ },
797
+ forceAllowId: true
798
+ });
799
+ sessionsByUser.get(user.id).push(session);
800
+ }
801
+ }
802
+ const result = await adapter.findMany({
803
+ model: "user",
804
+ join: { session: true },
805
+ limit: 2
806
+ });
807
+ expect(result).toHaveLength(2);
808
+ result.forEach((user) => {
809
+ expect(user.session).toHaveLength(3);
810
+ });
811
+ },
812
+ "findMany - should select fields": async () => {
813
+ const expectedResults = (await insertRandom("user", 3)).map(([{ id, email }]) => ({
814
+ id,
815
+ email
816
+ }));
817
+ const select = ["id", "email"];
818
+ const result = await adapter.findMany({
819
+ model: "user",
820
+ where: [{
821
+ field: "id",
822
+ value: expectedResults.map((r) => r.id),
823
+ operator: "in"
824
+ }],
825
+ select
826
+ });
827
+ expect(result.length).toEqual(expectedResults.length);
828
+ expect(result[0]).toSatisfy((obj) => expectedResults.some((r) => r.id === obj.id && r.email === obj.email) && Object.entries(obj).every(([k, v]) => select.includes(k) || v === void 0));
829
+ },
830
+ "findMany - should select fields with one-to-many join": async () => {
831
+ const user = await adapter.create({
832
+ model: "user",
833
+ data: { ...await generate("user") },
834
+ forceAllowId: true
835
+ });
836
+ const session = await adapter.create({
837
+ model: "session",
838
+ data: {
839
+ ...await generate("session"),
840
+ userId: user.id
841
+ },
842
+ forceAllowId: true
843
+ });
844
+ const [result] = await adapter.findMany({
845
+ model: "user",
846
+ where: [{
847
+ field: "id",
848
+ value: user.id
849
+ }],
850
+ select: ["email", "name"],
851
+ join: { session: true }
852
+ });
853
+ expect(result).toBeDefined();
854
+ expect(result?.email).toEqual(user.email);
855
+ expect(result?.name).toEqual(user.name);
856
+ expect(result?.session).toBeDefined();
857
+ expect(Array.isArray(result?.session)).toBe(true);
858
+ expect(result?.session).toHaveLength(1);
859
+ expect(result?.session[0]).toEqual(session);
860
+ },
861
+ "findMany - should select fields with one-to-one join": async () => {
862
+ await modifyBetterAuthOptions({ plugins: [{
863
+ id: "one-to-one-test",
864
+ schema: { oneToOneTable: { fields: { oneToOne: {
865
+ type: "string",
866
+ required: true,
867
+ references: {
868
+ field: "id",
869
+ model: "user"
870
+ },
871
+ unique: true
872
+ } } } }
873
+ }] }, true);
874
+ const user = await adapter.create({
875
+ model: "user",
876
+ data: { ...await generate("user") },
877
+ forceAllowId: true
878
+ });
879
+ const oneToOne = await adapter.create({
880
+ model: "oneToOneTable",
881
+ data: { oneToOne: user.id }
882
+ });
883
+ const [result] = await adapter.findMany({
884
+ model: "user",
885
+ where: [{
886
+ field: "id",
887
+ value: user.id
888
+ }],
889
+ select: ["email", "name"],
890
+ join: { oneToOneTable: true }
891
+ });
892
+ expect(result).toBeDefined();
893
+ expect(result?.email).toEqual(user.email);
894
+ expect(result?.name).toEqual(user.name);
895
+ expect(result?.oneToOneTable).toBeDefined();
896
+ expect(result?.oneToOneTable).toEqual(oneToOne);
897
+ },
898
+ "findMany - should select fields with multiple joins": async () => {
899
+ const user = await adapter.create({
900
+ model: "user",
901
+ data: { ...await generate("user") },
902
+ forceAllowId: true
903
+ });
904
+ const session = await adapter.create({
905
+ model: "session",
906
+ data: {
907
+ ...await generate("session"),
908
+ userId: user.id
909
+ },
910
+ forceAllowId: true
911
+ });
912
+ const account = await adapter.create({
913
+ model: "account",
914
+ data: {
915
+ ...await generate("account"),
916
+ userId: user.id
917
+ },
918
+ forceAllowId: true
919
+ });
920
+ const [result] = await adapter.findMany({
921
+ model: "user",
922
+ where: [{
923
+ field: "id",
924
+ value: user.id
925
+ }],
926
+ select: ["email", "name"],
927
+ join: {
928
+ session: true,
929
+ account: true
930
+ }
931
+ });
932
+ expect(result).toBeDefined();
933
+ expect(result?.email).toEqual(user.email);
934
+ expect(result?.name).toEqual(user.name);
935
+ expect(result?.session).toBeDefined();
936
+ expect(Array.isArray(result?.session)).toBe(true);
937
+ expect(result?.session).toHaveLength(1);
938
+ expect(result?.session[0]).toEqual(session);
939
+ expect(result?.account).toBeDefined();
940
+ expect(Array.isArray(result?.account)).toBe(true);
941
+ expect(result?.account).toHaveLength(1);
942
+ expect(result?.account[0]).toEqual(account);
943
+ },
944
+ "findMany - should find many with join and offset": async () => {
945
+ const users = [];
946
+ for (let i = 0; i < 5; i++) {
947
+ const user = await adapter.create({
948
+ model: "user",
949
+ data: {
950
+ ...await generate("user"),
951
+ name: `user-${i.toString().padStart(2, "0")}`
952
+ },
953
+ forceAllowId: true
954
+ });
955
+ users.push(user);
956
+ for (let j = 0; j < 2; j++) await adapter.create({
957
+ model: "session",
958
+ data: {
959
+ ...await generate("session"),
960
+ userId: user.id
961
+ },
962
+ forceAllowId: true
963
+ });
964
+ }
965
+ expect((await adapter.findMany({
966
+ model: "user",
967
+ join: { session: true },
968
+ offset: 2
969
+ })).length).toBe(3);
970
+ },
971
+ "findMany - should find many with join and sortBy": async () => {
972
+ let n = -1;
973
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
974
+ type: "number",
975
+ defaultValue() {
976
+ return ++n;
977
+ }
978
+ } } } }, true);
979
+ const users = [];
980
+ for (let i = 0; i < 5; i++) {
981
+ const user = await adapter.create({
982
+ model: "user",
983
+ data: { ...await generate("user") },
984
+ forceAllowId: true
985
+ });
986
+ users.push(user);
987
+ for (let j = 0; j < 2; j++) await adapter.create({
988
+ model: "session",
989
+ data: {
990
+ ...await generate("session"),
991
+ userId: user.id
992
+ },
993
+ forceAllowId: true
994
+ });
995
+ }
996
+ const result = await adapter.findMany({
997
+ model: "user",
998
+ join: { session: true },
999
+ sortBy: {
1000
+ field: "numericField",
1001
+ direction: "desc"
1002
+ }
1003
+ });
1004
+ expect(result[0].numericField).toBeGreaterThan(result[result.length - 1].numericField);
1005
+ result.forEach((user) => {
1006
+ expect(user.session.length).toBeGreaterThan(0);
1007
+ });
1008
+ },
1009
+ "findMany - should find many with join and where clause": async () => {
1010
+ const users = [];
1011
+ for (let i = 0; i < 5; i++) {
1012
+ const user = await adapter.create({
1013
+ model: "user",
1014
+ data: {
1015
+ ...await generate("user"),
1016
+ name: i < 2 ? `target-user-${i}` : `other-user-${i}`
1017
+ },
1018
+ forceAllowId: true
1019
+ });
1020
+ users.push(user);
1021
+ for (let j = 0; j < 2; j++) await adapter.create({
1022
+ model: "session",
1023
+ data: {
1024
+ ...await generate("session"),
1025
+ userId: user.id
1026
+ },
1027
+ forceAllowId: true
1028
+ });
1029
+ }
1030
+ const result = await adapter.findMany({
1031
+ model: "user",
1032
+ where: [{
1033
+ field: "name",
1034
+ value: "target-user",
1035
+ operator: "starts_with"
1036
+ }],
1037
+ join: { session: true }
1038
+ });
1039
+ expect(result).toHaveLength(2);
1040
+ result.forEach((user) => {
1041
+ expect(user.name.startsWith("target-user")).toBe(true);
1042
+ expect(user.session).toHaveLength(2);
1043
+ });
1044
+ },
1045
+ "findMany - should find many with join, where, limit, and offset": async () => {
1046
+ const users = [];
1047
+ for (let i = 0; i < 10; i++) {
1048
+ const user = await adapter.create({
1049
+ model: "user",
1050
+ data: {
1051
+ ...await generate("user"),
1052
+ name: `target-${i.toString().padStart(2, "0")}`
1053
+ },
1054
+ forceAllowId: true
1055
+ });
1056
+ users.push(user);
1057
+ for (let j = 0; j < 2; j++) await adapter.create({
1058
+ model: "session",
1059
+ data: {
1060
+ ...await generate("session"),
1061
+ userId: user.id
1062
+ },
1063
+ forceAllowId: true
1064
+ });
1065
+ }
1066
+ const result = await adapter.findMany({
1067
+ model: "user",
1068
+ where: [{
1069
+ field: "name",
1070
+ value: "target",
1071
+ operator: "starts_with"
1072
+ }],
1073
+ join: { session: true },
1074
+ limit: 3,
1075
+ offset: 2
1076
+ });
1077
+ expect(result).toHaveLength(3);
1078
+ result.forEach((user) => {
1079
+ expect(user.session).toHaveLength(2);
1080
+ });
1081
+ },
1082
+ "findMany - should find many with one-to-one join": async () => {
1083
+ await modifyBetterAuthOptions({ plugins: [{
1084
+ id: "one-to-one-test",
1085
+ schema: { oneToOneTable: { fields: { oneToOne: {
1086
+ type: "string",
1087
+ required: true,
1088
+ references: {
1089
+ field: "id",
1090
+ model: "user"
1091
+ },
1092
+ unique: true
1093
+ } } } }
1094
+ }] }, true);
1095
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1096
+ const oneToOneRecords = [];
1097
+ for (const user of users) {
1098
+ const record = await adapter.create({
1099
+ model: "oneToOneTable",
1100
+ data: { oneToOne: user.id }
1101
+ });
1102
+ oneToOneRecords.push(record);
1103
+ }
1104
+ const resultsWithJoin = (await adapter.findMany({
1105
+ model: "user",
1106
+ join: { oneToOneTable: true }
1107
+ })).filter((r) => r.oneToOneTable);
1108
+ expect(resultsWithJoin).toHaveLength(3);
1109
+ resultsWithJoin.forEach((user) => {
1110
+ expect(user.oneToOneTable).toBeDefined();
1111
+ expect(user.oneToOneTable.oneToOne).toBe(user.id);
1112
+ });
1113
+ },
1114
+ "findMany - should find many with both one-to-one and one-to-many joins": async () => {
1115
+ await modifyBetterAuthOptions({ plugins: [{
1116
+ id: "one-to-one-test",
1117
+ schema: { oneToOneTable: { fields: { oneToOne: {
1118
+ type: "string",
1119
+ required: true,
1120
+ references: {
1121
+ field: "id",
1122
+ model: "user"
1123
+ },
1124
+ unique: true
1125
+ } } } }
1126
+ }] }, true);
1127
+ const users = (await insertRandom("user", 2)).map((x) => x[0]);
1128
+ for (const user of users) {
1129
+ await adapter.create({
1130
+ model: "oneToOneTable",
1131
+ data: { oneToOne: user.id }
1132
+ });
1133
+ for (let i = 0; i < 2; i++) await adapter.create({
1134
+ model: "session",
1135
+ data: {
1136
+ ...await generate("session"),
1137
+ userId: user.id
1138
+ },
1139
+ forceAllowId: true
1140
+ });
1141
+ }
1142
+ const resultsWithBothJoins = (await adapter.findMany({
1143
+ model: "user",
1144
+ join: {
1145
+ oneToOneTable: true,
1146
+ session: true
1147
+ }
1148
+ })).filter((r) => r.oneToOneTable && r.session?.length > 0);
1149
+ expect(resultsWithBothJoins.length).toBeGreaterThanOrEqual(2);
1150
+ resultsWithBothJoins.forEach((user) => {
1151
+ expect(user.oneToOneTable).toBeDefined();
1152
+ expect(Array.isArray(user.session)).toBe(true);
1153
+ expect(user.session.length).toBeGreaterThan(0);
1154
+ });
1155
+ },
1156
+ "findMany - should return an empty array when no models are found": async () => {
1157
+ const useUUIDs = getBetterAuthOptions().advanced?.database?.generateId === "uuid";
1158
+ expect(await adapter.findMany({
1159
+ model: "user",
1160
+ where: [{
1161
+ field: "id",
1162
+ value: useUUIDs ? crypto.randomUUID() : "100000"
1163
+ }]
1164
+ })).toEqual([]);
1165
+ },
1166
+ "findMany - should return empty array when base records don't exist with joins": async () => {
1167
+ await modifyBetterAuthOptions({ plugins: [{
1168
+ id: "one-to-one-test",
1169
+ schema: { oneToOneTable: { fields: { oneToOne: {
1170
+ type: "string",
1171
+ required: true,
1172
+ references: {
1173
+ field: "id",
1174
+ model: "user"
1175
+ },
1176
+ unique: true
1177
+ } } } }
1178
+ }] }, true);
1179
+ const useUUIDs = getBetterAuthOptions().advanced?.database?.generateId === "uuid";
1180
+ expect(await adapter.findMany({
1181
+ model: "user",
1182
+ where: [{
1183
+ field: "id",
1184
+ value: useUUIDs ? crypto.randomUUID() : "100000"
1185
+ }],
1186
+ join: {
1187
+ session: true,
1188
+ account: true,
1189
+ oneToOneTable: true
1190
+ }
1191
+ })).toEqual([]);
1192
+ },
1193
+ "findMany - should find many models with starts_with operator": async () => {
1194
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1195
+ expect(sortModels(await adapter.findMany({
1196
+ model: "user",
1197
+ where: [{
1198
+ field: "name",
1199
+ value: "user",
1200
+ operator: "starts_with"
1201
+ }]
1202
+ }))).toEqual(sortModels(users));
1203
+ },
1204
+ "findMany - starts_with should not interpret regex patterns": async () => {
1205
+ const userTemplate = await generate("user");
1206
+ const literalRegexUser = await adapter.create({
1207
+ model: "user",
1208
+ data: {
1209
+ ...userTemplate,
1210
+ name: ".*danger"
1211
+ },
1212
+ forceAllowId: true
1213
+ });
1214
+ await insertRandom("user", 3);
1215
+ const result = await adapter.findMany({
1216
+ model: "user",
1217
+ where: [{
1218
+ field: "name",
1219
+ value: ".*",
1220
+ operator: "starts_with"
1221
+ }]
1222
+ });
1223
+ expect(result.length).toBe(1);
1224
+ expect(result[0].id).toBe(literalRegexUser.id);
1225
+ expect(result[0].name.startsWith(".*")).toBe(true);
1226
+ },
1227
+ "findMany - ends_with should not interpret regex patterns": async () => {
1228
+ const userTemplate = await generate("user");
1229
+ const literalRegexUser = await adapter.create({
1230
+ model: "user",
1231
+ data: {
1232
+ ...userTemplate,
1233
+ name: "danger.*"
1234
+ },
1235
+ forceAllowId: true
1236
+ });
1237
+ await insertRandom("user", 3);
1238
+ const result = await adapter.findMany({
1239
+ model: "user",
1240
+ where: [{
1241
+ field: "name",
1242
+ value: ".*",
1243
+ operator: "ends_with"
1244
+ }]
1245
+ });
1246
+ expect(result.length).toBe(1);
1247
+ expect(result[0].id).toBe(literalRegexUser.id);
1248
+ expect(result[0].name.endsWith(".*")).toBe(true);
1249
+ },
1250
+ "findMany - contains should not interpret regex patterns": async () => {
1251
+ const userTemplate = await generate("user");
1252
+ const literalRegexUser = await adapter.create({
1253
+ model: "user",
1254
+ data: {
1255
+ ...userTemplate,
1256
+ name: "prefix-.*-suffix"
1257
+ },
1258
+ forceAllowId: true
1259
+ });
1260
+ await insertRandom("user", 3);
1261
+ const result = await adapter.findMany({
1262
+ model: "user",
1263
+ where: [{
1264
+ field: "name",
1265
+ value: ".*",
1266
+ operator: "contains"
1267
+ }]
1268
+ });
1269
+ expect(result.length).toBe(1);
1270
+ expect(result[0].id).toBe(literalRegexUser.id);
1271
+ expect(result[0].name.includes(".*")).toBe(true);
1272
+ },
1273
+ "findMany - should find many models with ends_with operator": async () => {
1274
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1275
+ for (const user of users) {
1276
+ const res = await adapter.update({
1277
+ model: "user",
1278
+ where: [{
1279
+ field: "id",
1280
+ value: user.id
1281
+ }],
1282
+ update: { name: user.name.toLowerCase() }
1283
+ });
1284
+ if (!res) throw new Error("No result");
1285
+ const u = users.find((u) => u.id === user.id);
1286
+ u.name = res.name;
1287
+ u.updatedAt = res.updatedAt;
1288
+ }
1289
+ const ends_with = users[0].name.slice(-1);
1290
+ const result = await adapter.findMany({
1291
+ model: "user",
1292
+ where: [{
1293
+ field: "name",
1294
+ value: ends_with,
1295
+ operator: "ends_with"
1296
+ }]
1297
+ });
1298
+ const expectedResult = sortModels(users.filter((user) => user.name.endsWith(ends_with)));
1299
+ if (result.length !== expectedResult.length) {
1300
+ console.log(`Result length: ${result.length}`);
1301
+ console.log(sortModels(result));
1302
+ console.log("--------------------------------");
1303
+ console.log(`Expected result length: ${expectedResult.length} - key: ${JSON.stringify(ends_with)}`);
1304
+ console.log(expectedResult);
1305
+ }
1306
+ expect(sortModels(result)).toEqual(expectedResult);
1307
+ },
1308
+ "findMany - should find many models with contains operator": async () => {
1309
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1310
+ expect(users[0].email).toContain("@email.com");
1311
+ expect(sortModels(await adapter.findMany({
1312
+ model: "user",
1313
+ where: [{
1314
+ field: "email",
1315
+ value: "mail",
1316
+ operator: "contains"
1317
+ }]
1318
+ }))).toEqual(sortModels(users));
1319
+ },
1320
+ "findMany - should handle multiple where conditions with different operators": async () => {
1321
+ const testData = [{
1322
+ name: "john doe",
1323
+ email: "john@example.com"
1324
+ }, {
1325
+ name: "jane smith",
1326
+ email: "jane@gmail.com"
1327
+ }];
1328
+ const createdUsers = [];
1329
+ for (const data of testData) {
1330
+ const user = await adapter.create({
1331
+ model: "user",
1332
+ data: {
1333
+ ...generate("user"),
1334
+ ...data
1335
+ },
1336
+ forceAllowId: true
1337
+ });
1338
+ createdUsers.push(user);
1339
+ }
1340
+ const result = await adapter.findMany({
1341
+ model: "user",
1342
+ where: [{
1343
+ field: "email",
1344
+ value: "john@example.com",
1345
+ operator: "eq",
1346
+ connector: "AND"
1347
+ }, {
1348
+ field: "name",
1349
+ value: "john",
1350
+ operator: "contains",
1351
+ connector: "AND"
1352
+ }]
1353
+ });
1354
+ expect(result.length).toBe(1);
1355
+ expect(result[0].email).toBe("john@example.com");
1356
+ expect(result[0].name).toBe("john doe");
1357
+ const result2 = await adapter.findMany({
1358
+ model: "user",
1359
+ where: [{
1360
+ field: "email",
1361
+ value: "gmail",
1362
+ operator: "contains",
1363
+ connector: "AND"
1364
+ }, {
1365
+ field: "name",
1366
+ value: "jane",
1367
+ operator: "contains",
1368
+ connector: "AND"
1369
+ }]
1370
+ });
1371
+ expect(result2.length).toBe(1);
1372
+ expect(result2[0].email).toBe("jane@gmail.com");
1373
+ expect(result2[0].name).toBe("jane smith");
1374
+ const result3 = await adapter.findMany({
1375
+ model: "user",
1376
+ where: [{
1377
+ field: "email",
1378
+ value: "john",
1379
+ operator: "starts_with",
1380
+ connector: "AND"
1381
+ }, {
1382
+ field: "name",
1383
+ value: "john",
1384
+ operator: "contains",
1385
+ connector: "AND"
1386
+ }]
1387
+ });
1388
+ expect(result3.length).toBe(1);
1389
+ expect(result3[0].email).toBe("john@example.com");
1390
+ expect(result3[0].name).toBe("john doe");
1391
+ },
1392
+ "findMany - should find many models with contains operator (using symbol)": async () => {
1393
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1394
+ expect(sortModels(await adapter.findMany({
1395
+ model: "user",
1396
+ where: [{
1397
+ field: "email",
1398
+ value: "@",
1399
+ operator: "contains"
1400
+ }]
1401
+ }))).toEqual(sortModels(users));
1402
+ },
1403
+ "findMany - should find many models with eq operator": async () => {
1404
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1405
+ expect(sortModels(await adapter.findMany({
1406
+ model: "user",
1407
+ where: [{
1408
+ field: "email",
1409
+ value: users[0].email,
1410
+ operator: "eq"
1411
+ }]
1412
+ }))).toEqual(sortModels([users[0]]));
1413
+ },
1414
+ "findMany - should find many models with ne operator": async () => {
1415
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1416
+ expect(sortModels(await adapter.findMany({
1417
+ model: "user",
1418
+ where: [{
1419
+ field: "email",
1420
+ value: users[0].email,
1421
+ operator: "ne"
1422
+ }]
1423
+ }))).toEqual(sortModels(users.slice(1)));
1424
+ },
1425
+ "findMany - should find many models with gt operator": async () => {
1426
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1427
+ const oldestUser = users.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime())[0];
1428
+ const result = await adapter.findMany({
1429
+ model: "user",
1430
+ where: [{
1431
+ field: "createdAt",
1432
+ value: oldestUser.createdAt,
1433
+ operator: "gt"
1434
+ }]
1435
+ });
1436
+ const expectedResult = sortModels(users.filter((user) => user.createdAt > oldestUser.createdAt));
1437
+ expect(result.length).not.toBe(0);
1438
+ expect(sortModels(result)).toEqual(expectedResult);
1439
+ },
1440
+ "findMany - should find many models with gte operator": async () => {
1441
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1442
+ const oldestUser = users.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())[0];
1443
+ const result = await adapter.findMany({
1444
+ model: "user",
1445
+ where: [{
1446
+ field: "createdAt",
1447
+ value: oldestUser.createdAt,
1448
+ operator: "gte"
1449
+ }]
1450
+ });
1451
+ const expectedResult = users.filter((user) => user.createdAt >= oldestUser.createdAt);
1452
+ expect(result.length).not.toBe(0);
1453
+ expect(sortModels(result)).toEqual(sortModels(expectedResult));
1454
+ },
1455
+ "findMany - should find many models with lte operator": async () => {
1456
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1457
+ const result = await adapter.findMany({
1458
+ model: "user",
1459
+ where: [{
1460
+ field: "createdAt",
1461
+ value: users[0].createdAt,
1462
+ operator: "lte"
1463
+ }]
1464
+ });
1465
+ const expectedResult = users.filter((user) => user.createdAt <= users[0].createdAt);
1466
+ expect(sortModels(result)).toEqual(sortModels(expectedResult));
1467
+ },
1468
+ "findMany - should find many models with lt operator": async () => {
1469
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1470
+ const result = await adapter.findMany({
1471
+ model: "user",
1472
+ where: [{
1473
+ field: "createdAt",
1474
+ value: users[0].createdAt,
1475
+ operator: "lt"
1476
+ }]
1477
+ });
1478
+ const expectedResult = users.filter((user) => user.createdAt < users[0].createdAt);
1479
+ expect(sortModels(result)).toEqual(sortModels(expectedResult));
1480
+ },
1481
+ "findMany - should find many models with in operator": async () => {
1482
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1483
+ const result = await adapter.findMany({
1484
+ model: "user",
1485
+ where: [{
1486
+ field: "id",
1487
+ value: [users[0].id, users[1].id],
1488
+ operator: "in"
1489
+ }]
1490
+ });
1491
+ const expectedResult = users.filter((user) => user.id === users[0].id || user.id === users[1].id);
1492
+ expect(sortModels(result)).toEqual(sortModels(expectedResult));
1493
+ },
1494
+ "findMany - should find many models with not_in operator": async () => {
1495
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1496
+ expect(sortModels(await adapter.findMany({
1497
+ model: "user",
1498
+ where: [{
1499
+ field: "id",
1500
+ value: [users[0].id, users[1].id],
1501
+ operator: "not_in"
1502
+ }]
1503
+ }))).toEqual([users[2]]);
1504
+ },
1505
+ "findMany - should find many models with sortBy": async () => {
1506
+ let n = -1;
1507
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
1508
+ type: "number",
1509
+ defaultValue() {
1510
+ return n++;
1511
+ }
1512
+ } } } }, true);
1513
+ const users = (await insertRandom("user", 5)).map((x) => x[0]);
1514
+ const result = await adapter.findMany({
1515
+ model: "user",
1516
+ sortBy: {
1517
+ field: "numericField",
1518
+ direction: "asc"
1519
+ }
1520
+ });
1521
+ const expectedResult = users.map((x) => x.numericField).sort((a, b) => a - b);
1522
+ try {
1523
+ expect(result.map((x) => x.numericField)).toEqual(expectedResult);
1524
+ } catch (error) {
1525
+ console.log(`--------------------------------`);
1526
+ console.log(`result:`);
1527
+ console.log(result.map((x) => x.id));
1528
+ console.log(`expected result:`);
1529
+ console.log(expectedResult);
1530
+ console.log(`--------------------------------`);
1531
+ throw error;
1532
+ }
1533
+ if (getBetterAuthOptions().advanced?.database?.generateId === "serial") expect(Number(users[0].id)).not.toBeNaN();
1534
+ },
1535
+ "findMany - should find many models with limit": async () => {
1536
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1537
+ const result = await adapter.findMany({
1538
+ model: "user",
1539
+ limit: 2
1540
+ });
1541
+ expect(result.length).toEqual(2);
1542
+ expect(users.find((x) => x.id === result[0].id)).not.toBeNull();
1543
+ },
1544
+ "findMany - should find many models with offset": async () => {
1545
+ const count = 10;
1546
+ await insertRandom("user", count);
1547
+ expect((await adapter.findMany({
1548
+ model: "user",
1549
+ offset: 2
1550
+ })).length).toEqual(count - 2);
1551
+ },
1552
+ "findMany - should find many models with limit and offset": async () => {
1553
+ await insertRandom("user", 5);
1554
+ const result = await adapter.findMany({
1555
+ model: "user",
1556
+ limit: 2,
1557
+ offset: 2
1558
+ });
1559
+ expect(result.length).toEqual(2);
1560
+ expect(result).toBeInstanceOf(Array);
1561
+ result.forEach((user) => {
1562
+ expect(user).toHaveProperty("id");
1563
+ expect(user).toHaveProperty("name");
1564
+ expect(user).toHaveProperty("email");
1565
+ });
1566
+ },
1567
+ "findMany - should find many models with sortBy and offset": async () => {
1568
+ let n = -1;
1569
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
1570
+ type: "number",
1571
+ defaultValue() {
1572
+ return n++;
1573
+ }
1574
+ } } } }, true);
1575
+ const users = (await insertRandom("user", 5)).map((x) => x[0]);
1576
+ const result = await adapter.findMany({
1577
+ model: "user",
1578
+ sortBy: {
1579
+ field: "numericField",
1580
+ direction: "asc"
1581
+ },
1582
+ offset: 2
1583
+ });
1584
+ expect(result).toHaveLength(3);
1585
+ expect(result).toEqual(users.sort((a, b) => a.numericField - b.numericField).slice(2));
1586
+ },
1587
+ "findMany - should find many models with sortBy and limit": async () => {
1588
+ let n = -1;
1589
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
1590
+ type: "number",
1591
+ defaultValue() {
1592
+ return n++;
1593
+ }
1594
+ } } } }, true);
1595
+ const users = (await insertRandom("user", 5)).map((x) => x[0]);
1596
+ expect(await adapter.findMany({
1597
+ model: "user",
1598
+ sortBy: {
1599
+ field: "numericField",
1600
+ direction: "asc"
1601
+ },
1602
+ limit: 2
1603
+ })).toEqual(users.sort((a, b) => a.numericField - b.numericField).slice(0, 2));
1604
+ },
1605
+ "findMany - should find many models with sortBy and limit and offset": async () => {
1606
+ let n = -1;
1607
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
1608
+ type: "number",
1609
+ defaultValue() {
1610
+ return n++;
1611
+ }
1612
+ } } } }, true);
1613
+ const users = (await insertRandom("user", 5)).map((x) => x[0]);
1614
+ const result = await adapter.findMany({
1615
+ model: "user",
1616
+ sortBy: {
1617
+ field: "numericField",
1618
+ direction: "asc"
1619
+ },
1620
+ limit: 2,
1621
+ offset: 2
1622
+ });
1623
+ expect(result.length).toBe(2);
1624
+ expect(result).toEqual(users.sort((a, b) => a.numericField - b.numericField).slice(2, 4));
1625
+ },
1626
+ "findMany - should find many models with sortBy and limit and offset and where": async () => {
1627
+ let n = -1;
1628
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
1629
+ type: "number",
1630
+ defaultValue() {
1631
+ return n++;
1632
+ }
1633
+ } } } }, true);
1634
+ const users = (await insertRandom("user", 10)).map((x) => x[0]);
1635
+ let i = -1;
1636
+ for (const user of users) {
1637
+ i++;
1638
+ if (i < 5) continue;
1639
+ const result = await adapter.update({
1640
+ model: "user",
1641
+ where: [{
1642
+ field: "id",
1643
+ value: user.id
1644
+ }],
1645
+ update: { name: user.name + "-last" }
1646
+ });
1647
+ if (!result) throw new Error("No result");
1648
+ users[i].name = result.name;
1649
+ users[i].updatedAt = result.updatedAt;
1650
+ }
1651
+ const result = await adapter.findMany({
1652
+ model: "user",
1653
+ sortBy: {
1654
+ field: "numericField",
1655
+ direction: "asc"
1656
+ },
1657
+ limit: 2,
1658
+ offset: 2,
1659
+ where: [{
1660
+ field: "name",
1661
+ value: "last",
1662
+ operator: "ends_with"
1663
+ }]
1664
+ });
1665
+ let expectedResult = [];
1666
+ expectedResult = users.filter((user) => user.name.endsWith("last")).sort((a, b) => a.numericField - b.numericField).slice(2, 4);
1667
+ try {
1668
+ expect(result.length).toBe(2);
1669
+ expect(result).toEqual(expectedResult);
1670
+ } catch (error) {
1671
+ console.log(`--------------------------------`);
1672
+ console.log(`results:`);
1673
+ console.log(result.map((x) => x.id));
1674
+ console.log(`expected results, sorted:`);
1675
+ console.log(users.filter((x) => x.name.toString().endsWith("last")).map((x) => x.numericField).sort((a, b) => a - b));
1676
+ console.log(`expected results, sorted + offset:`);
1677
+ console.log(users.filter((x) => x.name.toString().endsWith("last")).map((x) => x.numericField).sort((a, b) => a - b).slice(2, 4));
1678
+ console.log(`--------------------------------`);
1679
+ console.log("FAIL", error);
1680
+ console.log(`--------------------------------`);
1681
+ throw error;
1682
+ }
1683
+ },
1684
+ "update - should update a model": async () => {
1685
+ const [user] = await insertRandom("user");
1686
+ const result = await adapter.update({
1687
+ model: "user",
1688
+ where: [{
1689
+ field: "id",
1690
+ value: user.id
1691
+ }],
1692
+ update: { name: "test-name" }
1693
+ });
1694
+ const expectedResult = {
1695
+ ...user,
1696
+ name: "test-name"
1697
+ };
1698
+ result.updatedAt = user.updatedAt;
1699
+ expect(result).toEqual(expectedResult);
1700
+ const findResult = await adapter.findOne({
1701
+ model: "user",
1702
+ where: [{
1703
+ field: "id",
1704
+ value: user.id
1705
+ }]
1706
+ });
1707
+ findResult.updatedAt = user.updatedAt;
1708
+ expect(findResult).toEqual(expectedResult);
1709
+ },
1710
+ "updateMany - should update all models when where is empty": async () => {
1711
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1712
+ await adapter.updateMany({
1713
+ model: "user",
1714
+ where: [],
1715
+ update: { name: "test-name" }
1716
+ });
1717
+ const result = await adapter.findMany({ model: "user" });
1718
+ expect(sortModels(result)).toEqual(sortModels(users).map((user, i) => ({
1719
+ ...user,
1720
+ name: "test-name",
1721
+ updatedAt: sortModels(result)[i].updatedAt
1722
+ })));
1723
+ },
1724
+ "updateMany - should update many models with a specific where": async () => {
1725
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1726
+ await adapter.updateMany({
1727
+ model: "user",
1728
+ where: [{
1729
+ field: "id",
1730
+ value: users[0].id
1731
+ }],
1732
+ update: { name: "test-name" }
1733
+ });
1734
+ const result = await adapter.findOne({
1735
+ model: "user",
1736
+ where: [{
1737
+ field: "id",
1738
+ value: users[0].id
1739
+ }]
1740
+ });
1741
+ expect(result).toEqual({
1742
+ ...users[0],
1743
+ name: "test-name",
1744
+ updatedAt: result.updatedAt
1745
+ });
1746
+ },
1747
+ "updateMany - should update many models with a multiple where": async () => {
1748
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1749
+ await adapter.updateMany({
1750
+ model: "user",
1751
+ where: [{
1752
+ field: "id",
1753
+ value: users[0].id,
1754
+ connector: "OR"
1755
+ }, {
1756
+ field: "id",
1757
+ value: users[1].id,
1758
+ connector: "OR"
1759
+ }],
1760
+ update: { name: "test-name" }
1761
+ });
1762
+ const result = await adapter.findOne({
1763
+ model: "user",
1764
+ where: [{
1765
+ field: "id",
1766
+ value: users[0].id
1767
+ }]
1768
+ });
1769
+ expect(result).toEqual({
1770
+ ...users[0],
1771
+ name: "test-name",
1772
+ updatedAt: result.updatedAt
1773
+ });
1774
+ },
1775
+ "delete - should delete a model": async () => {
1776
+ const [user] = await insertRandom("user");
1777
+ await adapter.delete({
1778
+ model: "user",
1779
+ where: [{
1780
+ field: "id",
1781
+ value: user.id
1782
+ }]
1783
+ });
1784
+ expect(await adapter.findOne({
1785
+ model: "user",
1786
+ where: [{
1787
+ field: "id",
1788
+ value: user.id
1789
+ }]
1790
+ })).toBeNull();
1791
+ },
1792
+ "delete - should not throw on record not found": async () => {
1793
+ const useUUIDs = getBetterAuthOptions().advanced?.database?.generateId === "uuid";
1794
+ await expect(adapter.delete({
1795
+ model: "user",
1796
+ where: [{
1797
+ field: "id",
1798
+ value: useUUIDs ? crypto.randomUUID() : "100000"
1799
+ }]
1800
+ })).resolves.not.toThrow();
1801
+ },
1802
+ "delete - should delete by non-unique field": async () => {
1803
+ const [verification] = await insertRandom("verification");
1804
+ await adapter.delete({
1805
+ model: "verification",
1806
+ where: [{
1807
+ field: "identifier",
1808
+ value: verification.identifier
1809
+ }]
1810
+ });
1811
+ expect(await adapter.findOne({
1812
+ model: "verification",
1813
+ where: [{
1814
+ field: "identifier",
1815
+ value: verification.identifier
1816
+ }]
1817
+ })).toBeNull();
1818
+ },
1819
+ "deleteMany - should delete many models": async () => {
1820
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1821
+ await adapter.deleteMany({
1822
+ model: "user",
1823
+ where: [{
1824
+ field: "id",
1825
+ value: users[0].id,
1826
+ connector: "OR"
1827
+ }, {
1828
+ field: "id",
1829
+ value: users[1].id,
1830
+ connector: "OR"
1831
+ }]
1832
+ });
1833
+ expect(sortModels(await adapter.findMany({ model: "user" }))).toEqual(sortModels(users.slice(2)));
1834
+ },
1835
+ "deleteMany - starts_with should not interpret regex patterns": async () => {
1836
+ const userTemplate = await generate("user");
1837
+ const literalRegexUser = await adapter.create({
1838
+ model: "user",
1839
+ data: {
1840
+ ...userTemplate,
1841
+ name: ".*danger"
1842
+ },
1843
+ forceAllowId: true
1844
+ });
1845
+ const normalUsers = (await insertRandom("user", 3)).map((x) => x[0]);
1846
+ await adapter.deleteMany({
1847
+ model: "user",
1848
+ where: [{
1849
+ field: "name",
1850
+ value: ".*",
1851
+ operator: "starts_with"
1852
+ }]
1853
+ });
1854
+ expect(await adapter.findOne({
1855
+ model: "user",
1856
+ where: [{
1857
+ field: "id",
1858
+ value: literalRegexUser.id
1859
+ }]
1860
+ })).toBeNull();
1861
+ for (const user of normalUsers) expect(await adapter.findOne({
1862
+ model: "user",
1863
+ where: [{
1864
+ field: "id",
1865
+ value: user.id
1866
+ }]
1867
+ })).not.toBeNull();
1868
+ },
1869
+ "deleteMany - ends_with should not interpret regex patterns": async () => {
1870
+ const userTemplate = await generate("user");
1871
+ const literalRegexUser = await adapter.create({
1872
+ model: "user",
1873
+ data: {
1874
+ ...userTemplate,
1875
+ name: "danger.*"
1876
+ },
1877
+ forceAllowId: true
1878
+ });
1879
+ const normalUsers = (await insertRandom("user", 3)).map((x) => x[0]);
1880
+ await adapter.deleteMany({
1881
+ model: "user",
1882
+ where: [{
1883
+ field: "name",
1884
+ value: ".*",
1885
+ operator: "ends_with"
1886
+ }]
1887
+ });
1888
+ expect(await adapter.findOne({
1889
+ model: "user",
1890
+ where: [{
1891
+ field: "id",
1892
+ value: literalRegexUser.id
1893
+ }]
1894
+ })).toBeNull();
1895
+ for (const user of normalUsers) expect(await adapter.findOne({
1896
+ model: "user",
1897
+ where: [{
1898
+ field: "id",
1899
+ value: user.id
1900
+ }]
1901
+ })).not.toBeNull();
1902
+ },
1903
+ "deleteMany - contains should not interpret regex patterns": async () => {
1904
+ const userTemplate = await generate("user");
1905
+ const literalRegexUser = await adapter.create({
1906
+ model: "user",
1907
+ data: {
1908
+ ...userTemplate,
1909
+ name: "prefix-.*-suffix"
1910
+ },
1911
+ forceAllowId: true
1912
+ });
1913
+ const normalUsers = (await insertRandom("user", 3)).map((x) => x[0]);
1914
+ await adapter.deleteMany({
1915
+ model: "user",
1916
+ where: [{
1917
+ field: "name",
1918
+ value: ".*",
1919
+ operator: "contains"
1920
+ }]
1921
+ });
1922
+ expect(await adapter.findOne({
1923
+ model: "user",
1924
+ where: [{
1925
+ field: "id",
1926
+ value: literalRegexUser.id
1927
+ }]
1928
+ })).toBeNull();
1929
+ for (const user of normalUsers) expect(await adapter.findOne({
1930
+ model: "user",
1931
+ where: [{
1932
+ field: "id",
1933
+ value: user.id
1934
+ }]
1935
+ })).not.toBeNull();
1936
+ },
1937
+ "deleteMany - should delete many models with numeric values": async () => {
1938
+ let i = 0;
1939
+ await modifyBetterAuthOptions({ user: { additionalFields: { numericField: {
1940
+ type: "number",
1941
+ defaultValue() {
1942
+ return i++;
1943
+ }
1944
+ } } } }, true);
1945
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1946
+ if (!users[0] || !users[1] || !users[2]) {
1947
+ expect(false).toBe(true);
1948
+ throw new Error("Users not found");
1949
+ }
1950
+ expect(users[0].numericField).toEqual(0);
1951
+ expect(users[1].numericField).toEqual(1);
1952
+ expect(users[2].numericField).toEqual(2);
1953
+ await adapter.deleteMany({
1954
+ model: "user",
1955
+ where: [{
1956
+ field: "numericField",
1957
+ value: users[0].numericField,
1958
+ operator: "gt"
1959
+ }]
1960
+ });
1961
+ expect(await adapter.findMany({ model: "user" })).toEqual([users[0]]);
1962
+ },
1963
+ "deleteMany - should delete many models with boolean values": async () => {
1964
+ const users = (await insertRandom("user", 3)).map((x) => x[0]);
1965
+ if (!users[0] || !users[1] || !users[2]) {
1966
+ expect(false).toBe(true);
1967
+ throw new Error("Users not found");
1968
+ }
1969
+ await adapter.updateMany({
1970
+ model: "user",
1971
+ where: [],
1972
+ update: { emailVerified: true }
1973
+ });
1974
+ await adapter.update({
1975
+ model: "user",
1976
+ where: [{
1977
+ field: "id",
1978
+ value: users[1].id
1979
+ }],
1980
+ update: { emailVerified: false }
1981
+ });
1982
+ await adapter.deleteMany({
1983
+ model: "user",
1984
+ where: [{
1985
+ field: "emailVerified",
1986
+ value: true
1987
+ }]
1988
+ });
1989
+ const result = await adapter.findMany({ model: "user" });
1990
+ expect(result).toHaveLength(1);
1991
+ expect(result.find((user) => user.id === users[0]?.id)).toBeUndefined();
1992
+ expect(result.find((user) => user.id === users[1]?.id)).toBeDefined();
1993
+ expect(result.find((user) => user.id === users[2]?.id)).toBeUndefined();
1994
+ },
1995
+ "count - should count many models": async () => {
1996
+ const users = await insertRandom("user", 15);
1997
+ expect(await adapter.count({ model: "user" })).toEqual(users.length);
1998
+ },
1999
+ "count - should return 0 with no rows to count": async () => {
2000
+ expect(await adapter.count({ model: "user" })).toEqual(0);
2001
+ },
2002
+ "count - should count with where clause": async () => {
2003
+ const users = (await insertRandom("user", 15)).map((x) => x[0]);
2004
+ expect(await adapter.count({
2005
+ model: "user",
2006
+ where: [{
2007
+ field: "id",
2008
+ value: users[2].id,
2009
+ connector: "OR"
2010
+ }, {
2011
+ field: "id",
2012
+ value: users[3].id,
2013
+ connector: "OR"
2014
+ }]
2015
+ })).toEqual(2);
2016
+ },
2017
+ "update - should correctly return record when updating a field used in where clause": async () => {
2018
+ const [user] = await insertRandom("user");
2019
+ const originalEmail = user.email;
2020
+ const result = await adapter.update({
2021
+ model: "user",
2022
+ where: [{
2023
+ field: "email",
2024
+ value: originalEmail
2025
+ }],
2026
+ update: { email: "newemail@example.com" }
2027
+ });
2028
+ expect(result).toBeDefined();
2029
+ expect(result.email).toBe("newemail@example.com");
2030
+ expect(result.id).toBe(user.id);
2031
+ const foundUser = await adapter.findOne({
2032
+ model: "user",
2033
+ where: [{
2034
+ field: "email",
2035
+ value: "newemail@example.com"
2036
+ }]
2037
+ });
2038
+ expect(foundUser).toBeDefined();
2039
+ expect(foundUser.id).toBe(user.id);
2040
+ expect(await adapter.findOne({
2041
+ model: "user",
2042
+ where: [{
2043
+ field: "email",
2044
+ value: originalEmail
2045
+ }]
2046
+ })).toBeNull();
2047
+ },
2048
+ "update - should handle updating multiple fields including where clause field": async () => {
2049
+ const [user] = await insertRandom("user");
2050
+ const originalEmail = user.email;
2051
+ const result = await adapter.update({
2052
+ model: "user",
2053
+ where: [{
2054
+ field: "email",
2055
+ value: originalEmail
2056
+ }],
2057
+ update: {
2058
+ email: "updated@example.com",
2059
+ name: "Updated Name",
2060
+ emailVerified: true
2061
+ }
2062
+ });
2063
+ expect(result.email).toBe("updated@example.com");
2064
+ expect(result.name).toBe("Updated Name");
2065
+ expect(result.emailVerified).toBe(true);
2066
+ expect(result.id).toBe(user.id);
2067
+ },
2068
+ "update - should work when updated field is not in where clause": async () => {
2069
+ const [user] = await insertRandom("user");
2070
+ const result = await adapter.update({
2071
+ model: "user",
2072
+ where: [{
2073
+ field: "email",
2074
+ value: user.email
2075
+ }],
2076
+ update: { name: "Updated Name Only" }
2077
+ });
2078
+ expect(result.name).toBe("Updated Name Only");
2079
+ expect(result.email).toBe(user.email);
2080
+ expect(result.id).toBe(user.id);
2081
+ },
2082
+ "findOne - backwards join should only return single record not array": async () => {
2083
+ const user = await adapter.create({
2084
+ model: "user",
2085
+ data: { ...await generate("user") },
2086
+ forceAllowId: true
2087
+ });
2088
+ const session = await adapter.create({
2089
+ model: "session",
2090
+ data: {
2091
+ ...await generate("session"),
2092
+ userId: user.id
2093
+ },
2094
+ forceAllowId: true
2095
+ });
2096
+ const result = await adapter.findOne({
2097
+ model: "session",
2098
+ where: [{
2099
+ field: "id",
2100
+ value: session.id
2101
+ }],
2102
+ join: { user: true }
2103
+ });
2104
+ expect(result?.user).toBeDefined();
2105
+ expect(Array.isArray(result?.user)).toBe(false);
2106
+ expect(result?.user?.id).toBe(user.id);
2107
+ },
2108
+ "findMany - backwards join should only return single record not array": async () => {
2109
+ const users = (await insertRandom("user", 2)).map((x) => x[0]);
2110
+ const sessions = [];
2111
+ for (const user of users) {
2112
+ const session = await adapter.create({
2113
+ model: "session",
2114
+ data: {
2115
+ ...await generate("session"),
2116
+ userId: user.id
2117
+ },
2118
+ forceAllowId: true
2119
+ });
2120
+ sessions.push(session);
2121
+ }
2122
+ (await adapter.findMany({
2123
+ model: "session",
2124
+ join: { user: true }
2125
+ })).forEach((session) => {
2126
+ expect(session.user).toBeDefined();
2127
+ expect(Array.isArray(session.user)).toBe(false);
2128
+ expect(session.user?.id).toBeDefined();
2129
+ });
2130
+ },
2131
+ "findOne - backwards join with modified field name (session base, users-table join)": async () => {
2132
+ await modifyBetterAuthOptions({ user: { modelName: "user_table" } }, true);
2133
+ const user = await adapter.create({
2134
+ model: "user",
2135
+ data: { ...await generate("user") },
2136
+ forceAllowId: true
2137
+ });
2138
+ const session = await adapter.create({
2139
+ model: "session",
2140
+ data: {
2141
+ ...await generate("session"),
2142
+ userId: user.id
2143
+ },
2144
+ forceAllowId: true
2145
+ });
2146
+ const result = await adapter.findOne({
2147
+ model: "session",
2148
+ where: [{
2149
+ field: "id",
2150
+ value: session.id
2151
+ }],
2152
+ join: { user: true }
2153
+ });
2154
+ expect(result).toEqual({
2155
+ ...session,
2156
+ user
2157
+ });
2158
+ expect(result?.user).toBeDefined();
2159
+ expect(Array.isArray(result?.user)).toBe(false);
2160
+ expect(result?.user?.id).toBe(user.id);
2161
+ },
2162
+ "findOne - multiple joins should return result even when some joined tables have no matching rows": async () => {
2163
+ await modifyBetterAuthOptions({ plugins: [organization({ teams: { enabled: true } })] }, true);
2164
+ const user = await adapter.create({
2165
+ model: "user",
2166
+ data: { ...await generate("user") },
2167
+ forceAllowId: true
2168
+ });
2169
+ const organizationData = await adapter.create({
2170
+ model: "organization",
2171
+ data: {
2172
+ name: "Test Organization",
2173
+ slug: "test-org-" + Math.random(),
2174
+ createdAt: /* @__PURE__ */ new Date()
2175
+ },
2176
+ forceAllowId: true
2177
+ });
2178
+ await adapter.create({
2179
+ model: "member",
2180
+ data: {
2181
+ organizationId: organizationData.id,
2182
+ userId: user.id,
2183
+ role: "owner",
2184
+ createdAt: /* @__PURE__ */ new Date()
2185
+ },
2186
+ forceAllowId: true
2187
+ });
2188
+ await adapter.create({
2189
+ model: "team",
2190
+ data: {
2191
+ name: "Test Team",
2192
+ organizationId: organizationData.id,
2193
+ createdAt: /* @__PURE__ */ new Date()
2194
+ },
2195
+ forceAllowId: true
2196
+ });
2197
+ const result = await adapter.findOne({
2198
+ model: "organization",
2199
+ where: [{
2200
+ field: "id",
2201
+ value: organizationData.id
2202
+ }],
2203
+ join: {
2204
+ member: true,
2205
+ team: true,
2206
+ invitation: true
2207
+ }
2208
+ });
2209
+ expect(result).toBeDefined();
2210
+ expect(result?.id).toBe(organizationData.id);
2211
+ expect(result?.name).toBe("Test Organization");
2212
+ expect(Array.isArray(result?.member)).toBe(true);
2213
+ expect(result?.member).toHaveLength(1);
2214
+ expect(result?.member[0]?.userId).toBe(user.id);
2215
+ expect(result?.member[0]?.role).toBe("owner");
2216
+ expect(Array.isArray(result?.team)).toBe(true);
2217
+ expect(result?.team).toHaveLength(1);
2218
+ expect(result?.team[0]?.name).toBe("Test Team");
2219
+ expect(Array.isArray(result?.invitation)).toBe(true);
2220
+ expect(result?.invitation).toHaveLength(0);
2221
+ },
2222
+ "findOne - should be able to perform a limited join": async () => {
2223
+ const user = await adapter.create({
2224
+ model: "user",
2225
+ data: { ...await generate("user") },
2226
+ forceAllowId: true
2227
+ });
2228
+ for (let i = 0; i < 5; i++) await adapter.create({
2229
+ model: "session",
2230
+ data: {
2231
+ ...await generate("session"),
2232
+ userId: user.id
2233
+ },
2234
+ forceAllowId: true
2235
+ });
2236
+ const result = await adapter.findOne({
2237
+ model: "user",
2238
+ where: [{
2239
+ field: "id",
2240
+ value: user.id
2241
+ }],
2242
+ join: { session: { limit: 2 } }
2243
+ });
2244
+ expect(result).toBeDefined();
2245
+ expect(result?.session).toBeDefined();
2246
+ expect(result?.session).toHaveLength(2);
2247
+ expect(result?.session[0]?.userId).toBe(user.id);
2248
+ },
2249
+ "findOne - should be able to perform a complex limited join": async () => {
2250
+ const user = await adapter.create({
2251
+ model: "user",
2252
+ data: { ...await generate("user") },
2253
+ forceAllowId: true
2254
+ });
2255
+ for (let i = 0; i < 5; i++) {
2256
+ await adapter.create({
2257
+ model: "session",
2258
+ data: {
2259
+ ...await generate("session"),
2260
+ userId: user.id
2261
+ },
2262
+ forceAllowId: true
2263
+ });
2264
+ await adapter.create({
2265
+ model: "account",
2266
+ data: {
2267
+ ...await generate("account"),
2268
+ userId: user.id
2269
+ },
2270
+ forceAllowId: true
2271
+ });
2272
+ }
2273
+ const result = await adapter.findOne({
2274
+ model: "user",
2275
+ where: [{
2276
+ field: "id",
2277
+ value: user.id
2278
+ }],
2279
+ join: {
2280
+ session: { limit: 2 },
2281
+ account: { limit: 3 }
2282
+ }
2283
+ });
2284
+ expect(result).toBeDefined();
2285
+ expect(result?.session).toBeDefined();
2286
+ expect(result?.session).toHaveLength(2);
2287
+ expect(result?.account).toBeDefined();
2288
+ expect(result?.account).toHaveLength(3);
2289
+ },
2290
+ "findMany - should be able to perform a limited join": async () => {
2291
+ const users = [];
2292
+ for (let i = 0; i < 5; i++) {
2293
+ const user = await adapter.create({
2294
+ model: "user",
2295
+ data: { ...await generate("user") },
2296
+ forceAllowId: true
2297
+ });
2298
+ users.push(user);
2299
+ }
2300
+ const sessionsByUser = /* @__PURE__ */ new Map();
2301
+ for (const user of users) {
2302
+ sessionsByUser.set(user.id, []);
2303
+ for (let i = 0; i < 5; i++) {
2304
+ const session = await adapter.create({
2305
+ model: "session",
2306
+ data: {
2307
+ ...await generate("session"),
2308
+ userId: user.id
2309
+ },
2310
+ forceAllowId: true
2311
+ });
2312
+ sessionsByUser.get(user.id).push(session);
2313
+ }
2314
+ }
2315
+ const result = await adapter.findMany({
2316
+ model: "user",
2317
+ join: { session: { limit: 2 } }
2318
+ });
2319
+ expect(result).toBeDefined();
2320
+ expect(result).toHaveLength(5);
2321
+ result.forEach((user) => {
2322
+ expect(user.session).toBeDefined();
2323
+ expect(user.session).toHaveLength(2);
2324
+ expect(user.session[0]?.userId).toBe(user.id);
2325
+ });
2326
+ },
2327
+ "findMany - should be able to perform a complex limited join": async () => {
2328
+ const users = [];
2329
+ const sessionsByUser = /* @__PURE__ */ new Map();
2330
+ const accountsByUser = /* @__PURE__ */ new Map();
2331
+ for (let i = 0; i < 5; i++) {
2332
+ const user = await adapter.create({
2333
+ model: "user",
2334
+ data: { ...await generate("user") },
2335
+ forceAllowId: true
2336
+ });
2337
+ users.push(user);
2338
+ sessionsByUser.set(user.id, []);
2339
+ accountsByUser.set(user.id, []);
2340
+ for (let i = 0; i < 5; i++) {
2341
+ const session = await adapter.create({
2342
+ model: "session",
2343
+ data: {
2344
+ ...await generate("session"),
2345
+ userId: user.id
2346
+ },
2347
+ forceAllowId: true
2348
+ });
2349
+ sessionsByUser.get(user.id).push(session);
2350
+ const account = await adapter.create({
2351
+ model: "account",
2352
+ data: {
2353
+ ...await generate("account"),
2354
+ userId: user.id
2355
+ },
2356
+ forceAllowId: true
2357
+ });
2358
+ accountsByUser.get(user.id).push(account);
2359
+ }
2360
+ }
2361
+ const result = await adapter.findMany({
2362
+ model: "user",
2363
+ join: {
2364
+ session: { limit: 2 },
2365
+ account: { limit: 3 }
2366
+ },
2367
+ limit: 2,
2368
+ offset: 2
2369
+ });
2370
+ expect(result).toBeDefined();
2371
+ expect(result).toHaveLength(2);
2372
+ result.forEach((user) => {
2373
+ expect(user.session).toBeDefined();
2374
+ expect(user.session).toHaveLength(2);
2375
+ expect(user.session[0]?.userId).toBe(user.id);
2376
+ expect(user.account).toBeDefined();
2377
+ expect(user.account).toHaveLength(3);
2378
+ expect(user.account[0]?.userId).toBe(user.id);
2379
+ });
2380
+ },
2381
+ "findOne - should return null for one-to-one join when joined record doesn't exist": async () => {
2382
+ await modifyBetterAuthOptions({ plugins: [{
2383
+ id: "one-to-one-test",
2384
+ schema: { oneToOneTable: { fields: { oneToOne: {
2385
+ type: "string",
2386
+ required: true,
2387
+ references: {
2388
+ field: "id",
2389
+ model: "user"
2390
+ },
2391
+ unique: true
2392
+ } } } }
2393
+ }] }, true);
2394
+ const user = await adapter.create({
2395
+ model: "user",
2396
+ data: { ...await generate("user") },
2397
+ forceAllowId: true
2398
+ });
2399
+ const result = await adapter.findOne({
2400
+ model: "user",
2401
+ where: [{
2402
+ field: "id",
2403
+ value: user.id
2404
+ }],
2405
+ join: { oneToOneTable: true }
2406
+ });
2407
+ expect(result).toBeDefined();
2408
+ expect(result?.id).toBe(user.id);
2409
+ expect(result?.oneToOneTable).toBeNull();
2410
+ },
2411
+ "findMany - should return null for one-to-one join when joined records don't exist": async () => {
2412
+ await modifyBetterAuthOptions({ plugins: [{
2413
+ id: "one-to-one-test",
2414
+ schema: { oneToOneTable: { fields: { oneToOne: {
2415
+ type: "string",
2416
+ required: true,
2417
+ references: {
2418
+ field: "id",
2419
+ model: "user"
2420
+ },
2421
+ unique: true
2422
+ } } } }
2423
+ }] }, true);
2424
+ const userWithJoin = await adapter.create({
2425
+ model: "user",
2426
+ data: { ...await generate("user") },
2427
+ forceAllowId: true
2428
+ });
2429
+ const oneToOne = await adapter.create({
2430
+ model: "oneToOneTable",
2431
+ data: { oneToOne: userWithJoin.id }
2432
+ });
2433
+ const userWithoutJoin = await adapter.create({
2434
+ model: "user",
2435
+ data: { ...await generate("user") },
2436
+ forceAllowId: true
2437
+ });
2438
+ const result = await adapter.findMany({
2439
+ model: "user",
2440
+ where: [{
2441
+ field: "id",
2442
+ value: userWithJoin.id,
2443
+ connector: "OR"
2444
+ }, {
2445
+ field: "id",
2446
+ value: userWithoutJoin.id,
2447
+ connector: "OR"
2448
+ }],
2449
+ join: { oneToOneTable: true }
2450
+ });
2451
+ expect(result).toBeDefined();
2452
+ expect(result.length).toBe(2);
2453
+ const resultWithJoin = result.find((u) => u.id === userWithJoin.id);
2454
+ const resultWithoutJoin = result.find((u) => u.id === userWithoutJoin.id);
2455
+ expect(resultWithJoin).toBeDefined();
2456
+ expect(resultWithJoin?.oneToOneTable).toBeDefined();
2457
+ expect(resultWithJoin?.oneToOneTable?.id).toBe(oneToOne.id);
2458
+ expect(resultWithoutJoin).toBeDefined();
2459
+ expect(resultWithoutJoin?.oneToOneTable).toBeNull();
2460
+ },
2461
+ "findMany - should return empty array for one-to-many join when joined records don't exist": async () => {
2462
+ const userWithSessions = await adapter.create({
2463
+ model: "user",
2464
+ data: { ...await generate("user") },
2465
+ forceAllowId: true
2466
+ });
2467
+ const session1 = await adapter.create({
2468
+ model: "session",
2469
+ data: {
2470
+ ...await generate("session"),
2471
+ userId: userWithSessions.id
2472
+ },
2473
+ forceAllowId: true
2474
+ });
2475
+ const session2 = await adapter.create({
2476
+ model: "session",
2477
+ data: {
2478
+ ...await generate("session"),
2479
+ userId: userWithSessions.id
2480
+ },
2481
+ forceAllowId: true
2482
+ });
2483
+ const userWithoutSessions = await adapter.create({
2484
+ model: "user",
2485
+ data: { ...await generate("user") },
2486
+ forceAllowId: true
2487
+ });
2488
+ const result = await adapter.findMany({
2489
+ model: "user",
2490
+ where: [{
2491
+ field: "id",
2492
+ value: userWithSessions.id,
2493
+ connector: "OR"
2494
+ }, {
2495
+ field: "id",
2496
+ value: userWithoutSessions.id,
2497
+ connector: "OR"
2498
+ }],
2499
+ join: { session: true }
2500
+ });
2501
+ expect(result).toBeDefined();
2502
+ expect(result.length).toBe(2);
2503
+ const resultWithSessions = result.find((u) => u.id === userWithSessions.id);
2504
+ const resultWithoutSessions = result.find((u) => u.id === userWithoutSessions.id);
2505
+ expect(resultWithSessions).toBeDefined();
2506
+ expect(Array.isArray(resultWithSessions?.session)).toBe(true);
2507
+ expect(resultWithSessions?.session).toHaveLength(2);
2508
+ expect(resultWithSessions?.session.some((s) => s.id === session1.id)).toBe(true);
2509
+ expect(resultWithSessions?.session.some((s) => s.id === session2.id)).toBe(true);
2510
+ expect(resultWithoutSessions).toBeDefined();
2511
+ expect(Array.isArray(resultWithoutSessions?.session)).toBe(true);
2512
+ expect(resultWithoutSessions?.session).toHaveLength(0);
2513
+ },
2514
+ "findMany - should handle mixed joins correctly when some are missing": async () => {
2515
+ await modifyBetterAuthOptions({ plugins: [{
2516
+ id: "one-to-one-test",
2517
+ schema: { oneToOneTable: { fields: { oneToOne: {
2518
+ type: "string",
2519
+ required: true,
2520
+ references: {
2521
+ field: "id",
2522
+ model: "user"
2523
+ },
2524
+ unique: true
2525
+ } } } }
2526
+ }] }, true);
2527
+ const user1 = await adapter.create({
2528
+ model: "user",
2529
+ data: { ...await generate("user") },
2530
+ forceAllowId: true
2531
+ });
2532
+ const oneToOne1 = await adapter.create({
2533
+ model: "oneToOneTable",
2534
+ data: { oneToOne: user1.id }
2535
+ });
2536
+ const session1 = await adapter.create({
2537
+ model: "session",
2538
+ data: {
2539
+ ...await generate("session"),
2540
+ userId: user1.id
2541
+ },
2542
+ forceAllowId: true
2543
+ });
2544
+ const user2 = await adapter.create({
2545
+ model: "user",
2546
+ data: { ...await generate("user") },
2547
+ forceAllowId: true
2548
+ });
2549
+ const oneToOne2 = await adapter.create({
2550
+ model: "oneToOneTable",
2551
+ data: { oneToOne: user2.id }
2552
+ });
2553
+ const user3 = await adapter.create({
2554
+ model: "user",
2555
+ data: { ...await generate("user") },
2556
+ forceAllowId: true
2557
+ });
2558
+ const session3 = await adapter.create({
2559
+ model: "session",
2560
+ data: {
2561
+ ...await generate("session"),
2562
+ userId: user3.id
2563
+ },
2564
+ forceAllowId: true
2565
+ });
2566
+ const user4 = await adapter.create({
2567
+ model: "user",
2568
+ data: { ...await generate("user") },
2569
+ forceAllowId: true
2570
+ });
2571
+ const result = await adapter.findMany({
2572
+ model: "user",
2573
+ where: [
2574
+ {
2575
+ field: "id",
2576
+ value: user1.id,
2577
+ connector: "OR"
2578
+ },
2579
+ {
2580
+ field: "id",
2581
+ value: user2.id,
2582
+ connector: "OR"
2583
+ },
2584
+ {
2585
+ field: "id",
2586
+ value: user3.id,
2587
+ connector: "OR"
2588
+ },
2589
+ {
2590
+ field: "id",
2591
+ value: user4.id,
2592
+ connector: "OR"
2593
+ }
2594
+ ],
2595
+ join: {
2596
+ oneToOneTable: true,
2597
+ session: true
2598
+ }
2599
+ });
2600
+ expect(result).toBeDefined();
2601
+ expect(result.length).toBe(4);
2602
+ const result1 = result.find((u) => u.id === user1.id);
2603
+ expect(result1).toBeDefined();
2604
+ expect(result1?.oneToOneTable).toBeDefined();
2605
+ expect(result1?.oneToOneTable?.id).toBe(oneToOne1.id);
2606
+ expect(Array.isArray(result1?.session)).toBe(true);
2607
+ expect(result1?.session).toHaveLength(1);
2608
+ expect(result1?.session[0]?.id).toBe(session1.id);
2609
+ const result2 = result.find((u) => u.id === user2.id);
2610
+ expect(result2).toBeDefined();
2611
+ expect(result2?.oneToOneTable).toBeDefined();
2612
+ expect(result2?.oneToOneTable?.id).toBe(oneToOne2.id);
2613
+ expect(Array.isArray(result2?.session)).toBe(true);
2614
+ expect(result2?.session).toHaveLength(0);
2615
+ const result3 = result.find((u) => u.id === user3.id);
2616
+ expect(result3).toBeDefined();
2617
+ expect(result3?.oneToOneTable).toBeNull();
2618
+ expect(Array.isArray(result3?.session)).toBe(true);
2619
+ expect(result3?.session).toHaveLength(1);
2620
+ expect(result3?.session[0]?.id).toBe(session3.id);
2621
+ const result4 = result.find((u) => u.id === user4.id);
2622
+ expect(result4).toBeDefined();
2623
+ expect(result4?.oneToOneTable).toBeNull();
2624
+ expect(Array.isArray(result4?.session)).toBe(true);
2625
+ expect(result4?.session).toHaveLength(0);
2626
+ },
2627
+ "create - should support arrays": {
2628
+ migrateBetterAuth: { plugins: [{
2629
+ id: "string-arrays-test",
2630
+ schema: { testModel: { fields: {
2631
+ stringArray: {
2632
+ type: "string[]",
2633
+ required: true
2634
+ },
2635
+ numberArray: {
2636
+ type: "number[]",
2637
+ required: true
2638
+ }
2639
+ } } }
2640
+ }] },
2641
+ test: async () => {
2642
+ const result = await adapter.create({
2643
+ model: "testModel",
2644
+ data: {
2645
+ stringArray: [
2646
+ "1",
2647
+ "2",
2648
+ "3"
2649
+ ],
2650
+ numberArray: [
2651
+ 1,
2652
+ 2,
2653
+ 3
2654
+ ]
2655
+ }
2656
+ });
2657
+ expect(result.stringArray).toEqual([
2658
+ "1",
2659
+ "2",
2660
+ "3"
2661
+ ]);
2662
+ expect(result.numberArray).toEqual([
2663
+ 1,
2664
+ 2,
2665
+ 3
2666
+ ]);
2667
+ const findResult = await adapter.findOne({
2668
+ model: "testModel",
2669
+ where: [{
2670
+ field: "id",
2671
+ value: result.id
2672
+ }]
2673
+ });
2674
+ expect(findResult).toEqual(result);
2675
+ expect(findResult?.stringArray).toEqual([
2676
+ "1",
2677
+ "2",
2678
+ "3"
2679
+ ]);
2680
+ expect(findResult?.numberArray).toEqual([
2681
+ 1,
2682
+ 2,
2683
+ 3
2684
+ ]);
2685
+ }
2686
+ },
2687
+ "create - should support json": {
2688
+ migrateBetterAuth: { plugins: [{
2689
+ id: "json-test",
2690
+ schema: { testModel: { fields: { json: {
2691
+ type: "json",
2692
+ required: true
2693
+ } } } }
2694
+ }] },
2695
+ test: async () => {
2696
+ const result = await adapter.create({
2697
+ model: "testModel",
2698
+ data: { json: { foo: "bar" } }
2699
+ });
2700
+ expect(result.json).toEqual({ foo: "bar" });
2701
+ const findResult = await adapter.findOne({
2702
+ model: "testModel",
2703
+ where: [{
2704
+ field: "id",
2705
+ value: result.id
2706
+ }]
2707
+ });
2708
+ expect(findResult).toEqual(result);
2709
+ expect(findResult?.json).toEqual({ foo: "bar" });
2710
+ console.log(findResult);
2711
+ }
2712
+ },
2713
+ "update - should support multiple where conditions under AND connector with unique field": async () => {
2714
+ const [user] = await insertRandom("user");
2715
+ const result = await adapter.update({
2716
+ model: "user",
2717
+ where: [{
2718
+ field: "email",
2719
+ value: user.email
2720
+ }, {
2721
+ field: "id",
2722
+ value: user.id
2723
+ }],
2724
+ update: { name: "Updated Name" }
2725
+ });
2726
+ expect(result).toBeDefined();
2727
+ expect(result.name).toBe("Updated Name");
2728
+ expect(result.email).toBe(user.email);
2729
+ expect(result.id).toBe(user.id);
2730
+ },
2731
+ "findMany - eq operator with null value (single condition) should use IS NULL": async () => {
2732
+ const withNull = await adapter.create({
2733
+ model: "user",
2734
+ data: {
2735
+ ...await generate("user"),
2736
+ image: null
2737
+ },
2738
+ forceAllowId: true
2739
+ });
2740
+ const withImage = await adapter.create({
2741
+ model: "user",
2742
+ data: {
2743
+ ...await generate("user"),
2744
+ image: "https://example.com/avatar.png"
2745
+ },
2746
+ forceAllowId: true
2747
+ });
2748
+ const nullIds = (await adapter.findMany({
2749
+ model: "user",
2750
+ where: [{
2751
+ field: "image",
2752
+ operator: "eq",
2753
+ value: null
2754
+ }]
2755
+ })).map((u) => u.id);
2756
+ expect(nullIds).toContain(withNull.id);
2757
+ expect(nullIds).not.toContain(withImage.id);
2758
+ const notNullIds = (await adapter.findMany({
2759
+ model: "user",
2760
+ where: [{
2761
+ field: "image",
2762
+ operator: "ne",
2763
+ value: null
2764
+ }]
2765
+ })).map((u) => u.id);
2766
+ expect(notNullIds).not.toContain(withNull.id);
2767
+ expect(notNullIds).toContain(withImage.id);
2768
+ },
2769
+ "findMany - eq and ne operators with null value in AND group should use IS NULL / IS NOT NULL": async () => {
2770
+ const nullVerified = await adapter.create({
2771
+ model: "user",
2772
+ data: {
2773
+ ...await generate("user"),
2774
+ image: null,
2775
+ emailVerified: true
2776
+ },
2777
+ forceAllowId: true
2778
+ });
2779
+ const nullUnverified = await adapter.create({
2780
+ model: "user",
2781
+ data: {
2782
+ ...await generate("user"),
2783
+ image: null,
2784
+ emailVerified: false
2785
+ },
2786
+ forceAllowId: true
2787
+ });
2788
+ const imageVerified = await adapter.create({
2789
+ model: "user",
2790
+ data: {
2791
+ ...await generate("user"),
2792
+ image: "https://example.com/avatar.png",
2793
+ emailVerified: true
2794
+ },
2795
+ forceAllowId: true
2796
+ });
2797
+ const eqIds = (await adapter.findMany({
2798
+ model: "user",
2799
+ where: [{
2800
+ field: "image",
2801
+ operator: "eq",
2802
+ value: null,
2803
+ connector: "AND"
2804
+ }, {
2805
+ field: "emailVerified",
2806
+ value: true,
2807
+ connector: "AND"
2808
+ }]
2809
+ })).map((u) => u.id);
2810
+ expect(eqIds).toContain(nullVerified.id);
2811
+ expect(eqIds).not.toContain(nullUnverified.id);
2812
+ expect(eqIds).not.toContain(imageVerified.id);
2813
+ const neIds = (await adapter.findMany({
2814
+ model: "user",
2815
+ where: [{
2816
+ field: "image",
2817
+ operator: "ne",
2818
+ value: null,
2819
+ connector: "AND"
2820
+ }, {
2821
+ field: "emailVerified",
2822
+ value: true,
2823
+ connector: "AND"
2824
+ }]
2825
+ })).map((u) => u.id);
2826
+ expect(neIds).not.toContain(nullVerified.id);
2827
+ expect(neIds).not.toContain(nullUnverified.id);
2828
+ expect(neIds).toContain(imageVerified.id);
2829
+ },
2830
+ "findMany - eq and ne operators with null value in OR group should use IS NULL / IS NOT NULL": async () => {
2831
+ const withNull = await adapter.create({
2832
+ model: "user",
2833
+ data: {
2834
+ ...await generate("user"),
2835
+ image: null
2836
+ },
2837
+ forceAllowId: true
2838
+ });
2839
+ const targetImage = await adapter.create({
2840
+ model: "user",
2841
+ data: {
2842
+ ...await generate("user"),
2843
+ image: "https://example.com/target.png"
2844
+ },
2845
+ forceAllowId: true
2846
+ });
2847
+ const otherImage = await adapter.create({
2848
+ model: "user",
2849
+ data: {
2850
+ ...await generate("user"),
2851
+ image: "https://example.com/other.png"
2852
+ },
2853
+ forceAllowId: true
2854
+ });
2855
+ const eqIds = (await adapter.findMany({
2856
+ model: "user",
2857
+ where: [{
2858
+ field: "image",
2859
+ operator: "eq",
2860
+ value: null,
2861
+ connector: "OR"
2862
+ }, {
2863
+ field: "email",
2864
+ value: targetImage.email,
2865
+ connector: "OR"
2866
+ }]
2867
+ })).map((u) => u.id);
2868
+ expect(eqIds).toContain(withNull.id);
2869
+ expect(eqIds).toContain(targetImage.id);
2870
+ expect(eqIds).not.toContain(otherImage.id);
2871
+ const neIds = (await adapter.findMany({
2872
+ model: "user",
2873
+ where: [{
2874
+ field: "image",
2875
+ operator: "ne",
2876
+ value: null,
2877
+ connector: "OR"
2878
+ }, {
2879
+ field: "email",
2880
+ value: withNull.email,
2881
+ connector: "OR"
2882
+ }]
2883
+ })).map((u) => u.id);
2884
+ expect(neIds).toContain(withNull.id);
2885
+ expect(neIds).toContain(targetImage.id);
2886
+ expect(neIds).toContain(otherImage.id);
2887
+ },
2888
+ "update - should return updated record when where condition uses null value": async () => {
2889
+ const withNull = await adapter.create({
2890
+ model: "user",
2891
+ data: {
2892
+ ...await generate("user"),
2893
+ image: null
2894
+ },
2895
+ forceAllowId: true
2896
+ });
2897
+ const result = await adapter.update({
2898
+ model: "user",
2899
+ where: [{
2900
+ field: "id",
2901
+ value: withNull.id
2902
+ }, {
2903
+ field: "image",
2904
+ operator: "eq",
2905
+ value: null
2906
+ }],
2907
+ update: { name: "null-where-updated" }
2908
+ });
2909
+ expect(result).toBeDefined();
2910
+ expect(result.id).toBe(withNull.id);
2911
+ expect(result.name).toBe("null-where-updated");
2912
+ }
2913
+ };
2914
+ };
2915
+ const getTestKeys = () => Object.keys(getNormalTestSuiteTests({}));
2916
+ const enableJoinTests = getTestKeys().reduce((acc, test) => {
2917
+ if (test.includes("join")) acc[test] = false;
2918
+ return acc;
2919
+ }, {});
2920
+ //#endregion
2921
+ export { enableJoinTests, getNormalTestSuiteTests, normalTestSuite };
2922
+
2923
+ //# sourceMappingURL=basic.mjs.map