@geekmidas/testkit 0.0.17 → 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 (95) hide show
  1. package/README.md +302 -199
  2. package/dist/{Factory-CRquB4vz.d.mts → Factory-Cmr3s3-s.d.mts} +2 -2
  3. package/dist/Factory.d.mts +2 -2
  4. package/dist/{KyselyFactory-DRQ83r0o.d.cts → KyselyFactory-BFygzOlO.d.cts} +21 -8
  5. package/dist/{KyselyFactory-BDS_QqRT.d.mts → KyselyFactory-BTdygZ-i.d.mts} +23 -10
  6. package/dist/{KyselyFactory-BcYkC0t2.mjs → KyselyFactory-CXY5gJk2.mjs} +25 -12
  7. package/dist/KyselyFactory-CXY5gJk2.mjs.map +1 -0
  8. package/dist/{KyselyFactory-Cf0o2YxO.cjs → KyselyFactory-DaaCykWP.cjs} +25 -12
  9. package/dist/KyselyFactory-DaaCykWP.cjs.map +1 -0
  10. package/dist/KyselyFactory.cjs +1 -1
  11. package/dist/KyselyFactory.d.cts +1 -1
  12. package/dist/KyselyFactory.d.mts +3 -3
  13. package/dist/KyselyFactory.mjs +1 -1
  14. package/dist/{ObjectionFactory-C3tHvX1d.d.mts → ObjectionFactory-BagGjikT.d.mts} +26 -13
  15. package/dist/{ObjectionFactory-C4X78k0B.d.cts → ObjectionFactory-CeSIN3kZ.d.cts} +24 -11
  16. package/dist/{ObjectionFactory-CDriunkS.cjs → ObjectionFactory-Eb04AOnv.cjs} +28 -15
  17. package/dist/ObjectionFactory-Eb04AOnv.cjs.map +1 -0
  18. package/dist/{ObjectionFactory-8hebmnai.mjs → ObjectionFactory-zf2fLKrL.mjs} +28 -15
  19. package/dist/ObjectionFactory-zf2fLKrL.mjs.map +1 -0
  20. package/dist/ObjectionFactory.cjs +1 -1
  21. package/dist/ObjectionFactory.d.cts +1 -1
  22. package/dist/ObjectionFactory.d.mts +3 -3
  23. package/dist/ObjectionFactory.mjs +1 -1
  24. package/dist/{VitestKyselyTransactionIsolator-COCVfvfr.d.mts → VitestKyselyTransactionIsolator-4HOeLQ0d.d.cts} +2 -2
  25. package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs → VitestKyselyTransactionIsolator-DX_VPKS-.cjs} +2 -2
  26. package/dist/{VitestKyselyTransactionIsolator-Cst3vFjb.cjs.map → VitestKyselyTransactionIsolator-DX_VPKS-.cjs.map} +1 -1
  27. package/dist/{VitestKyselyTransactionIsolator-DYUYVEh9.d.cts → VitestKyselyTransactionIsolator-DnyZMaA-.d.mts} +2 -2
  28. package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs → VitestKyselyTransactionIsolator-XDL3ngs_.mjs} +2 -2
  29. package/dist/{VitestKyselyTransactionIsolator-BxjlD1YM.mjs.map → VitestKyselyTransactionIsolator-XDL3ngs_.mjs.map} +1 -1
  30. package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
  31. package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
  32. package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
  33. package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
  34. package/dist/{VitestObjectionTransactionIsolator-b973r9O1.d.mts → VitestObjectionTransactionIsolator-COVDlpEo.d.cts} +2 -2
  35. package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs → VitestObjectionTransactionIsolator-D_tlOtq8.cjs} +2 -2
  36. package/dist/{VitestObjectionTransactionIsolator-DzeF4UAq.cjs.map → VitestObjectionTransactionIsolator-D_tlOtq8.cjs.map} +1 -1
  37. package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs → VitestObjectionTransactionIsolator-_EhJKu_O.mjs} +2 -2
  38. package/dist/{VitestObjectionTransactionIsolator-BU-jXEhz.mjs.map → VitestObjectionTransactionIsolator-_EhJKu_O.mjs.map} +1 -1
  39. package/dist/{VitestObjectionTransactionIsolator-CJ4ds5Qv.d.cts → VitestObjectionTransactionIsolator-lZUSz1w0.d.mts} +2 -2
  40. package/dist/VitestObjectionTransactionIsolator.cjs +2 -2
  41. package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
  42. package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
  43. package/dist/VitestObjectionTransactionIsolator.mjs +2 -2
  44. package/dist/{VitestTransactionIsolator-CskiiJbW.mjs → VitestTransactionIsolator-BIaMs4c2.mjs} +40 -2
  45. package/dist/VitestTransactionIsolator-BIaMs4c2.mjs.map +1 -0
  46. package/dist/{VitestTransactionIsolator-BQ5FpLtC.cjs → VitestTransactionIsolator-BKIrj3Uy.cjs} +45 -1
  47. package/dist/VitestTransactionIsolator-BKIrj3Uy.cjs.map +1 -0
  48. package/dist/{VitestTransactionIsolator-DdLNODZg.d.cts → VitestTransactionIsolator-CyG_i_Nj.d.cts} +61 -3
  49. package/dist/{VitestTransactionIsolator-CsfJBxcb.d.mts → VitestTransactionIsolator-DWDbnITQ.d.mts} +61 -3
  50. package/dist/VitestTransactionIsolator.cjs +3 -2
  51. package/dist/VitestTransactionIsolator.d.cts +2 -2
  52. package/dist/VitestTransactionIsolator.d.mts +2 -2
  53. package/dist/VitestTransactionIsolator.mjs +2 -2
  54. package/dist/better-auth.cjs +8 -11
  55. package/dist/better-auth.cjs.map +1 -1
  56. package/dist/better-auth.d.cts +2 -2
  57. package/dist/better-auth.d.mts +2 -2
  58. package/dist/better-auth.mjs +8 -11
  59. package/dist/better-auth.mjs.map +1 -1
  60. package/dist/{directory-B4oYx02C.d.mts → directory-BXavAeJZ.d.mts} +3 -3
  61. package/dist/{directory-BUcnztHI.d.cts → directory-DlkPEzL4.d.cts} +3 -3
  62. package/dist/{faker-Br8MzXil.d.mts → faker-DHh7xs4u.d.mts} +3 -3
  63. package/dist/faker.d.mts +1 -1
  64. package/dist/kysely.cjs +58 -4
  65. package/dist/kysely.cjs.map +1 -1
  66. package/dist/kysely.d.cts +58 -5
  67. package/dist/kysely.d.mts +59 -6
  68. package/dist/kysely.mjs +57 -5
  69. package/dist/kysely.mjs.map +1 -1
  70. package/dist/objection.cjs +54 -4
  71. package/dist/objection.cjs.map +1 -1
  72. package/dist/objection.d.cts +54 -5
  73. package/dist/objection.d.mts +55 -6
  74. package/dist/objection.mjs +53 -5
  75. package/dist/objection.mjs.map +1 -1
  76. package/dist/os/directory.d.cts +1 -1
  77. package/dist/os/directory.d.mts +1 -1
  78. package/dist/os/index.d.cts +1 -1
  79. package/dist/os/index.d.mts +1 -1
  80. package/package.json +7 -3
  81. package/src/KyselyFactory.ts +29 -16
  82. package/src/ObjectionFactory.ts +34 -19
  83. package/src/VitestTransactionIsolator.ts +110 -2
  84. package/src/__tests__/KyselyFactory.spec.ts +10 -10
  85. package/src/__tests__/ObjectionFactory.spec.ts +9 -12
  86. package/src/__tests__/integration.spec.ts +171 -14
  87. package/src/better-auth.ts +13 -15
  88. package/src/kysely.ts +66 -0
  89. package/src/objection.ts +61 -0
  90. package/dist/KyselyFactory-BcYkC0t2.mjs.map +0 -1
  91. package/dist/KyselyFactory-Cf0o2YxO.cjs.map +0 -1
  92. package/dist/ObjectionFactory-8hebmnai.mjs.map +0 -1
  93. package/dist/ObjectionFactory-CDriunkS.cjs.map +0 -1
  94. package/dist/VitestTransactionIsolator-BQ5FpLtC.cjs.map +0 -1
  95. package/dist/VitestTransactionIsolator-CskiiJbW.mjs.map +0 -1
package/README.md CHANGED
@@ -6,20 +6,21 @@
6
6
  [![TypeScript](https://img.shields.io/badge/TypeScript-5.8.2-blue)](https://www.typescriptlang.org)
7
7
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
8
8
 
9
- ## 🚀 Overview
9
+ ## Overview
10
10
 
11
11
  **@geekmidas/testkit** provides a comprehensive set of testing utilities designed to simplify database testing in TypeScript applications. It offers factory patterns for creating test data, supports multiple database libraries, and ensures type safety throughout your tests.
12
12
 
13
13
  ### Key Features
14
14
 
15
- - 🏭 **Factory Pattern**: Create test data with minimal boilerplate
16
- - 🔒 **Type Safety**: Full TypeScript support with automatic schema inference
17
- - 🗄️ **Multi-Database Support**: Works with Kysely and Objection.js
18
- - 🔄 **Transaction Isolation**: Built-in support for test isolation
19
- - 🚀 **Performance**: Efficient batch operations and data seeding
20
- - 🧩 **Flexible**: Extensible architecture for custom implementations
15
+ - **Factory Pattern**: Create test data with minimal boilerplate
16
+ - **Type Safety**: Full TypeScript support with automatic schema inference
17
+ - **Multi-Database Support**: Works with Kysely and Objection.js
18
+ - **Transaction Isolation**: Built-in support for test isolation
19
+ - **Enhanced Faker**: Extended faker with timestamps, sequences, and coordinates
20
+ - **AWS Mocks**: Mock Lambda contexts and API Gateway events
21
+ - **Better Auth**: In-memory adapter for authentication testing
21
22
 
22
- ## 📦 Installation
23
+ ## Installation
23
24
 
24
25
  ```bash
25
26
  npm install --save-dev @geekmidas/testkit
@@ -29,9 +30,22 @@ pnpm add -D @geekmidas/testkit
29
30
  yarn add -D @geekmidas/testkit
30
31
  ```
31
32
 
32
- ## 🛠️ Quick Start
33
+ ## Subpath Exports
33
34
 
34
- ### With Kysely
35
+ ```typescript
36
+ import { KyselyFactory } from '@geekmidas/testkit/kysely';
37
+ import { ObjectionFactory } from '@geekmidas/testkit/objection';
38
+ import { faker } from '@geekmidas/testkit/faker';
39
+ import { waitFor } from '@geekmidas/testkit/timer';
40
+ import { itWithDir } from '@geekmidas/testkit/os';
41
+ import { createMockContext, createMockV1Event, createMockV2Event } from '@geekmidas/testkit/aws';
42
+ import { createMockLogger } from '@geekmidas/testkit/logger';
43
+ import { memoryAdapter } from '@geekmidas/testkit/better-auth';
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ### Database Factories with Kysely
35
49
 
36
50
  ```typescript
37
51
  import { KyselyFactory } from '@geekmidas/testkit/kysely';
@@ -40,41 +54,43 @@ import { Kysely } from 'kysely';
40
54
  // Define your database schema
41
55
  interface Database {
42
56
  users: {
43
- id: number;
57
+ id: string;
44
58
  name: string;
45
59
  email: string;
46
60
  createdAt: Date;
47
61
  };
48
62
  posts: {
49
- id: number;
63
+ id: string;
50
64
  title: string;
51
65
  content: string;
52
- userId: number;
53
- publishedAt: Date | null;
66
+ userId: string;
54
67
  };
55
68
  }
56
69
 
57
70
  // Create builders for your tables
58
- const userBuilder = KyselyFactory.createBuilder<Database, 'users'>({
59
- table: 'users',
60
- defaults: async () => ({
61
- name: 'John Doe',
62
- email: `user${Date.now()}@example.com`,
63
- createdAt: new Date(),
64
- }),
65
- });
66
-
67
- const postBuilder = KyselyFactory.createBuilder<Database, 'posts'>({
68
- table: 'posts',
69
- defaults: async () => ({
70
- title: 'Test Post',
71
- content: 'Lorem ipsum dolor sit amet',
72
- publishedAt: null,
73
- }),
74
- });
71
+ const builders = {
72
+ user: KyselyFactory.createBuilder<Database, 'users'>(
73
+ 'users',
74
+ (attrs, factory, db, faker) => ({
75
+ id: faker.string.uuid(),
76
+ name: faker.person.fullName(),
77
+ email: faker.internet.email(),
78
+ createdAt: new Date(),
79
+ ...attrs,
80
+ })
81
+ ),
82
+ post: KyselyFactory.createBuilder<Database, 'posts'>(
83
+ 'posts',
84
+ (attrs, factory, db, faker) => ({
85
+ id: faker.string.uuid(),
86
+ title: 'Test Post',
87
+ content: faker.lorem.paragraph(),
88
+ ...attrs,
89
+ })
90
+ ),
91
+ };
75
92
 
76
93
  // Initialize factory
77
- const builders = { user: userBuilder, post: postBuilder };
78
94
  const factory = new KyselyFactory(builders, {}, db);
79
95
 
80
96
  // Use in tests
@@ -101,124 +117,200 @@ describe('User Service', () => {
101
117
  import { ObjectionFactory } from '@geekmidas/testkit/objection';
102
118
  import { Model } from 'objection';
103
119
 
104
- // Define your models
105
120
  class User extends Model {
106
121
  static tableName = 'users';
107
- id!: number;
122
+ id!: string;
108
123
  name!: string;
109
124
  email!: string;
110
125
  }
111
126
 
112
- class Post extends Model {
113
- static tableName = 'posts';
114
- id!: number;
115
- title!: string;
116
- userId!: number;
117
- }
118
-
119
- // Create builders
120
- const userBuilder = {
121
- table: 'users',
122
- model: User,
123
- defaults: async () => ({
124
- name: 'John Doe',
125
- email: `user${Date.now()}@example.com`,
126
- }),
127
+ const builders = {
128
+ user: ObjectionFactory.createBuilder(
129
+ User,
130
+ (attrs, factory, db, faker) => ({
131
+ id: faker.string.uuid(),
132
+ name: faker.person.fullName(),
133
+ email: faker.internet.email(),
134
+ ...attrs,
135
+ })
136
+ ),
127
137
  };
128
138
 
129
- // Use in tests
130
- const factory = new ObjectionFactory({ user: userBuilder }, {});
139
+ const factory = new ObjectionFactory(builders, {}, knex);
131
140
  const user = await factory.insert('user', { name: 'Jane Doe' });
132
141
  ```
133
142
 
134
- ## 🏗️ Core Concepts
143
+ ## Enhanced Faker
144
+
145
+ The testkit provides an enhanced faker instance with additional utilities for common test data patterns.
146
+
147
+ ```typescript
148
+ import { faker } from '@geekmidas/testkit/faker';
149
+
150
+ // Standard faker methods
151
+ const name = faker.person.fullName();
152
+ const email = faker.internet.email();
153
+
154
+ // Generate timestamps for database records
155
+ const { createdAt, updatedAt } = faker.timestamps();
156
+ // createdAt: Date in the past
157
+ // updatedAt: Date between createdAt and now
158
+
159
+ // Sequential numbers (useful for unique IDs)
160
+ faker.sequence(); // 1
161
+ faker.sequence(); // 2
162
+ faker.sequence('user'); // 1 (separate sequence)
163
+ faker.sequence('user'); // 2
164
+
165
+ // Reset sequences between tests
166
+ faker.resetSequence('user');
167
+ faker.resetAllSequences();
168
+
169
+ // Generate prices as numbers
170
+ const price = faker.price(); // 29.99
171
+
172
+ // Generate reverse domain identifiers
173
+ faker.identifier(); // "com.example.widget1"
174
+ faker.identifier('user'); // "org.acme.user"
175
+
176
+ // Generate coordinates within/outside a radius
177
+ const center = { lat: 40.7128, lng: -74.0060 };
178
+ faker.coordinates.within(center, 1000); // Within 1km
179
+ faker.coordinates.outside(center, 1000, 5000); // Between 1km and 5km
180
+ ```
181
+
182
+ ## Timer Utilities
183
+
184
+ Simple async wait utility for tests.
135
185
 
136
- ### Builders
186
+ ```typescript
187
+ import { waitFor } from '@geekmidas/testkit/timer';
188
+
189
+ it('should process after delay', async () => {
190
+ startBackgroundProcess();
191
+ await waitFor(100); // Wait 100ms
192
+ expect(processComplete).toBe(true);
193
+ });
194
+ ```
137
195
 
138
- Builders define how to create test data for each table. They specify:
196
+ ## OS Utilities
139
197
 
140
- - **Table name**: The database table to insert into
141
- - **Default values**: Function returning default attributes
142
- - **Transformations**: Optional data transformations before insertion
143
- - **Relations**: Optional related data to create after insertion
198
+ Vitest fixture for temporary directory creation with automatic cleanup.
144
199
 
145
200
  ```typescript
146
- const userBuilder = KyselyFactory.createBuilder<Database, 'users'>({
147
- table: 'users',
148
- defaults: async () => ({
149
- id: generateId(),
150
- name: faker.person.fullName(),
151
- email: faker.internet.email(),
152
- createdAt: new Date(),
153
- }),
154
- transform: async (data) => ({
155
- ...data,
156
- email: data.email.toLowerCase(),
157
- }),
158
- relations: async (user, factory) => {
159
- // Create related data after user insertion
160
- await factory.insert('profile', { userId: user.id });
161
- },
201
+ import { itWithDir } from '@geekmidas/testkit/os';
202
+
203
+ // Creates a temp directory before test, removes it after
204
+ itWithDir('should write files to temp dir', async ({ dir }) => {
205
+ const filePath = path.join(dir, 'test.txt');
206
+ await fs.writeFile(filePath, 'hello');
207
+
208
+ const content = await fs.readFile(filePath, 'utf-8');
209
+ expect(content).toBe('hello');
210
+ // Directory is automatically cleaned up after test
162
211
  });
163
212
  ```
164
213
 
165
- ### Seeds
214
+ ## AWS Testing Utilities
166
215
 
167
- Seeds are functions that create complex test scenarios with multiple related entities:
216
+ Mock AWS Lambda contexts and API Gateway events for testing Lambda handlers.
168
217
 
169
218
  ```typescript
170
- const blogSeed = async (factory: Factory) => {
171
- const author = await factory.insert('user', {
172
- name: 'Blog Author',
173
- role: 'author',
219
+ import {
220
+ createMockContext,
221
+ createMockV1Event,
222
+ createMockV2Event
223
+ } from '@geekmidas/testkit/aws';
224
+
225
+ describe('Lambda Handler', () => {
226
+ it('should handle API Gateway v1 event', async () => {
227
+ const event = createMockV1Event({
228
+ httpMethod: 'POST',
229
+ path: '/users',
230
+ body: JSON.stringify({ name: 'John' }),
231
+ });
232
+ const context = createMockContext();
233
+
234
+ const result = await handler(event, context);
235
+ expect(result.statusCode).toBe(201);
174
236
  });
175
237
 
176
- const categories = await factory.insertMany(3, 'category');
238
+ it('should handle API Gateway v2 event', async () => {
239
+ const event = createMockV2Event({
240
+ routeKey: 'POST /users',
241
+ rawPath: '/users',
242
+ body: JSON.stringify({ name: 'John' }),
243
+ });
244
+ const context = createMockContext();
177
245
 
178
- const posts = await factory.insertMany(5, 'post', (index) => ({
179
- title: `Post ${index + 1}`,
180
- authorId: author.id,
181
- categoryId: categories[index % categories.length].id,
182
- }));
246
+ const result = await handler(event, context);
247
+ expect(result.statusCode).toBe(201);
248
+ });
249
+ });
250
+ ```
183
251
 
184
- return { author, categories, posts };
185
- };
252
+ ## Logger Testing Utilities
186
253
 
187
- // Use in tests
188
- const data = await factory.seed('blog');
254
+ Create mock loggers for testing code that uses `@geekmidas/logger`.
255
+
256
+ ```typescript
257
+ import { createMockLogger } from '@geekmidas/testkit/logger';
258
+
259
+ describe('Service', () => {
260
+ it('should log errors', async () => {
261
+ const logger = createMockLogger();
262
+ const service = new MyService(logger);
263
+
264
+ await service.doSomethingRisky();
265
+
266
+ expect(logger.error).toHaveBeenCalledWith(
267
+ expect.objectContaining({ error: expect.any(Error) }),
268
+ 'Operation failed'
269
+ );
270
+ });
271
+ });
189
272
  ```
190
273
 
191
- ### Transaction Support
274
+ ## Better Auth Testing
192
275
 
193
- TestKit supports transaction-based test isolation:
276
+ In-memory adapter for testing Better Auth without a real database.
194
277
 
195
278
  ```typescript
196
- describe('User Service', () => {
197
- let trx: Transaction<Database>;
198
- let factory: KyselyFactory;
279
+ import { memoryAdapter } from '@geekmidas/testkit/better-auth';
280
+ import { betterAuth } from 'better-auth';
281
+
282
+ describe('Authentication', () => {
283
+ const adapter = memoryAdapter({
284
+ debugLogs: false,
285
+ initialData: {
286
+ user: [{ id: '1', email: 'test@example.com', name: 'Test User' }],
287
+ },
288
+ });
199
289
 
200
- beforeEach(async () => {
201
- trx = await db.transaction();
202
- factory = new KyselyFactory(builders, seeds, trx);
290
+ const auth = betterAuth({
291
+ database: adapter,
292
+ // ... other config
203
293
  });
204
294
 
205
- afterEach(async () => {
206
- await trx.rollback();
295
+ afterEach(() => {
296
+ adapter.clear(); // Reset data between tests
207
297
  });
208
298
 
209
- it('should perform operations in isolation', async () => {
210
- const user = await factory.insert('user');
211
- // Test operations...
212
- // All changes will be rolled back after the test
299
+ it('should create user', async () => {
300
+ await auth.api.signUp({
301
+ email: 'new@example.com',
302
+ password: 'password123',
303
+ });
304
+
305
+ const data = adapter.getAllData();
306
+ expect(data.user).toHaveLength(2);
213
307
  });
214
308
  });
215
309
  ```
216
310
 
217
- ## 📚 Advanced Usage
311
+ ## Database Migration
218
312
 
219
- ### Database Migration
220
-
221
- TestKit includes utilities for managing test database migrations:
313
+ TestKit includes utilities for managing test database migrations.
222
314
 
223
315
  ```typescript
224
316
  import { PostgresKyselyMigrator } from '@geekmidas/testkit/kysely';
@@ -237,147 +329,158 @@ const migrator = new PostgresKyselyMigrator({
237
329
  // In test setup
238
330
  beforeAll(async () => {
239
331
  const cleanup = await migrator.start();
240
- // Database is created and migrations are run
241
-
242
- // Store cleanup function for later
243
332
  globalThis.cleanupDb = cleanup;
244
333
  });
245
334
 
246
335
  afterAll(async () => {
247
336
  await globalThis.cleanupDb?.();
248
- // Database is dropped
249
337
  });
250
338
  ```
251
339
 
252
- ### Custom Factories
340
+ ## Transaction Isolation
253
341
 
254
- You can extend the base Factory class for custom implementations:
342
+ TestKit supports transaction-based test isolation:
255
343
 
256
344
  ```typescript
257
- import { Factory } from '@geekmidas/testkit/factory';
258
-
259
- class MongoFactory extends Factory {
260
- async performInsert(table: string, data: any) {
261
- const collection = this.db.collection(table);
262
- const result = await collection.insertOne(data);
263
- return { ...data, _id: result.insertedId };
264
- }
265
-
266
- async performInsertMany(table: string, data: any[]) {
267
- const collection = this.db.collection(table);
268
- const result = await collection.insertMany(data);
269
- return data.map((item, index) => ({
270
- ...item,
271
- _id: result.insertedIds[index],
272
- }));
273
- }
274
- }
275
- ```
345
+ describe('User Service', () => {
346
+ let trx: Transaction<Database>;
347
+ let factory: KyselyFactory;
276
348
 
277
- ### Dynamic Attributes
349
+ beforeEach(async () => {
350
+ trx = await db.transaction();
351
+ factory = new KyselyFactory(builders, seeds, trx);
352
+ });
278
353
 
279
- Create dynamic attributes for each record in batch operations:
354
+ afterEach(async () => {
355
+ await trx.rollback();
356
+ });
280
357
 
281
- ```typescript
282
- const users = await factory.insertMany(10, 'user', (index) => ({
283
- name: `User ${index + 1}`,
284
- email: `user${index + 1}@example.com`,
285
- isAdmin: index === 0, // First user is admin
286
- }));
358
+ it('should perform operations in isolation', async () => {
359
+ const user = await factory.insert('user');
360
+ // All changes will be rolled back after the test
361
+ });
362
+ });
287
363
  ```
288
364
 
289
- ### Conditional Auto-insertion
365
+ ## Seeds
290
366
 
291
- Control whether builders automatically insert data:
367
+ Seeds are functions that create complex test scenarios:
292
368
 
293
369
  ```typescript
294
- const draftBuilder = KyselyFactory.createBuilder<Database, 'posts'>({
295
- table: 'posts',
296
- defaults: async () => ({
297
- title: 'Draft Post',
298
- status: 'draft',
299
- }),
300
- autoInsert: false, // Don't insert automatically
301
- });
370
+ const blogSeed = async (factory: Factory) => {
371
+ const author = await factory.insert('user', {
372
+ name: 'Blog Author',
373
+ role: 'author',
374
+ });
375
+
376
+ const categories = await factory.insertMany(3, 'category');
377
+
378
+ const posts = await factory.insertMany(5, 'post', (index) => ({
379
+ title: `Post ${index + 1}`,
380
+ authorId: author.id,
381
+ categoryId: categories[index % categories.length].id,
382
+ }));
383
+
384
+ return { author, categories, posts };
385
+ };
302
386
 
303
- // Manually handle the data
304
- const draftData = await draftBuilder.build();
305
- // Perform validation or modifications...
306
- const post = await db.insertInto('posts').values(draftData).execute();
387
+ // Use in tests
388
+ const data = await factory.seed('blog');
307
389
  ```
308
390
 
309
- ## 🔧 API Reference
391
+ ## API Reference
310
392
 
311
393
  ### KyselyFactory
312
394
 
313
395
  ```typescript
314
- class KyselyFactory<TBuilders, TSeeds> extends Factory {
396
+ class KyselyFactory<DB, Builders, Seeds> {
315
397
  constructor(
316
- builders: TBuilders,
317
- seeds: TSeeds,
318
- db: Kysely<any> | Transaction<any>
398
+ builders: Builders,
399
+ seeds: Seeds,
400
+ db: Kysely<DB> | ControlledTransaction<DB>
319
401
  );
320
402
 
321
- static createBuilder<TDatabase, TTable>(
322
- config: BuilderConfig<TDatabase, TTable>
323
- ): Builder;
324
-
325
- insert<K extends keyof TBuilders>(
326
- name: K,
327
- overrides?: Partial<BuilderOutput>
328
- ): Promise<BuilderOutput>;
329
-
330
- insertMany<K extends keyof TBuilders>(
403
+ static createBuilder<DB, TableName extends keyof DB & string>(
404
+ table: TableName,
405
+ item?: (
406
+ attrs: Partial<Insertable<DB[TableName]>>,
407
+ factory: KyselyFactory,
408
+ db: Kysely<DB>,
409
+ faker: FakerFactory
410
+ ) => Partial<Insertable<DB[TableName]>> | Promise<...>,
411
+ autoInsert?: boolean
412
+ ): BuilderFunction;
413
+
414
+ insert<K extends keyof Builders>(
415
+ builderName: K,
416
+ attrs?: Partial<BuilderAttrs>
417
+ ): Promise<BuilderResult>;
418
+
419
+ insertMany<K extends keyof Builders>(
331
420
  count: number,
332
- name: K,
333
- overrides?: Partial<BuilderOutput> | ((index: number) => Partial<BuilderOutput>)
334
- ): Promise<BuilderOutput[]>;
335
-
336
- seed<K extends keyof TSeeds>(
337
- name: K,
338
- ...args: Parameters<TSeeds[K]>
339
- ): Promise<ReturnType<TSeeds[K]>>;
421
+ builderName: K,
422
+ attrs?: Partial<BuilderAttrs> | ((idx: number, faker: FakerFactory) => Partial<BuilderAttrs>)
423
+ ): Promise<BuilderResult[]>;
424
+
425
+ seed<K extends keyof Seeds>(
426
+ seedName: K,
427
+ attrs?: SeedAttrs
428
+ ): Promise<SeedResult>;
340
429
  }
341
430
  ```
342
431
 
343
- ### ObjectionFactory
432
+ ### Enhanced Faker
344
433
 
345
434
  ```typescript
346
- class ObjectionFactory<TBuilders, TSeeds> extends Factory {
347
- constructor(
348
- builders: TBuilders,
349
- seeds: TSeeds,
350
- knex?: Knex
351
- );
352
-
353
- // Same methods as KyselyFactory
435
+ interface EnhancedFaker extends Faker {
436
+ timestamps(): { createdAt: Date; updatedAt: Date };
437
+ sequence(name?: string): number;
438
+ resetSequence(name?: string, value?: number): void;
439
+ resetAllSequences(): void;
440
+ identifier(suffix?: string): string;
441
+ price(): number;
442
+ coordinates: {
443
+ within(center: Coordinate, radiusMeters: number): Coordinate;
444
+ outside(center: Coordinate, minRadius: number, maxRadius: number): Coordinate;
445
+ };
354
446
  }
355
447
  ```
356
448
 
357
- ### Builder Configuration
449
+ ### AWS Mocks
358
450
 
359
451
  ```typescript
360
- interface BuilderConfig<TDatabase, TTable> {
361
- table: TTable;
362
- defaults: () => Promise<Insertable<TDatabase[TTable]>>;
363
- transform?: (data: any) => Promise<any>;
364
- relations?: (inserted: any, factory: Factory) => Promise<void>;
365
- autoInsert?: boolean;
366
- }
452
+ function createMockContext(): Context;
453
+ function createMockV1Event(overrides?: Partial<APIGatewayProxyEvent>): APIGatewayProxyEvent;
454
+ function createMockV2Event(overrides?: Partial<APIGatewayProxyEventV2>): APIGatewayProxyEventV2;
455
+ ```
456
+
457
+ ### Memory Adapter (Better Auth)
458
+
459
+ ```typescript
460
+ function memoryAdapter(config?: {
461
+ debugLogs?: boolean;
462
+ usePlural?: boolean;
463
+ initialData?: Record<string, any[]>;
464
+ }): DatabaseAdapter & {
465
+ clear(): void;
466
+ getAllData(): Record<string, any[]>;
467
+ getStore(): Map<string, any>;
468
+ };
367
469
  ```
368
470
 
369
- ## 🧪 Testing Best Practices
471
+ ## Testing Best Practices
370
472
 
371
473
  1. **Use Transactions**: Always wrap tests in transactions for isolation
372
474
  2. **Create Minimal Data**: Only create the data necessary for each test
373
475
  3. **Use Seeds for Complex Scenarios**: Encapsulate complex setups in seeds
374
476
  4. **Leverage Type Safety**: Let TypeScript catch schema mismatches
375
477
  5. **Clean Up Resources**: Always clean up database connections and transactions
478
+ 6. **Reset Sequences**: Call `faker.resetAllSequences()` in `beforeEach` for predictable IDs
376
479
 
377
- ## 🤝 Contributing
480
+ ## Contributing
378
481
 
379
482
  We welcome contributions! Please see our [Contributing Guide](../../CONTRIBUTING.md) for details.
380
483
 
381
- ## 📄 License
484
+ ## License
382
485
 
383
- This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details.
486
+ This project is licensed under the MIT License - see the [LICENSE](../../LICENSE) file for details.
@@ -1,4 +1,4 @@
1
- import { FakerFactory } from "./faker-Br8MzXil.mjs";
1
+ import { FakerFactory } from "./faker-DHh7xs4u.mjs";
2
2
 
3
3
  //#region src/Factory.d.ts
4
4
 
@@ -129,4 +129,4 @@ type MixedFactoryBuilder<Attrs = any, Factory = any, Result = any, DB = any> = (
129
129
  type FactorySeed<Attrs = any, Factory = any, Result = any, DB = any> = (attrs: Attrs, factory: Factory, db: DB) => Promise<Result>;
130
130
  //#endregion
131
131
  export { Factory, FactorySeed, MixedFactoryBuilder };
132
- //# sourceMappingURL=Factory-CRquB4vz.d.mts.map
132
+ //# sourceMappingURL=Factory-Cmr3s3-s.d.mts.map
@@ -1,3 +1,3 @@
1
- import "./faker-Br8MzXil.mjs";
2
- import { Factory, FactorySeed, MixedFactoryBuilder } from "./Factory-CRquB4vz.mjs";
1
+ import "./faker-DHh7xs4u.mjs";
2
+ import { Factory, FactorySeed, MixedFactoryBuilder } from "./Factory-Cmr3s3-s.mjs";
3
3
  export { Factory, FactorySeed, MixedFactoryBuilder };