@geekmidas/testkit 0.0.16 → 0.1.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/README.md +302 -199
- package/dist/{KyselyFactory-DRQ83r0o.d.cts → KyselyFactory-BFygzOlO.d.cts} +21 -8
- package/dist/{KyselyFactory-Cx3sezwH.d.mts → KyselyFactory-BTdygZ-i.d.mts} +21 -8
- package/dist/{KyselyFactory-BcYkC0t2.mjs → KyselyFactory-CXY5gJk2.mjs} +25 -12
- package/dist/KyselyFactory-CXY5gJk2.mjs.map +1 -0
- package/dist/{KyselyFactory-Cf0o2YxO.cjs → KyselyFactory-DaaCykWP.cjs} +25 -12
- package/dist/KyselyFactory-DaaCykWP.cjs.map +1 -0
- package/dist/KyselyFactory.cjs +1 -1
- package/dist/KyselyFactory.d.cts +1 -1
- package/dist/KyselyFactory.d.mts +1 -1
- package/dist/KyselyFactory.mjs +1 -1
- package/dist/{ObjectionFactory-C-59Hjwj.d.mts → ObjectionFactory-BagGjikT.d.mts} +24 -11
- package/dist/{ObjectionFactory-C4X78k0B.d.cts → ObjectionFactory-CeSIN3kZ.d.cts} +24 -11
- package/dist/{ObjectionFactory-CDriunkS.cjs → ObjectionFactory-Eb04AOnv.cjs} +28 -15
- package/dist/ObjectionFactory-Eb04AOnv.cjs.map +1 -0
- package/dist/{ObjectionFactory-8hebmnai.mjs → ObjectionFactory-zf2fLKrL.mjs} +28 -15
- package/dist/ObjectionFactory-zf2fLKrL.mjs.map +1 -0
- package/dist/ObjectionFactory.cjs +1 -1
- package/dist/ObjectionFactory.d.cts +1 -1
- package/dist/ObjectionFactory.d.mts +1 -1
- package/dist/ObjectionFactory.mjs +1 -1
- package/dist/{VitestKyselyTransactionIsolator-COCVfvfr.d.mts → VitestKyselyTransactionIsolator-4HOeLQ0d.d.cts} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs → VitestKyselyTransactionIsolator-DX_VPKS-.cjs} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs.map → VitestKyselyTransactionIsolator-DX_VPKS-.cjs.map} +1 -1
- package/dist/{VitestKyselyTransactionIsolator-DYUYVEh9.d.cts → VitestKyselyTransactionIsolator-DnyZMaA-.d.mts} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs → VitestKyselyTransactionIsolator-XDL3ngs_.mjs} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs.map → VitestKyselyTransactionIsolator-XDL3ngs_.mjs.map} +1 -1
- package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
- package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
- package/dist/{VitestObjectionTransactionIsolator-b973r9O1.d.mts → VitestObjectionTransactionIsolator-COVDlpEo.d.cts} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs → VitestObjectionTransactionIsolator-D_tlOtq8.cjs} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs.map → VitestObjectionTransactionIsolator-D_tlOtq8.cjs.map} +1 -1
- package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs → VitestObjectionTransactionIsolator-_EhJKu_O.mjs} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs.map → VitestObjectionTransactionIsolator-_EhJKu_O.mjs.map} +1 -1
- package/dist/{VitestObjectionTransactionIsolator-CJ4ds5Qv.d.cts → VitestObjectionTransactionIsolator-lZUSz1w0.d.mts} +2 -2
- package/dist/VitestObjectionTransactionIsolator.cjs +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
- package/dist/VitestObjectionTransactionIsolator.mjs +2 -2
- package/dist/{VitestTransactionIsolator-CskiiJbW.mjs → VitestTransactionIsolator-BIaMs4c2.mjs} +40 -2
- package/dist/VitestTransactionIsolator-BIaMs4c2.mjs.map +1 -0
- package/dist/{VitestTransactionIsolator-BQ5FpLtC.cjs → VitestTransactionIsolator-BKIrj3Uy.cjs} +45 -1
- package/dist/VitestTransactionIsolator-BKIrj3Uy.cjs.map +1 -0
- package/dist/{VitestTransactionIsolator-CsfJBxcb.d.mts → VitestTransactionIsolator-CyG_i_Nj.d.cts} +61 -3
- package/dist/{VitestTransactionIsolator-DdLNODZg.d.cts → VitestTransactionIsolator-DWDbnITQ.d.mts} +61 -3
- package/dist/VitestTransactionIsolator.cjs +3 -2
- package/dist/VitestTransactionIsolator.d.cts +2 -2
- package/dist/VitestTransactionIsolator.d.mts +2 -2
- package/dist/VitestTransactionIsolator.mjs +2 -2
- package/dist/better-auth.cjs +242 -0
- package/dist/better-auth.cjs.map +1 -0
- package/dist/better-auth.d.cts +17 -0
- package/dist/better-auth.d.mts +17 -0
- package/dist/better-auth.mjs +241 -0
- package/dist/better-auth.mjs.map +1 -0
- package/dist/{directory-B4oYx02C.d.mts → directory-BXavAeJZ.d.mts} +3 -3
- package/dist/{directory-BUcnztHI.d.cts → directory-DlkPEzL4.d.cts} +3 -3
- package/dist/kysely.cjs +58 -4
- package/dist/kysely.cjs.map +1 -1
- package/dist/kysely.d.cts +58 -5
- package/dist/kysely.d.mts +58 -5
- package/dist/kysely.mjs +57 -5
- package/dist/kysely.mjs.map +1 -1
- package/dist/objection.cjs +54 -4
- package/dist/objection.cjs.map +1 -1
- package/dist/objection.d.cts +54 -5
- package/dist/objection.d.mts +54 -5
- package/dist/objection.mjs +53 -5
- package/dist/objection.mjs.map +1 -1
- package/dist/os/directory.d.cts +1 -1
- package/dist/os/directory.d.mts +1 -1
- package/dist/os/index.d.cts +1 -1
- package/dist/os/index.d.mts +1 -1
- package/package.json +13 -3
- package/src/KyselyFactory.ts +29 -16
- package/src/ObjectionFactory.ts +34 -19
- package/src/VitestTransactionIsolator.ts +110 -2
- package/src/__tests__/KyselyFactory.spec.ts +10 -10
- package/src/__tests__/ObjectionFactory.spec.ts +9 -12
- package/src/__tests__/better-auth.spec.ts +21 -0
- package/src/__tests__/integration.spec.ts +171 -14
- package/src/better-auth.ts +325 -0
- package/src/kysely.ts +66 -0
- package/src/objection.ts +61 -0
- package/dist/KyselyFactory-BcYkC0t2.mjs.map +0 -1
- package/dist/KyselyFactory-Cf0o2YxO.cjs.map +0 -1
- package/dist/ObjectionFactory-8hebmnai.mjs.map +0 -1
- package/dist/ObjectionFactory-CDriunkS.cjs.map +0 -1
- package/dist/VitestTransactionIsolator-BQ5FpLtC.cjs.map +0 -1
- package/dist/VitestTransactionIsolator-CskiiJbW.mjs.map +0 -1
package/src/ObjectionFactory.ts
CHANGED
|
@@ -19,17 +19,17 @@ import { type FakerFactory, faker } from './faker.ts';
|
|
|
19
19
|
*
|
|
20
20
|
* // Create builders
|
|
21
21
|
* const builders = {
|
|
22
|
-
* user: (attrs) =>
|
|
22
|
+
* user: ObjectionFactory.createBuilder(User, ({ attrs, faker }) => ({
|
|
23
23
|
* id: faker.string.uuid(),
|
|
24
24
|
* name: faker.person.fullName(),
|
|
25
25
|
* email: faker.internet.email(),
|
|
26
26
|
* ...attrs
|
|
27
|
-
* }),
|
|
28
|
-
* post: (attrs) =>
|
|
27
|
+
* })),
|
|
28
|
+
* post: ObjectionFactory.createBuilder(Post, ({ attrs }) => ({
|
|
29
29
|
* title: 'Test Post',
|
|
30
30
|
* content: 'Test content',
|
|
31
31
|
* ...attrs
|
|
32
|
-
* })
|
|
32
|
+
* })),
|
|
33
33
|
* };
|
|
34
34
|
*
|
|
35
35
|
* // Create factory instance
|
|
@@ -65,15 +65,15 @@ export class ObjectionFactory<
|
|
|
65
65
|
* @template Result - The result type (defaults to the model instance)
|
|
66
66
|
*
|
|
67
67
|
* @param ModelClass - The Objection.js Model class
|
|
68
|
-
* @param
|
|
68
|
+
* @param defaults - Optional function to provide default values (receives destructured context)
|
|
69
69
|
* @param autoInsert - Whether to automatically insert the record (default: true)
|
|
70
70
|
* @returns A builder function that creates and optionally inserts records
|
|
71
71
|
*
|
|
72
72
|
* @example
|
|
73
73
|
* ```typescript
|
|
74
|
-
* // Create a simple builder with defaults
|
|
74
|
+
* // Create a simple builder with defaults - destructure only what you need
|
|
75
75
|
* const userBuilder = ObjectionFactory.createBuilder(User,
|
|
76
|
-
* (attrs,
|
|
76
|
+
* ({ attrs, faker }) => ({
|
|
77
77
|
* id: faker.string.uuid(),
|
|
78
78
|
* name: faker.person.fullName(),
|
|
79
79
|
* email: faker.internet.email(),
|
|
@@ -82,9 +82,17 @@ export class ObjectionFactory<
|
|
|
82
82
|
* })
|
|
83
83
|
* );
|
|
84
84
|
*
|
|
85
|
+
* // Only need faker? Just destructure that
|
|
86
|
+
* const leaveTypeBuilder = ObjectionFactory.createBuilder(LeaveType,
|
|
87
|
+
* ({ faker }) => ({
|
|
88
|
+
* name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
|
|
89
|
+
* code: faker.string.alpha({ length: 3, casing: 'upper' }),
|
|
90
|
+
* })
|
|
91
|
+
* );
|
|
92
|
+
*
|
|
85
93
|
* // Create a builder that doesn't auto-insert (useful for nested inserts)
|
|
86
94
|
* const addressBuilder = ObjectionFactory.createBuilder(Address,
|
|
87
|
-
* (attrs) => ({
|
|
95
|
+
* ({ attrs }) => ({
|
|
88
96
|
* street: '123 Main St',
|
|
89
97
|
* city: 'Anytown',
|
|
90
98
|
* ...attrs
|
|
@@ -94,7 +102,7 @@ export class ObjectionFactory<
|
|
|
94
102
|
*
|
|
95
103
|
* // Use with relations
|
|
96
104
|
* const postBuilder = ObjectionFactory.createBuilder(Post,
|
|
97
|
-
* async (attrs, factory) => ({
|
|
105
|
+
* async ({ attrs, factory, faker }) => ({
|
|
98
106
|
* title: faker.lorem.sentence(),
|
|
99
107
|
* content: faker.lorem.paragraphs(),
|
|
100
108
|
* authorId: attrs.authorId || (await factory.insert('user')).id,
|
|
@@ -110,12 +118,14 @@ export class ObjectionFactory<
|
|
|
110
118
|
Result = InstanceType<TModel>,
|
|
111
119
|
>(
|
|
112
120
|
ModelClass: TModel,
|
|
113
|
-
|
|
114
|
-
attrs: Attrs
|
|
115
|
-
factory: Factory
|
|
116
|
-
db: Knex
|
|
117
|
-
faker: FakerFactory
|
|
118
|
-
) =>
|
|
121
|
+
defaults?: (context: {
|
|
122
|
+
attrs: Attrs;
|
|
123
|
+
factory: Factory;
|
|
124
|
+
db: Knex;
|
|
125
|
+
faker: FakerFactory;
|
|
126
|
+
}) =>
|
|
127
|
+
| Partial<InstanceType<TModel>>
|
|
128
|
+
| Promise<Partial<InstanceType<TModel>>>,
|
|
119
129
|
autoInsert?: boolean,
|
|
120
130
|
): (
|
|
121
131
|
attrs: Attrs,
|
|
@@ -127,15 +137,20 @@ export class ObjectionFactory<
|
|
|
127
137
|
attrs: Attrs,
|
|
128
138
|
factory: Factory,
|
|
129
139
|
db: Knex,
|
|
130
|
-
|
|
140
|
+
fakerInstance: FakerFactory,
|
|
131
141
|
) => {
|
|
132
142
|
// Start with attributes
|
|
133
143
|
let data: Partial<InstanceType<TModel>> = { ...attrs };
|
|
134
144
|
|
|
135
145
|
// Apply defaults
|
|
136
|
-
if (
|
|
137
|
-
const
|
|
138
|
-
|
|
146
|
+
if (defaults) {
|
|
147
|
+
const defaultValues = await defaults({
|
|
148
|
+
attrs,
|
|
149
|
+
factory,
|
|
150
|
+
db,
|
|
151
|
+
faker: fakerInstance,
|
|
152
|
+
});
|
|
153
|
+
data = { ...defaultValues, ...data };
|
|
139
154
|
}
|
|
140
155
|
|
|
141
156
|
// Create model instance
|
|
@@ -5,8 +5,9 @@ import type { TestAPI } from 'vitest';
|
|
|
5
5
|
* Used with Vitest's test.extend() API to inject transactions into tests.
|
|
6
6
|
*
|
|
7
7
|
* @template Transaction - The transaction type specific to the database driver
|
|
8
|
+
* @template Extended - Additional context properties provided by the extend function
|
|
8
9
|
*/
|
|
9
|
-
export interface DatabaseFixtures<Transaction> {
|
|
10
|
+
export interface DatabaseFixtures<Transaction, Extended = object> {
|
|
10
11
|
/**
|
|
11
12
|
* The database transaction available to the test.
|
|
12
13
|
* All database operations should use this transaction to ensure proper rollback.
|
|
@@ -14,6 +15,31 @@ export interface DatabaseFixtures<Transaction> {
|
|
|
14
15
|
trx: Transaction;
|
|
15
16
|
}
|
|
16
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Combined fixtures type that merges the base transaction fixture with extended context.
|
|
20
|
+
*/
|
|
21
|
+
export type ExtendedDatabaseFixtures<
|
|
22
|
+
Transaction,
|
|
23
|
+
Extended = object,
|
|
24
|
+
> = DatabaseFixtures<Transaction> & Extended;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Function type for extending test context with additional properties.
|
|
28
|
+
* Receives the transaction and returns additional context to be merged with { trx }.
|
|
29
|
+
*
|
|
30
|
+
* @template Transaction - The transaction type
|
|
31
|
+
* @template Extended - The type of additional context to provide
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```typescript
|
|
35
|
+
* const extendContext: ExtendContextFn<Transaction<DB>, { factory: KyselyFactory }> =
|
|
36
|
+
* (trx) => ({ factory: new KyselyFactory(builders, seeds, trx) });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export type ExtendContextFn<Transaction, Extended> = (
|
|
40
|
+
trx: Transaction,
|
|
41
|
+
) => Extended | Promise<Extended>;
|
|
42
|
+
|
|
17
43
|
/**
|
|
18
44
|
* PostgreSQL transaction isolation levels.
|
|
19
45
|
* Controls the visibility of concurrent transactions.
|
|
@@ -123,7 +149,7 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
123
149
|
) {
|
|
124
150
|
return this.api.extend<DatabaseFixtures<Transaction>>({
|
|
125
151
|
// This fixture automatically provides a transaction to each test
|
|
126
|
-
trx: async ({}, use) => {
|
|
152
|
+
trx: async ({}, use: (value: Transaction) => Promise<void>) => {
|
|
127
153
|
// Create a custom error class for rollback
|
|
128
154
|
class TestRollback extends Error {
|
|
129
155
|
constructor() {
|
|
@@ -168,3 +194,85 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
168
194
|
|
|
169
195
|
export type DatabaseConnectionFn<Conn> = () => Conn | Promise<Conn>;
|
|
170
196
|
export type DatabaseConnection<Conn> = DatabaseConnectionFn<Conn>;
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Type for fixture creator functions that depend on the transaction.
|
|
200
|
+
* Each function receives the transaction and returns the fixture value.
|
|
201
|
+
*/
|
|
202
|
+
export type FixtureCreators<
|
|
203
|
+
Transaction,
|
|
204
|
+
Extended extends Record<string, unknown>,
|
|
205
|
+
> = {
|
|
206
|
+
[K in keyof Extended]: (
|
|
207
|
+
trx: Transaction,
|
|
208
|
+
) => Extended[K] | Promise<Extended[K]>;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Extends a wrapped test API with additional fixtures that depend on the transaction.
|
|
213
|
+
* This allows composing test context with factories, repositories, or other helpers.
|
|
214
|
+
*
|
|
215
|
+
* @template Transaction - The transaction type
|
|
216
|
+
* @template Extended - The type of additional context to provide
|
|
217
|
+
* @param wrappedTest - The base wrapped test from wrapVitestWithTransaction
|
|
218
|
+
* @param fixtures - Object mapping fixture names to creator functions
|
|
219
|
+
* @returns An extended test API with both trx and the additional fixtures
|
|
220
|
+
*
|
|
221
|
+
* @example
|
|
222
|
+
* ```typescript
|
|
223
|
+
* import { wrapVitestKyselyTransaction, extendWithFixtures } from '@geekmidas/testkit/kysely';
|
|
224
|
+
*
|
|
225
|
+
* // Create base wrapped test
|
|
226
|
+
* const baseTest = wrapVitestKyselyTransaction(test, db, createTestTables);
|
|
227
|
+
*
|
|
228
|
+
* // Extend with fixtures
|
|
229
|
+
* const it = extendWithFixtures(baseTest, {
|
|
230
|
+
* factory: (trx) => new KyselyFactory(builders, seeds, trx),
|
|
231
|
+
* userRepo: (trx) => new UserRepository(trx),
|
|
232
|
+
* });
|
|
233
|
+
*
|
|
234
|
+
* // Use in tests - trx and all fixtures are available
|
|
235
|
+
* it('should create user with factory', async ({ trx, factory, userRepo }) => {
|
|
236
|
+
* const user = await factory.insert('user', { name: 'Test' });
|
|
237
|
+
* expect(user).toBeDefined();
|
|
238
|
+
* });
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
export function extendWithFixtures<
|
|
242
|
+
Transaction,
|
|
243
|
+
Extended extends Record<string, unknown>,
|
|
244
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
245
|
+
T extends ReturnType<TestAPI['extend']> = any,
|
|
246
|
+
>(
|
|
247
|
+
wrappedTest: T,
|
|
248
|
+
fixtures: FixtureCreators<Transaction, Extended>,
|
|
249
|
+
): T & {
|
|
250
|
+
<C extends object>(
|
|
251
|
+
name: string,
|
|
252
|
+
fn: (
|
|
253
|
+
context: DatabaseFixtures<Transaction> & Extended & C,
|
|
254
|
+
) => Promise<void>,
|
|
255
|
+
): void;
|
|
256
|
+
<C extends object>(
|
|
257
|
+
name: string,
|
|
258
|
+
options: object,
|
|
259
|
+
fn: (
|
|
260
|
+
context: DatabaseFixtures<Transaction> & Extended & C,
|
|
261
|
+
) => Promise<void>,
|
|
262
|
+
): void;
|
|
263
|
+
} {
|
|
264
|
+
// Build fixture definitions for Vitest's extend API
|
|
265
|
+
const fixtureDefinitions: Record<string, any> = {};
|
|
266
|
+
|
|
267
|
+
for (const [key, creator] of Object.entries(fixtures)) {
|
|
268
|
+
fixtureDefinitions[key] = async (
|
|
269
|
+
{ trx }: { trx: Transaction },
|
|
270
|
+
use: (value: unknown) => Promise<void>,
|
|
271
|
+
) => {
|
|
272
|
+
const value = await (creator as (trx: Transaction) => unknown)(trx);
|
|
273
|
+
await use(value);
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
return (wrappedTest as any).extend(fixtureDefinitions);
|
|
278
|
+
}
|
|
@@ -24,7 +24,7 @@ describe('KyselyFactory', () => {
|
|
|
24
24
|
async ({ trx }) => {
|
|
25
25
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
26
26
|
'users',
|
|
27
|
-
async (attrs) => ({
|
|
27
|
+
async ({ attrs }) => ({
|
|
28
28
|
name: 'John Doe',
|
|
29
29
|
email: `user${Date.now()}@example.com`,
|
|
30
30
|
createdAt: new Date(),
|
|
@@ -57,7 +57,7 @@ describe('KyselyFactory', () => {
|
|
|
57
57
|
async ({ trx }) => {
|
|
58
58
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
59
59
|
'users',
|
|
60
|
-
async (attrs) => ({
|
|
60
|
+
async ({ attrs }) => ({
|
|
61
61
|
name: 'John Doe',
|
|
62
62
|
email: `user${Date.now()}@example.com`,
|
|
63
63
|
createdAt: new Date(),
|
|
@@ -98,7 +98,7 @@ describe('KyselyFactory', () => {
|
|
|
98
98
|
|
|
99
99
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
100
100
|
'posts',
|
|
101
|
-
async (attrs, factory) => {
|
|
101
|
+
async ({ attrs, factory }) => {
|
|
102
102
|
// Create a user if userId not provided
|
|
103
103
|
if (!attrs.userId) {
|
|
104
104
|
const user = await factory.insert('user');
|
|
@@ -156,7 +156,7 @@ describe('KyselyFactory', () => {
|
|
|
156
156
|
async ({ trx }) => {
|
|
157
157
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
158
158
|
'users',
|
|
159
|
-
async (
|
|
159
|
+
async () => ({
|
|
160
160
|
name: 'John Doe',
|
|
161
161
|
email: `user${Date.now()}-${Math.random()}@example.com`,
|
|
162
162
|
createdAt: new Date(),
|
|
@@ -189,7 +189,7 @@ describe('KyselyFactory', () => {
|
|
|
189
189
|
async ({ trx }) => {
|
|
190
190
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
191
191
|
'users',
|
|
192
|
-
async (
|
|
192
|
+
async () => ({
|
|
193
193
|
email: `user${Date.now()}-${Math.random()}@example.com`,
|
|
194
194
|
createdAt: new Date(),
|
|
195
195
|
}),
|
|
@@ -233,7 +233,7 @@ describe('KyselyFactory', () => {
|
|
|
233
233
|
let counter = 0;
|
|
234
234
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
235
235
|
'users',
|
|
236
|
-
async (
|
|
236
|
+
async () => {
|
|
237
237
|
// Simulate async operation
|
|
238
238
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
239
239
|
counter++;
|
|
@@ -307,7 +307,7 @@ describe('KyselyFactory', () => {
|
|
|
307
307
|
async ({ trx }) => {
|
|
308
308
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
309
309
|
'users',
|
|
310
|
-
async (attrs) => ({
|
|
310
|
+
async ({ attrs }) => ({
|
|
311
311
|
name: 'John Doe',
|
|
312
312
|
email: `user${Date.now()}@example.com`,
|
|
313
313
|
createdAt: new Date(),
|
|
@@ -364,7 +364,7 @@ describe('KyselyFactory', () => {
|
|
|
364
364
|
async ({ trx }) => {
|
|
365
365
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
366
366
|
'users',
|
|
367
|
-
async (attrs) => ({
|
|
367
|
+
async ({ attrs }) => ({
|
|
368
368
|
name: 'John Doe',
|
|
369
369
|
email: `user${Date.now()}@example.com`,
|
|
370
370
|
createdAt: new Date(),
|
|
@@ -400,7 +400,7 @@ describe('KyselyFactory', () => {
|
|
|
400
400
|
async ({ trx }) => {
|
|
401
401
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
402
402
|
'users',
|
|
403
|
-
async (attrs) => ({
|
|
403
|
+
async ({ attrs }) => ({
|
|
404
404
|
name: 'John Doe',
|
|
405
405
|
email: `user${Date.now()}@example.com`,
|
|
406
406
|
createdAt: new Date(),
|
|
@@ -409,7 +409,7 @@ describe('KyselyFactory', () => {
|
|
|
409
409
|
|
|
410
410
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
411
411
|
'posts',
|
|
412
|
-
async (
|
|
412
|
+
async ({ factory }) => {
|
|
413
413
|
const user = await factory.insert('user');
|
|
414
414
|
return {
|
|
415
415
|
title: 'Default Post',
|
|
@@ -323,7 +323,7 @@ describe('ObjectionFactory', () => {
|
|
|
323
323
|
it('should create a builder function with auto-insert', async ({ trx }) => {
|
|
324
324
|
const userBuilder = ObjectionFactory.createBuilder(
|
|
325
325
|
User,
|
|
326
|
-
(attrs,
|
|
326
|
+
({ attrs, faker }) => ({
|
|
327
327
|
name: faker.person.fullName(),
|
|
328
328
|
...attrs,
|
|
329
329
|
}),
|
|
@@ -344,7 +344,7 @@ describe('ObjectionFactory', () => {
|
|
|
344
344
|
}) => {
|
|
345
345
|
const userBuilder = ObjectionFactory.createBuilder(
|
|
346
346
|
User,
|
|
347
|
-
(attrs) => ({
|
|
347
|
+
({ attrs }) => ({
|
|
348
348
|
name: 'No Insert User',
|
|
349
349
|
...attrs,
|
|
350
350
|
}),
|
|
@@ -369,8 +369,8 @@ describe('ObjectionFactory', () => {
|
|
|
369
369
|
|
|
370
370
|
const userBuilder = ObjectionFactory.createBuilder(
|
|
371
371
|
User,
|
|
372
|
-
(attrs, factory, db, fakerInstance) => {
|
|
373
|
-
capturedFactory =
|
|
372
|
+
({ attrs, factory: passedFactory, db, faker: fakerInstance }) => {
|
|
373
|
+
capturedFactory = passedFactory;
|
|
374
374
|
capturedDb = db;
|
|
375
375
|
capturedFaker = fakerInstance;
|
|
376
376
|
|
|
@@ -394,7 +394,7 @@ describe('ObjectionFactory', () => {
|
|
|
394
394
|
it('should handle async item functions', async ({ trx }) => {
|
|
395
395
|
const userBuilder = ObjectionFactory.createBuilder(
|
|
396
396
|
User,
|
|
397
|
-
async (attrs
|
|
397
|
+
async ({ attrs }) => {
|
|
398
398
|
// Simulate async operation
|
|
399
399
|
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
400
400
|
|
|
@@ -433,13 +433,10 @@ describe('ObjectionFactory', () => {
|
|
|
433
433
|
});
|
|
434
434
|
|
|
435
435
|
it('should allow overriding default values', async ({ trx }) => {
|
|
436
|
-
const userBuilder = ObjectionFactory.createBuilder(
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
...attrs,
|
|
441
|
-
}),
|
|
442
|
-
);
|
|
436
|
+
const userBuilder = ObjectionFactory.createBuilder(User, ({ attrs }) => ({
|
|
437
|
+
name: 'Default Name',
|
|
438
|
+
...attrs,
|
|
439
|
+
}));
|
|
443
440
|
|
|
444
441
|
const builders = { user: userBuilder };
|
|
445
442
|
const factory = new ObjectionFactory(builders, {}, trx);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { runAdapterTest } from 'better-auth/adapters/test';
|
|
2
|
+
import { afterAll, describe } from 'vitest';
|
|
3
|
+
import { memoryAdapter } from '../better-auth';
|
|
4
|
+
|
|
5
|
+
describe.skip('Memory Adapter Tests', async () => {
|
|
6
|
+
afterAll(async () => {
|
|
7
|
+
// Run DB cleanup here...
|
|
8
|
+
});
|
|
9
|
+
const adapter = memoryAdapter({
|
|
10
|
+
debugLogs: {
|
|
11
|
+
// If your adapter config allows passing in debug logs, then pass this here.
|
|
12
|
+
isRunningAdapterTests: true, // This is our super secret flag to let us know to only log debug logs if a test fails.
|
|
13
|
+
},
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
await runAdapterTest({
|
|
17
|
+
getAdapter: async (betterAuthOptions = {}) => {
|
|
18
|
+
return adapter(betterAuthOptions);
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
});
|
|
@@ -3,7 +3,7 @@ import { TEST_DATABASE_CONFIG } from '../../test/globalSetup';
|
|
|
3
3
|
import { type TestDatabase, createTestTables } from '../../test/helpers';
|
|
4
4
|
import { KyselyFactory } from '../KyselyFactory';
|
|
5
5
|
import { createKyselyDb } from '../helpers';
|
|
6
|
-
import { wrapVitestKyselyTransaction } from '../kysely';
|
|
6
|
+
import { extendWithFixtures, wrapVitestKyselyTransaction } from '../kysely';
|
|
7
7
|
|
|
8
8
|
const db = () => createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
|
|
9
9
|
const it = wrapVitestKyselyTransaction<TestDatabase>(
|
|
@@ -18,7 +18,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
18
18
|
// Create builders for all entities
|
|
19
19
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
20
20
|
'users',
|
|
21
|
-
async (
|
|
21
|
+
async () => ({
|
|
22
22
|
name: 'John Doe',
|
|
23
23
|
email: `user${Date.now()}-${Math.random()}@example.com`,
|
|
24
24
|
role: 'user' as const,
|
|
@@ -29,7 +29,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
29
29
|
|
|
30
30
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
31
31
|
'posts',
|
|
32
|
-
async (attrs, factory) => {
|
|
32
|
+
async ({ attrs, factory }) => {
|
|
33
33
|
// Create a user if no userId provided
|
|
34
34
|
if (!attrs.userId) {
|
|
35
35
|
const user = await factory.insert('user');
|
|
@@ -55,7 +55,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
55
55
|
const commentBuilder = KyselyFactory.createBuilder<
|
|
56
56
|
TestDatabase,
|
|
57
57
|
'comments'
|
|
58
|
-
>('comments', async (attrs, factory) => {
|
|
58
|
+
>('comments', async ({ attrs, factory }) => {
|
|
59
59
|
let postId = attrs.postId;
|
|
60
60
|
let userId = attrs.userId;
|
|
61
61
|
|
|
@@ -152,7 +152,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
152
152
|
|
|
153
153
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
154
154
|
'users',
|
|
155
|
-
async (
|
|
155
|
+
async () => ({
|
|
156
156
|
name: 'Default User',
|
|
157
157
|
email: `user${Date.now()}-${Math.random()}@example.com`,
|
|
158
158
|
role: 'user' as const,
|
|
@@ -163,7 +163,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
163
163
|
|
|
164
164
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
165
165
|
'posts',
|
|
166
|
-
async (attrs, factory) => {
|
|
166
|
+
async ({ attrs, factory }) => {
|
|
167
167
|
if (!attrs.userId) {
|
|
168
168
|
const user = await factory.insert('user');
|
|
169
169
|
return {
|
|
@@ -193,7 +193,10 @@ describe('Testkit Integration Tests', () => {
|
|
|
193
193
|
// Create complex seeds
|
|
194
194
|
const seeds = {
|
|
195
195
|
blogWithAdminAndPosts: KyselyFactory.createSeed(
|
|
196
|
-
async (
|
|
196
|
+
async (
|
|
197
|
+
attrs: { postCount?: number },
|
|
198
|
+
factory: KyselyFactory<TestDatabase, typeof builders, {}>,
|
|
199
|
+
) => {
|
|
197
200
|
// Create admin user
|
|
198
201
|
const admin = await factory.insert('user', {
|
|
199
202
|
name: 'Blog Admin',
|
|
@@ -229,13 +232,15 @@ describe('Testkit Integration Tests', () => {
|
|
|
229
232
|
usersWithPosts: KyselyFactory.createSeed(
|
|
230
233
|
async (
|
|
231
234
|
attrs: { userCount?: number; postsPerUser?: number },
|
|
232
|
-
factory:
|
|
233
|
-
db: any,
|
|
235
|
+
factory: KyselyFactory<TestDatabase, typeof builders, {}>,
|
|
234
236
|
) => {
|
|
235
237
|
const userCount = attrs.userCount || 2;
|
|
236
238
|
const postsPerUser = attrs.postsPerUser || 2;
|
|
237
239
|
|
|
238
|
-
const results: Array<{
|
|
240
|
+
const results: Array<{
|
|
241
|
+
user: Awaited<ReturnType<typeof builders.user>>;
|
|
242
|
+
posts: Awaited<ReturnType<typeof builders.post>>[];
|
|
243
|
+
}> = [];
|
|
239
244
|
|
|
240
245
|
for (let i = 0; i < userCount; i++) {
|
|
241
246
|
const user = await factory.insert('user', {
|
|
@@ -306,7 +311,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
306
311
|
it('should handle transaction isolation properly', async ({ trx }) => {
|
|
307
312
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
308
313
|
'users',
|
|
309
|
-
async (
|
|
314
|
+
async ({ faker }) => ({
|
|
310
315
|
name: 'Test User',
|
|
311
316
|
email: faker.internet.email(),
|
|
312
317
|
role: 'user' as const,
|
|
@@ -345,7 +350,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
345
350
|
it('should handle creating many records efficiently', async ({ trx }) => {
|
|
346
351
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
347
352
|
'users',
|
|
348
|
-
async (
|
|
353
|
+
async ({ faker }) => ({
|
|
349
354
|
name: `User ${Math.random()}`,
|
|
350
355
|
email: faker.internet.email().toLowerCase(),
|
|
351
356
|
role: 'user' as const,
|
|
@@ -381,7 +386,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
381
386
|
it('should handle complex attribute generation', async ({ trx }) => {
|
|
382
387
|
const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
383
388
|
'users',
|
|
384
|
-
async (attrs,
|
|
389
|
+
async ({ attrs, faker }) => {
|
|
385
390
|
return {
|
|
386
391
|
name: `Generated User ${attrs.id}`,
|
|
387
392
|
email: faker.internet.email().toLowerCase(),
|
|
@@ -392,7 +397,7 @@ describe('Testkit Integration Tests', () => {
|
|
|
392
397
|
|
|
393
398
|
const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
394
399
|
'posts',
|
|
395
|
-
async (attrs, factory) => {
|
|
400
|
+
async ({ attrs, factory }) => {
|
|
396
401
|
let userId = attrs.userId;
|
|
397
402
|
if (!userId) {
|
|
398
403
|
const user = await factory.insert('user');
|
|
@@ -445,3 +450,155 @@ describe('Testkit Integration Tests', () => {
|
|
|
445
450
|
});
|
|
446
451
|
});
|
|
447
452
|
});
|
|
453
|
+
|
|
454
|
+
describe('extendWithFixtures', () => {
|
|
455
|
+
// Create builders for use in extended fixtures
|
|
456
|
+
const builders = {
|
|
457
|
+
user: KyselyFactory.createBuilder<TestDatabase, 'users'>(
|
|
458
|
+
'users',
|
|
459
|
+
({ faker }) => ({
|
|
460
|
+
name: faker.person.fullName(),
|
|
461
|
+
email: faker.internet.email().toLowerCase(),
|
|
462
|
+
role: 'user' as const,
|
|
463
|
+
createdAt: new Date(),
|
|
464
|
+
updatedAt: new Date(),
|
|
465
|
+
}),
|
|
466
|
+
),
|
|
467
|
+
post: KyselyFactory.createBuilder<TestDatabase, 'posts'>(
|
|
468
|
+
'posts',
|
|
469
|
+
async ({ attrs, factory, faker }) => {
|
|
470
|
+
const userId = attrs.userId ?? (await factory.insert('user')).id;
|
|
471
|
+
return {
|
|
472
|
+
title: faker.lorem.sentence(),
|
|
473
|
+
content: faker.lorem.paragraphs(),
|
|
474
|
+
userId,
|
|
475
|
+
published: false,
|
|
476
|
+
createdAt: new Date(),
|
|
477
|
+
updatedAt: new Date(),
|
|
478
|
+
};
|
|
479
|
+
},
|
|
480
|
+
),
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
// Create base test with transaction
|
|
484
|
+
const baseTest = wrapVitestKyselyTransaction<TestDatabase>(
|
|
485
|
+
base,
|
|
486
|
+
db,
|
|
487
|
+
createTestTables,
|
|
488
|
+
);
|
|
489
|
+
|
|
490
|
+
// Extend with factory fixture
|
|
491
|
+
const itWithFactory = extendWithFixtures<
|
|
492
|
+
TestDatabase,
|
|
493
|
+
{ factory: KyselyFactory<TestDatabase, typeof builders, {}> }
|
|
494
|
+
>(baseTest, {
|
|
495
|
+
factory: (trx) => new KyselyFactory(builders, {}, trx),
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
itWithFactory(
|
|
499
|
+
'should provide factory fixture alongside trx',
|
|
500
|
+
async ({ trx, factory }) => {
|
|
501
|
+
// Both trx and factory should be available
|
|
502
|
+
expect(trx).toBeDefined();
|
|
503
|
+
expect(factory).toBeDefined();
|
|
504
|
+
expect(factory).toBeInstanceOf(KyselyFactory);
|
|
505
|
+
|
|
506
|
+
// Factory should work with the transaction
|
|
507
|
+
const user = await factory.insert('user', { name: 'Test User' });
|
|
508
|
+
expect(user.id).toBeDefined();
|
|
509
|
+
expect(user.name).toBe('Test User');
|
|
510
|
+
|
|
511
|
+
// Verify user exists in transaction
|
|
512
|
+
const found = await trx
|
|
513
|
+
.selectFrom('users')
|
|
514
|
+
.where('id', '=', user.id)
|
|
515
|
+
.selectAll()
|
|
516
|
+
.executeTakeFirst();
|
|
517
|
+
|
|
518
|
+
expect(found).toBeDefined();
|
|
519
|
+
expect(found?.name).toBe('Test User');
|
|
520
|
+
},
|
|
521
|
+
);
|
|
522
|
+
|
|
523
|
+
itWithFactory(
|
|
524
|
+
'should allow factory to create related records',
|
|
525
|
+
async ({ factory }) => {
|
|
526
|
+
// Create user first
|
|
527
|
+
const user = await factory.insert('user', {
|
|
528
|
+
name: 'Author',
|
|
529
|
+
email: 'author@example.com',
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// Create posts for the user
|
|
533
|
+
const posts = await factory.insertMany(3, 'post', (idx: number) => ({
|
|
534
|
+
title: `Post ${idx + 1}`,
|
|
535
|
+
userId: user.id,
|
|
536
|
+
published: idx === 0,
|
|
537
|
+
}));
|
|
538
|
+
|
|
539
|
+
expect(posts).toHaveLength(3);
|
|
540
|
+
expect(posts[0].userId).toBe(user.id);
|
|
541
|
+
expect(posts[0].published).toBe(true);
|
|
542
|
+
expect(posts[1].published).toBe(false);
|
|
543
|
+
},
|
|
544
|
+
);
|
|
545
|
+
|
|
546
|
+
// Test with multiple fixtures
|
|
547
|
+
const itWithMultipleFixtures = extendWithFixtures<
|
|
548
|
+
TestDatabase,
|
|
549
|
+
{
|
|
550
|
+
factory: KyselyFactory<TestDatabase, typeof builders, {}>;
|
|
551
|
+
userCount: number;
|
|
552
|
+
}
|
|
553
|
+
>(baseTest, {
|
|
554
|
+
factory: (trx) => new KyselyFactory(builders, {}, trx),
|
|
555
|
+
userCount: () => 42, // Simple fixture that doesn't use trx
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
itWithMultipleFixtures(
|
|
559
|
+
'should support multiple fixtures',
|
|
560
|
+
async ({ trx, factory, userCount }) => {
|
|
561
|
+
expect(trx).toBeDefined();
|
|
562
|
+
expect(factory).toBeInstanceOf(KyselyFactory);
|
|
563
|
+
expect(userCount).toBe(42);
|
|
564
|
+
|
|
565
|
+
// Use the factory
|
|
566
|
+
const user = await factory.insert('user');
|
|
567
|
+
expect(user.id).toBeDefined();
|
|
568
|
+
},
|
|
569
|
+
);
|
|
570
|
+
|
|
571
|
+
// Test async fixture creators
|
|
572
|
+
const itWithAsyncFixture = extendWithFixtures<
|
|
573
|
+
TestDatabase,
|
|
574
|
+
{ initialUser: Awaited<ReturnType<typeof builders.user>> }
|
|
575
|
+
>(baseTest, {
|
|
576
|
+
initialUser: async (trx) => {
|
|
577
|
+
// Create a user directly in the fixture
|
|
578
|
+
const factory = new KyselyFactory(builders, {}, trx);
|
|
579
|
+
return factory.insert('user', {
|
|
580
|
+
name: 'Initial User',
|
|
581
|
+
email: 'initial@example.com',
|
|
582
|
+
});
|
|
583
|
+
},
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
itWithAsyncFixture(
|
|
587
|
+
'should support async fixture creators',
|
|
588
|
+
async ({ trx, initialUser }) => {
|
|
589
|
+
expect(initialUser).toBeDefined();
|
|
590
|
+
expect(initialUser.name).toBe('Initial User');
|
|
591
|
+
expect(initialUser.email).toBe('initial@example.com');
|
|
592
|
+
|
|
593
|
+
// Verify user exists in database
|
|
594
|
+
const found = await trx
|
|
595
|
+
.selectFrom('users')
|
|
596
|
+
.where('id', '=', initialUser.id)
|
|
597
|
+
.selectAll()
|
|
598
|
+
.executeTakeFirst();
|
|
599
|
+
|
|
600
|
+
expect(found).toBeDefined();
|
|
601
|
+
expect(found?.id).toBe(initialUser.id);
|
|
602
|
+
},
|
|
603
|
+
);
|
|
604
|
+
});
|