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