@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.
Files changed (92) hide show
  1. package/README.md +302 -199
  2. package/dist/{KyselyFactory-DRQ83r0o.d.cts → KyselyFactory-BFygzOlO.d.cts} +21 -8
  3. package/dist/{KyselyFactory-Cx3sezwH.d.mts → KyselyFactory-BTdygZ-i.d.mts} +21 -8
  4. package/dist/{KyselyFactory-BcYkC0t2.mjs → KyselyFactory-CXY5gJk2.mjs} +25 -12
  5. package/dist/KyselyFactory-CXY5gJk2.mjs.map +1 -0
  6. package/dist/{KyselyFactory-Cf0o2YxO.cjs → KyselyFactory-DaaCykWP.cjs} +25 -12
  7. package/dist/KyselyFactory-DaaCykWP.cjs.map +1 -0
  8. package/dist/KyselyFactory.cjs +1 -1
  9. package/dist/KyselyFactory.d.cts +1 -1
  10. package/dist/KyselyFactory.d.mts +1 -1
  11. package/dist/KyselyFactory.mjs +1 -1
  12. package/dist/{ObjectionFactory-C-59Hjwj.d.mts → ObjectionFactory-BagGjikT.d.mts} +24 -11
  13. package/dist/{ObjectionFactory-C4X78k0B.d.cts → ObjectionFactory-CeSIN3kZ.d.cts} +24 -11
  14. package/dist/{ObjectionFactory-CDriunkS.cjs → ObjectionFactory-Eb04AOnv.cjs} +28 -15
  15. package/dist/ObjectionFactory-Eb04AOnv.cjs.map +1 -0
  16. package/dist/{ObjectionFactory-8hebmnai.mjs → ObjectionFactory-zf2fLKrL.mjs} +28 -15
  17. package/dist/ObjectionFactory-zf2fLKrL.mjs.map +1 -0
  18. package/dist/ObjectionFactory.cjs +1 -1
  19. package/dist/ObjectionFactory.d.cts +1 -1
  20. package/dist/ObjectionFactory.d.mts +1 -1
  21. package/dist/ObjectionFactory.mjs +1 -1
  22. package/dist/{VitestKyselyTransactionIsolator-COCVfvfr.d.mts → VitestKyselyTransactionIsolator-4HOeLQ0d.d.cts} +2 -2
  23. package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs → VitestKyselyTransactionIsolator-DX_VPKS-.cjs} +2 -2
  24. package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs.map → VitestKyselyTransactionIsolator-DX_VPKS-.cjs.map} +1 -1
  25. package/dist/{VitestKyselyTransactionIsolator-DYUYVEh9.d.cts → VitestKyselyTransactionIsolator-DnyZMaA-.d.mts} +2 -2
  26. package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs → VitestKyselyTransactionIsolator-XDL3ngs_.mjs} +2 -2
  27. package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs.map → VitestKyselyTransactionIsolator-XDL3ngs_.mjs.map} +1 -1
  28. package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
  29. package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
  30. package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
  31. package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
  32. package/dist/{VitestObjectionTransactionIsolator-b973r9O1.d.mts → VitestObjectionTransactionIsolator-COVDlpEo.d.cts} +2 -2
  33. package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs → VitestObjectionTransactionIsolator-D_tlOtq8.cjs} +2 -2
  34. package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs.map → VitestObjectionTransactionIsolator-D_tlOtq8.cjs.map} +1 -1
  35. package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs → VitestObjectionTransactionIsolator-_EhJKu_O.mjs} +2 -2
  36. package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs.map → VitestObjectionTransactionIsolator-_EhJKu_O.mjs.map} +1 -1
  37. package/dist/{VitestObjectionTransactionIsolator-CJ4ds5Qv.d.cts → VitestObjectionTransactionIsolator-lZUSz1w0.d.mts} +2 -2
  38. package/dist/VitestObjectionTransactionIsolator.cjs +2 -2
  39. package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
  40. package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
  41. package/dist/VitestObjectionTransactionIsolator.mjs +2 -2
  42. package/dist/{VitestTransactionIsolator-CskiiJbW.mjs → VitestTransactionIsolator-BIaMs4c2.mjs} +40 -2
  43. package/dist/VitestTransactionIsolator-BIaMs4c2.mjs.map +1 -0
  44. package/dist/{VitestTransactionIsolator-BQ5FpLtC.cjs → VitestTransactionIsolator-BKIrj3Uy.cjs} +45 -1
  45. package/dist/VitestTransactionIsolator-BKIrj3Uy.cjs.map +1 -0
  46. package/dist/{VitestTransactionIsolator-CsfJBxcb.d.mts → VitestTransactionIsolator-CyG_i_Nj.d.cts} +61 -3
  47. package/dist/{VitestTransactionIsolator-DdLNODZg.d.cts → VitestTransactionIsolator-DWDbnITQ.d.mts} +61 -3
  48. package/dist/VitestTransactionIsolator.cjs +3 -2
  49. package/dist/VitestTransactionIsolator.d.cts +2 -2
  50. package/dist/VitestTransactionIsolator.d.mts +2 -2
  51. package/dist/VitestTransactionIsolator.mjs +2 -2
  52. package/dist/better-auth.cjs +242 -0
  53. package/dist/better-auth.cjs.map +1 -0
  54. package/dist/better-auth.d.cts +17 -0
  55. package/dist/better-auth.d.mts +17 -0
  56. package/dist/better-auth.mjs +241 -0
  57. package/dist/better-auth.mjs.map +1 -0
  58. package/dist/{directory-B4oYx02C.d.mts → directory-BXavAeJZ.d.mts} +3 -3
  59. package/dist/{directory-BUcnztHI.d.cts → directory-DlkPEzL4.d.cts} +3 -3
  60. package/dist/kysely.cjs +58 -4
  61. package/dist/kysely.cjs.map +1 -1
  62. package/dist/kysely.d.cts +58 -5
  63. package/dist/kysely.d.mts +58 -5
  64. package/dist/kysely.mjs +57 -5
  65. package/dist/kysely.mjs.map +1 -1
  66. package/dist/objection.cjs +54 -4
  67. package/dist/objection.cjs.map +1 -1
  68. package/dist/objection.d.cts +54 -5
  69. package/dist/objection.d.mts +54 -5
  70. package/dist/objection.mjs +53 -5
  71. package/dist/objection.mjs.map +1 -1
  72. package/dist/os/directory.d.cts +1 -1
  73. package/dist/os/directory.d.mts +1 -1
  74. package/dist/os/index.d.cts +1 -1
  75. package/dist/os/index.d.mts +1 -1
  76. package/package.json +13 -3
  77. package/src/KyselyFactory.ts +29 -16
  78. package/src/ObjectionFactory.ts +34 -19
  79. package/src/VitestTransactionIsolator.ts +110 -2
  80. package/src/__tests__/KyselyFactory.spec.ts +10 -10
  81. package/src/__tests__/ObjectionFactory.spec.ts +9 -12
  82. package/src/__tests__/better-auth.spec.ts +21 -0
  83. package/src/__tests__/integration.spec.ts +171 -14
  84. package/src/better-auth.ts +325 -0
  85. package/src/kysely.ts +66 -0
  86. package/src/objection.ts +61 -0
  87. package/dist/KyselyFactory-BcYkC0t2.mjs.map +0 -1
  88. package/dist/KyselyFactory-Cf0o2YxO.cjs.map +0 -1
  89. package/dist/ObjectionFactory-8hebmnai.mjs.map +0 -1
  90. package/dist/ObjectionFactory-CDriunkS.cjs.map +0 -1
  91. package/dist/VitestTransactionIsolator-BQ5FpLtC.cjs.map +0 -1
  92. package/dist/VitestTransactionIsolator-CskiiJbW.mjs.map +0 -1
@@ -19,17 +19,17 @@ import { type FakerFactory, faker } from './faker.ts';
19
19
  *
20
20
  * // Create builders
21
21
  * const builders = {
22
- * user: (attrs) => User.fromJson({
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) => Post.fromJson({
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 item - Optional function to provide default values and transformations
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, factory, db, faker) => ({
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
- item?: (
114
- attrs: Attrs,
115
- factory: Factory,
116
- db: Knex,
117
- faker: FakerFactory,
118
- ) => Partial<InstanceType<TModel>> | Promise<Partial<InstanceType<TModel>>>,
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
- faker: FakerFactory,
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 (item) {
137
- const defaults = await item(attrs, factory, db, faker);
138
- data = { ...defaults, ...data };
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 (attrs) => ({
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 (attrs) => ({
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 (attrs) => {
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 (attrs, factory) => {
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, factory, db, faker) => ({
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 = factory;
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, factory, db, faker) => {
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
- User,
438
- (attrs, factory, db, faker) => ({
439
- name: 'Default Name',
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 (attrs) => ({
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 (attrs) => ({
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 (attrs: { postCount?: number }, factory: any, db: any) => {
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: any,
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<{ user: any; posts: any[] }> = [];
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 (attrs, factory, db, faker) => ({
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 (attrs, factory, db, faker) => ({
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, factory, db, faker) => {
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
+ });