@geekmidas/testkit 0.0.5 → 0.0.7

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/dist/Factory-WMhTNZ9S.cjs +56 -0
  2. package/dist/Factory-z2m01hMj.mjs +50 -0
  3. package/dist/Factory.cjs +1 -1
  4. package/dist/Factory.mjs +1 -1
  5. package/dist/KyselyFactory-Bdq1s1Go.cjs +215 -0
  6. package/dist/KyselyFactory-ELiHgHVv.mjs +210 -0
  7. package/dist/KyselyFactory.cjs +3 -3
  8. package/dist/KyselyFactory.mjs +3 -3
  9. package/dist/ObjectionFactory-89p-FFEw.mjs +178 -0
  10. package/dist/ObjectionFactory-C47B03Ot.cjs +183 -0
  11. package/dist/ObjectionFactory.cjs +2 -2
  12. package/dist/ObjectionFactory.mjs +2 -2
  13. package/dist/PostgresKyselyMigrator-Bs31emFd.cjs +87 -0
  14. package/dist/PostgresKyselyMigrator-ChIpZFYB.mjs +81 -0
  15. package/dist/PostgresKyselyMigrator.cjs +2 -2
  16. package/dist/PostgresKyselyMigrator.mjs +2 -2
  17. package/dist/PostgresMigrator-BtAWdLss.cjs +151 -0
  18. package/dist/PostgresMigrator-BzqksJcW.mjs +145 -0
  19. package/dist/PostgresMigrator.cjs +1 -1
  20. package/dist/PostgresMigrator.mjs +1 -1
  21. package/dist/VitestKyselyTransactionIsolator-AfxPJEwR.mjs +58 -0
  22. package/dist/VitestKyselyTransactionIsolator-YWnSJiIH.cjs +63 -0
  23. package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
  24. package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
  25. package/dist/VitestObjectionTransactionIsolator-0uX6DW5G.cjs +66 -0
  26. package/dist/VitestObjectionTransactionIsolator-BZRYy8iW.mjs +61 -0
  27. package/dist/VitestObjectionTransactionIsolator.cjs +4 -0
  28. package/dist/VitestObjectionTransactionIsolator.mjs +4 -0
  29. package/dist/VitestTransactionIsolator-DcOz0LZF.cjs +129 -0
  30. package/dist/VitestTransactionIsolator-kFL36T8x.mjs +117 -0
  31. package/dist/VitestTransactionIsolator.cjs +1 -1
  32. package/dist/VitestTransactionIsolator.mjs +1 -1
  33. package/dist/__tests__/Factory.spec.cjs +1 -1
  34. package/dist/__tests__/Factory.spec.mjs +1 -1
  35. package/dist/__tests__/KyselyFactory.spec.cjs +41 -40
  36. package/dist/__tests__/KyselyFactory.spec.mjs +42 -41
  37. package/dist/__tests__/ObjectionFactory.spec.cjs +3 -3
  38. package/dist/__tests__/ObjectionFactory.spec.mjs +3 -3
  39. package/dist/__tests__/PostgresMigrator.spec.cjs +2 -2
  40. package/dist/__tests__/PostgresMigrator.spec.mjs +2 -2
  41. package/dist/__tests__/faker.spec.cjs +1 -1
  42. package/dist/__tests__/faker.spec.mjs +1 -1
  43. package/dist/__tests__/integration.spec.cjs +11 -8
  44. package/dist/__tests__/integration.spec.mjs +12 -9
  45. package/dist/example.cjs +3 -3
  46. package/dist/example.mjs +3 -3
  47. package/dist/faker-CxKkEeYi.mjs +227 -0
  48. package/dist/faker-SMN4ira4.cjs +263 -0
  49. package/dist/faker.cjs +1 -1
  50. package/dist/faker.mjs +1 -1
  51. package/dist/helpers-CKMlwSYT.mjs +47 -0
  52. package/dist/helpers-H4hO5SZR.cjs +53 -0
  53. package/dist/helpers.cjs +2 -5
  54. package/dist/helpers.mjs +2 -4
  55. package/dist/kysely-B-GOhABm.cjs +72 -0
  56. package/dist/kysely-CqfoKVXs.mjs +67 -0
  57. package/dist/kysely.cjs +11 -15
  58. package/dist/kysely.mjs +9 -15
  59. package/dist/objection.cjs +86 -3
  60. package/dist/objection.mjs +83 -3
  61. package/package.json +2 -2
  62. package/src/Factory.ts +97 -0
  63. package/src/KyselyFactory.ts +180 -0
  64. package/src/ObjectionFactory.ts +145 -3
  65. package/src/PostgresKyselyMigrator.ts +54 -0
  66. package/src/PostgresMigrator.ts +90 -0
  67. package/src/VitestKyselyTransactionIsolator.ts +46 -0
  68. package/src/VitestObjectionTransactionIsolator.ts +73 -0
  69. package/src/VitestTransactionIsolator.ts +99 -2
  70. package/src/__tests__/KyselyFactory.spec.ts +4 -6
  71. package/src/__tests__/integration.spec.ts +8 -3
  72. package/src/faker.ts +158 -7
  73. package/src/helpers.ts +35 -18
  74. package/src/kysely.ts +66 -1
  75. package/src/objection.ts +95 -0
  76. package/dist/Factory-DREHoms3.cjs +0 -15
  77. package/dist/Factory-DlzMkMzb.mjs +0 -9
  78. package/dist/KyselyFactory-BX7Kv2uP.cjs +0 -65
  79. package/dist/KyselyFactory-pOMOFQWE.mjs +0 -60
  80. package/dist/ObjectionFactory-BlkzSEqo.cjs +0 -41
  81. package/dist/ObjectionFactory-ChuX8sZN.mjs +0 -36
  82. package/dist/PostgresKyselyMigrator-D8fm35-s.mjs +0 -27
  83. package/dist/PostgresKyselyMigrator-JTY2LfwD.cjs +0 -33
  84. package/dist/PostgresMigrator-Bz-tnjB6.cjs +0 -67
  85. package/dist/PostgresMigrator-CEoRKTdq.mjs +0 -61
  86. package/dist/VitestKyselyTransactionIsolator-BS3R-V0I.mjs +0 -12
  87. package/dist/VitestKyselyTransactionIsolator-DWSTKIe3.cjs +0 -17
  88. package/dist/VitestTransactionIsolator-BjVXqFs6.mjs +0 -40
  89. package/dist/VitestTransactionIsolator-Bx2c4OzK.cjs +0 -52
  90. package/dist/faker-BwaXA_RF.mjs +0 -85
  91. package/dist/faker-caz-8zt8.cjs +0 -121
  92. package/dist/helpers-B9Jdk_C7.cjs +0 -31
  93. package/dist/helpers-DOiGIkaU.mjs +0 -19
  94. /package/dist/{helpers-DKEBHABj.cjs → helpers-Bnm3Jy9X.cjs} +0 -0
  95. /package/dist/{helpers-BfuX-cjN.mjs → helpers-CukcFAU9.mjs} +0 -0
@@ -1,4 +1,84 @@
1
- import "./Factory-DlzMkMzb.mjs";
2
- import { ObjectionFactory } from "./ObjectionFactory-ChuX8sZN.mjs";
1
+ import "./Factory-z2m01hMj.mjs";
2
+ import { ObjectionFactory } from "./ObjectionFactory-89p-FFEw.mjs";
3
+ import { IsolationLevel } from "./VitestTransactionIsolator-kFL36T8x.mjs";
4
+ import { VitestObjectionTransactionIsolator } from "./VitestObjectionTransactionIsolator-BZRYy8iW.mjs";
3
5
 
4
- export { ObjectionFactory };
6
+ //#region src/objection.ts
7
+ /**
8
+ * Creates a wrapped Vitest test API with automatic transaction rollback for Objection.js.
9
+ * Each test runs in an isolated database transaction that is rolled back after completion.
10
+ * This ensures tests don't affect each other's data and run faster than truncating tables.
11
+ *
12
+ * @param api - The Vitest test API (usually `test` from vitest)
13
+ * @param knex - The Knex database connection instance
14
+ * @param setup - Optional setup function to run before each test in the transaction
15
+ * @param level - Transaction isolation level (defaults to REPEATABLE_READ)
16
+ * @returns A wrapped test API that provides transaction isolation
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { test } from 'vitest';
21
+ * import { wrapVitestObjectionTransaction } from '@geekmidas/testkit/objection';
22
+ * import { knex } from './database';
23
+ * import { User, Post } from './models';
24
+ *
25
+ * // Create isolated test with automatic rollback
26
+ * const isolatedTest = wrapVitestObjectionTransaction(test, knex);
27
+ *
28
+ * // Use in tests - each test gets its own transaction
29
+ * isolatedTest('should create user', async ({ trx }) => {
30
+ * const user = await User.query(trx)
31
+ * .insert({ name: 'Test User', email: 'test@example.com' });
32
+ *
33
+ * expect(user).toBeDefined();
34
+ * // User is automatically rolled back after test
35
+ * });
36
+ *
37
+ * // With setup function for common test data
38
+ * const testWithSetup = wrapVitestObjectionTransaction(
39
+ * test,
40
+ * knex,
41
+ * async (trx) => {
42
+ * // Create common test data
43
+ * await knex('settings')
44
+ * .transacting(trx)
45
+ * .insert({ key: 'test_mode', value: 'true' });
46
+ * }
47
+ * );
48
+ *
49
+ * testWithSetup('should have test settings', async ({ trx }) => {
50
+ * const setting = await knex('settings')
51
+ * .transacting(trx)
52
+ * .where('key', 'test_mode')
53
+ * .first();
54
+ *
55
+ * expect(setting?.value).toBe('true');
56
+ * });
57
+ *
58
+ * // Example with factory and transaction
59
+ * const isolatedTest = wrapVitestObjectionTransaction(test, knex);
60
+ * const factory = new ObjectionFactory(builders, seeds, knex);
61
+ *
62
+ * isolatedTest('creates related data', async ({ trx }) => {
63
+ * // Factory can use the transaction
64
+ * const user = await User.query(trx).insert({ name: 'Author' });
65
+ * const posts = await Post.query(trx).insert([
66
+ * { title: 'Post 1', userId: user.id },
67
+ * { title: 'Post 2', userId: user.id }
68
+ * ]);
69
+ *
70
+ * const userWithPosts = await User.query(trx)
71
+ * .findById(user.id)
72
+ * .withGraphFetched('posts');
73
+ *
74
+ * expect(userWithPosts.posts).toHaveLength(2);
75
+ * });
76
+ * ```
77
+ */
78
+ function wrapVitestObjectionTransaction(api, knex, setup, level = IsolationLevel.REPEATABLE_READ) {
79
+ const wrapper = new VitestObjectionTransactionIsolator(api);
80
+ return wrapper.wrapVitestWithTransaction(knex, setup, level);
81
+ }
82
+
83
+ //#endregion
84
+ export { IsolationLevel, ObjectionFactory, VitestObjectionTransactionIsolator, wrapVitestObjectionTransaction };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/testkit",
3
- "version": "0.0.5",
3
+ "version": "0.0.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -37,6 +37,6 @@
37
37
  "objection": "~3.1.5",
38
38
  "db-errors": "~0.2.3",
39
39
  "vitest": "~3.2.4",
40
- "@geekmidas/envkit": "0.0.3"
40
+ "@geekmidas/envkit": "0.0.4"
41
41
  }
42
42
  }
package/src/Factory.ts CHANGED
@@ -1,9 +1,50 @@
1
1
  import type { FakerFactory } from './faker';
2
2
 
3
+ /**
4
+ * Abstract base class for database factories used in testing.
5
+ * Provides a standardized interface for creating test data using builder and seed patterns.
6
+ *
7
+ * @template Builders - Record of builder functions for creating individual entities
8
+ * @template Seeds - Record of seed functions for creating complex test scenarios
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * // Define builders for creating individual records
13
+ * const builders = {
14
+ * user: (attrs) => ({ name: 'Test User', email: 'test@example.com', ...attrs }),
15
+ * post: (attrs) => ({ title: 'Test Post', content: 'Content', ...attrs })
16
+ * };
17
+ *
18
+ * // Define seeds for complex scenarios
19
+ * const seeds = {
20
+ * userWithPosts: async (attrs, factory) => {
21
+ * const user = await factory.insert('user', attrs);
22
+ * await factory.insertMany(3, 'post', { userId: user.id });
23
+ * return user;
24
+ * }
25
+ * };
26
+ * ```
27
+ */
3
28
  export abstract class Factory<
4
29
  Builders extends Record<string, any>,
5
30
  Seeds extends Record<string, any>,
6
31
  > {
32
+ /**
33
+ * Creates a typed seed function with proper type inference.
34
+ * This is a utility method to help with TypeScript type checking when defining seeds.
35
+ *
36
+ * @template Seed - The seed function type
37
+ * @param seedFn - The seed function to wrap
38
+ * @returns The same seed function with proper typing
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const userWithPostsSeed = Factory.createSeed(async (attrs, factory, db) => {
43
+ * const user = await factory.insert('user', attrs);
44
+ * return user;
45
+ * });
46
+ * ```
47
+ */
7
48
  static createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
8
49
  return seedFn;
9
50
  }
@@ -46,6 +87,33 @@ export abstract class Factory<
46
87
  ): ReturnType<Seeds[K]>;
47
88
  }
48
89
 
90
+ /**
91
+ * Type definition for a factory builder function that can work with different database types.
92
+ * Builders are responsible for creating individual database records with default values and relationships.
93
+ *
94
+ * @template Attrs - The attributes/input type for the builder
95
+ * @template Factory - The factory instance type
96
+ * @template Result - The type of object returned by the builder
97
+ * @template DB - The database connection type (Kysely, Knex, etc.)
98
+ *
99
+ * @param attrs - Partial attributes to override defaults
100
+ * @param factory - The factory instance for creating related records
101
+ * @param db - The database connection
102
+ * @returns The created record or a promise resolving to it
103
+ *
104
+ * @example
105
+ * ```typescript
106
+ * const userBuilder: MixedFactoryBuilder<UserAttrs, Factory, User, Kysely<DB>> =
107
+ * async (attrs, factory, db) => {
108
+ * return {
109
+ * id: faker.string.uuid(),
110
+ * name: faker.person.fullName(),
111
+ * email: faker.internet.email(),
112
+ * ...attrs
113
+ * };
114
+ * };
115
+ * ```
116
+ */
49
117
  export type MixedFactoryBuilder<
50
118
  Attrs = any,
51
119
  Factory = any,
@@ -53,6 +121,35 @@ export type MixedFactoryBuilder<
53
121
  DB = any,
54
122
  > = (attrs: Attrs, factory: Factory, db: DB) => Result | Promise<Result>;
55
123
 
124
+ /**
125
+ * Type definition for a factory seed function used to create complex test scenarios.
126
+ * Seeds typically create multiple related records to set up a complete test environment.
127
+ *
128
+ * @template Attrs - The attributes/input type for the seed
129
+ * @template Factory - The factory instance type
130
+ * @template Result - The type of object returned by the seed
131
+ * @template DB - The database connection type (Kysely, Knex, etc.)
132
+ *
133
+ * @param attrs - Configuration attributes for the seed
134
+ * @param factory - The factory instance for creating records
135
+ * @param db - The database connection
136
+ * @returns A promise resolving to the seed result
137
+ *
138
+ * @example
139
+ * ```typescript
140
+ * const userWithPostsSeed: FactorySeed<{ postCount?: number }, Factory, User, DB> =
141
+ * async (attrs, factory, db) => {
142
+ * const user = await factory.insert('user', attrs);
143
+ * const postCount = attrs.postCount || 3;
144
+ *
145
+ * for (let i = 0; i < postCount; i++) {
146
+ * await factory.insert('post', { userId: user.id });
147
+ * }
148
+ *
149
+ * return user;
150
+ * };
151
+ * ```
152
+ */
56
153
  export type FactorySeed<Attrs = any, Factory = any, Result = any, DB = any> = (
57
154
  attrs: Attrs,
58
155
  factory: Factory,
@@ -7,15 +7,68 @@ import type {
7
7
  import { Factory, type FactorySeed } from './Factory.ts';
8
8
  import { type FakerFactory, faker } from './faker.ts';
9
9
 
10
+ /**
11
+ * Factory implementation for Kysely ORM, providing test data creation utilities.
12
+ * Extends the base Factory class with Kysely-specific database operations.
13
+ *
14
+ * @template DB - The database schema type
15
+ * @template Builders - Record of builder functions for creating entities
16
+ * @template Seeds - Record of seed functions for complex test scenarios
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // Define your database schema
21
+ * interface Database {
22
+ * users: UsersTable;
23
+ * posts: PostsTable;
24
+ * }
25
+ *
26
+ * // Create builders
27
+ * const builders = {
28
+ * user: KyselyFactory.createBuilder<Database, 'users'>('users', (attrs, factory, db, faker) => ({
29
+ * id: faker.string.uuid(),
30
+ * name: faker.person.fullName(),
31
+ * email: faker.internet.email(),
32
+ * ...attrs
33
+ * })),
34
+ * post: KyselyFactory.createBuilder<Database, 'posts'>('posts', (attrs) => ({
35
+ * title: 'Test Post',
36
+ * content: 'Test content',
37
+ * ...attrs
38
+ * }))
39
+ * };
40
+ *
41
+ * // Create factory instance
42
+ * const factory = new KyselyFactory(builders, seeds, db);
43
+ *
44
+ * // Use in tests
45
+ * const user = await factory.insert('user', { name: 'John Doe' });
46
+ * ```
47
+ */
10
48
  export class KyselyFactory<
11
49
  DB,
12
50
  Builders extends Record<string, any>,
13
51
  Seeds extends Record<string, any>,
14
52
  > extends Factory<Builders, Seeds> {
53
+ /**
54
+ * Creates a typed seed function with proper type inference.
55
+ * Inherits from the base Factory class implementation.
56
+ *
57
+ * @template Seed - The seed function type
58
+ * @param seedFn - The seed function to wrap
59
+ * @returns The same seed function with proper typing
60
+ */
15
61
  static createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
16
62
  return Factory.createSeed(seedFn);
17
63
  }
18
64
 
65
+ /**
66
+ * Creates a new KyselyFactory instance.
67
+ *
68
+ * @param builders - Record of builder functions for creating individual entities
69
+ * @param seeds - Record of seed functions for creating complex test scenarios
70
+ * @param db - Kysely database instance or controlled transaction
71
+ */
19
72
  constructor(
20
73
  private builders: Builders,
21
74
  private seeds: Seeds,
@@ -24,6 +77,45 @@ export class KyselyFactory<
24
77
  super();
25
78
  }
26
79
 
80
+ /**
81
+ * Creates a typed builder function for a specific database table.
82
+ * This is a utility method that helps create builders with proper type inference for Kysely.
83
+ *
84
+ * @template DB - The database schema type
85
+ * @template TableName - The name of the table (must be a key of DB)
86
+ * @template Attrs - The attributes type for the builder (defaults to Partial<Insertable>)
87
+ * @template Factory - The factory instance type
88
+ * @template Result - The result type (defaults to Selectable of the table)
89
+ *
90
+ * @param table - The name of the database table
91
+ * @param item - Optional function to provide default values and transformations
92
+ * @param autoInsert - Whether to automatically insert the record (default: true)
93
+ * @returns A builder function that creates and optionally inserts records
94
+ *
95
+ * @example
96
+ * ```typescript
97
+ * // Create a simple builder with defaults
98
+ * const userBuilder = KyselyFactory.createBuilder<DB, 'users'>('users',
99
+ * (attrs, factory, db, faker) => ({
100
+ * id: faker.string.uuid(),
101
+ * name: faker.person.fullName(),
102
+ * email: faker.internet.email(),
103
+ * createdAt: new Date(),
104
+ * ...attrs
105
+ * })
106
+ * );
107
+ *
108
+ * // Create a builder that doesn't auto-insert (useful for nested inserts)
109
+ * const addressBuilder = KyselyFactory.createBuilder<DB, 'addresses'>('addresses',
110
+ * (attrs) => ({
111
+ * street: '123 Main St',
112
+ * city: 'Anytown',
113
+ * ...attrs
114
+ * }),
115
+ * false // Don't auto-insert
116
+ * );
117
+ * ```
118
+ */
27
119
  static createBuilder<
28
120
  DB,
29
121
  TableName extends keyof DB & string,
@@ -85,6 +177,35 @@ export class KyselyFactory<
85
177
  };
86
178
  }
87
179
 
180
+ /**
181
+ * Inserts a single record into the database using the specified builder.
182
+ * The builder function is responsible for generating the record data with defaults
183
+ * and the factory handles the actual database insertion.
184
+ *
185
+ * @template K - The builder name (must be a key of Builders)
186
+ * @param builderName - The name of the builder to use
187
+ * @param attrs - Optional attributes to override builder defaults
188
+ * @returns A promise resolving to the inserted record
189
+ * @throws Error if the specified builder doesn't exist
190
+ *
191
+ * @example
192
+ * ```typescript
193
+ * // Insert with defaults
194
+ * const user = await factory.insert('user');
195
+ *
196
+ * // Insert with overrides
197
+ * const adminUser = await factory.insert('user', {
198
+ * email: 'admin@example.com',
199
+ * role: 'admin'
200
+ * });
201
+ *
202
+ * // Use the inserted record
203
+ * const post = await factory.insert('post', {
204
+ * userId: user.id,
205
+ * title: 'My First Post'
206
+ * });
207
+ * ```
208
+ */
88
209
  async insert<K extends keyof Builders>(
89
210
  builderName: K,
90
211
  attrs?: Parameters<Builders[K]>[0],
@@ -126,6 +247,36 @@ export class KyselyFactory<
126
247
  return result;
127
248
  }
128
249
 
250
+ /**
251
+ * Inserts multiple records into the database using the specified builder.
252
+ * Supports both static attributes and dynamic attribute generation via a function.
253
+ *
254
+ * @template K - The builder name (must be a key of Builders)
255
+ * @param count - The number of records to insert
256
+ * @param builderName - The name of the builder to use
257
+ * @param attrs - Static attributes or a function that generates attributes for each record
258
+ * @returns A promise resolving to an array of inserted records
259
+ * @throws Error if the specified builder doesn't exist
260
+ *
261
+ * @example
262
+ * ```typescript
263
+ * // Insert multiple with same attributes
264
+ * const users = await factory.insertMany(5, 'user', { role: 'member' });
265
+ *
266
+ * // Insert multiple with dynamic attributes
267
+ * const posts = await factory.insertMany(10, 'post', (idx, faker) => ({
268
+ * title: `Post ${idx + 1}`,
269
+ * content: faker.lorem.paragraph(),
270
+ * publishedAt: faker.date.past()
271
+ * }));
272
+ *
273
+ * // Create users with sequential emails
274
+ * const admins = await factory.insertMany(3, 'user', (idx) => ({
275
+ * email: `admin${idx + 1}@example.com`,
276
+ * role: 'admin'
277
+ * }));
278
+ * ```
279
+ */
129
280
  // Method overloads for better type inference
130
281
  async insertMany<K extends keyof Builders>(
131
282
  count: number,
@@ -160,6 +311,35 @@ export class KyselyFactory<
160
311
  return Promise.all(promises);
161
312
  }
162
313
 
314
+ /**
315
+ * Executes a seed function to create complex test scenarios with multiple related records.
316
+ * Seeds are useful for setting up complete test environments with realistic data relationships.
317
+ *
318
+ * @template K - The seed name (must be a key of Seeds)
319
+ * @param seedName - The name of the seed to execute
320
+ * @param attrs - Optional configuration attributes for the seed
321
+ * @returns The result of the seed function (typically the primary record created)
322
+ * @throws Error if the specified seed doesn't exist
323
+ *
324
+ * @example
325
+ * ```typescript
326
+ * // Execute a simple seed
327
+ * const user = await factory.seed('userWithProfile');
328
+ *
329
+ * // Execute a seed with configuration
330
+ * const author = await factory.seed('authorWithBooks', {
331
+ * bookCount: 5,
332
+ * includeReviews: true
333
+ * });
334
+ *
335
+ * // Use seed result in tests
336
+ * const company = await factory.seed('companyWithDepartments', {
337
+ * departmentCount: 3,
338
+ * employeesPerDepartment: 10
339
+ * });
340
+ * expect(company.departments).toHaveLength(3);
341
+ * ```
342
+ */
163
343
  seed<K extends keyof Seeds>(
164
344
  seedName: K,
165
345
  attrs?: Parameters<Seeds[K]>[0],
@@ -1,14 +1,65 @@
1
1
  import type { Knex } from 'knex';
2
2
  import { Factory, type FactorySeed } from './Factory.ts';
3
3
 
4
+ /**
5
+ * Factory implementation for Objection.js ORM, providing test data creation utilities.
6
+ * Extends the base Factory class with Objection.js-specific database operations.
7
+ *
8
+ * @template Builders - Record of builder functions for creating entities
9
+ * @template Seeds - Record of seed functions for complex test scenarios
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * // Define your models with Objection.js
14
+ * class User extends Model {
15
+ * static tableName = 'users';
16
+ * }
17
+ *
18
+ * // Create builders
19
+ * const builders = {
20
+ * user: (attrs) => User.fromJson({
21
+ * id: faker.string.uuid(),
22
+ * name: faker.person.fullName(),
23
+ * email: faker.internet.email(),
24
+ * ...attrs
25
+ * }),
26
+ * post: (attrs) => Post.fromJson({
27
+ * title: 'Test Post',
28
+ * content: 'Test content',
29
+ * ...attrs
30
+ * })
31
+ * };
32
+ *
33
+ * // Create factory instance
34
+ * const factory = new ObjectionFactory(builders, seeds, knex);
35
+ *
36
+ * // Use in tests
37
+ * const user = await factory.insert('user', { name: 'John Doe' });
38
+ * ```
39
+ */
4
40
  export class ObjectionFactory<
5
41
  Builders extends Record<string, any>,
6
42
  Seeds extends Record<string, any>,
7
43
  > extends Factory<Builders, Seeds> {
44
+ /**
45
+ * Creates a typed seed function with proper type inference.
46
+ * Inherits from the base Factory class implementation.
47
+ *
48
+ * @template Seed - The seed function type
49
+ * @param seedFn - The seed function to wrap
50
+ * @returns The same seed function with proper typing
51
+ */
8
52
  static createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
9
53
  return Factory.createSeed(seedFn);
10
54
  }
11
55
 
56
+ /**
57
+ * Creates a new ObjectionFactory instance.
58
+ *
59
+ * @param builders - Record of builder functions for creating individual entities
60
+ * @param seeds - Record of seed functions for creating complex test scenarios
61
+ * @param db - Knex database connection instance
62
+ */
12
63
  constructor(
13
64
  private builders: Builders,
14
65
  private seeds: Seeds,
@@ -17,7 +68,37 @@ export class ObjectionFactory<
17
68
  super();
18
69
  }
19
70
 
20
- insert(factory, attrs = {}) {
71
+ /**
72
+ * Inserts a single record into the database using the specified builder.
73
+ * Uses Objection.js's insertGraph method to handle nested relations.
74
+ *
75
+ * @param factory - The name of the builder to use
76
+ * @param attrs - Optional attributes to override builder defaults
77
+ * @returns A promise resolving to the inserted record with all relations
78
+ * @throws Error if the specified builder doesn't exist
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * // Insert with defaults
83
+ * const user = await factory.insert('user');
84
+ *
85
+ * // Insert with overrides
86
+ * const adminUser = await factory.insert('user', {
87
+ * email: 'admin@example.com',
88
+ * role: 'admin'
89
+ * });
90
+ *
91
+ * // Insert with nested relations
92
+ * const userWithProfile = await factory.insert('user', {
93
+ * name: 'John Doe',
94
+ * profile: {
95
+ * bio: 'Software Developer',
96
+ * avatar: 'avatar.jpg'
97
+ * }
98
+ * });
99
+ * ```
100
+ */
101
+ insert(factory: keyof Builders, attrs: any = {}) {
21
102
  if (!(factory in this.builders)) {
22
103
  throw new Error(
23
104
  `Factory "${
@@ -30,7 +111,36 @@ export class ObjectionFactory<
30
111
  return record.$query(this.db).insertGraph(record).execute();
31
112
  }) as any;
32
113
  }
33
- insertMany(count, builderName, attrs = {}) {
114
+ /**
115
+ * Inserts multiple records into the database using the specified builder.
116
+ * Supports both static attributes and dynamic attribute generation via a function.
117
+ *
118
+ * @param count - The number of records to insert
119
+ * @param builderName - The name of the builder to use
120
+ * @param attrs - Static attributes or a function that generates attributes for each record
121
+ * @returns A promise resolving to an array of inserted records
122
+ * @throws Error if the specified builder doesn't exist
123
+ *
124
+ * @example
125
+ * ```typescript
126
+ * // Insert multiple with same attributes
127
+ * const users = await factory.insertMany(5, 'user', { role: 'member' });
128
+ *
129
+ * // Insert multiple with dynamic attributes
130
+ * const posts = await factory.insertMany(10, 'post', (idx) => ({
131
+ * title: `Post ${idx + 1}`,
132
+ * content: `Content for post ${idx + 1}`,
133
+ * publishedAt: new Date()
134
+ * }));
135
+ *
136
+ * // Create users with sequential emails
137
+ * const admins = await factory.insertMany(3, 'user', (idx) => ({
138
+ * email: `admin${idx + 1}@example.com`,
139
+ * role: 'admin'
140
+ * }));
141
+ * ```
142
+ */
143
+ insertMany(count: number, builderName: keyof Builders, attrs: any = {}) {
34
144
  if (!(builderName in this.builders)) {
35
145
  throw new Error(
36
146
  `Builder "${
@@ -52,7 +162,39 @@ export class ObjectionFactory<
52
162
 
53
163
  return Promise.all(records);
54
164
  }
55
- seed(seedName, attrs = {}) {
165
+ /**
166
+ * Executes a seed function to create complex test scenarios with multiple related records.
167
+ * Seeds are useful for setting up complete test environments with realistic data relationships.
168
+ *
169
+ * @param seedName - The name of the seed to execute
170
+ * @param attrs - Optional configuration attributes for the seed
171
+ * @returns The result of the seed function (typically the primary record created)
172
+ * @throws Error if the specified seed doesn't exist
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * // Execute a simple seed
177
+ * const user = await factory.seed('userWithProfile');
178
+ *
179
+ * // Execute a seed with configuration
180
+ * const author = await factory.seed('authorWithBooks', {
181
+ * bookCount: 5,
182
+ * includeReviews: true
183
+ * });
184
+ *
185
+ * // Use seed result in tests with Objection.js relations
186
+ * const company = await factory.seed('companyWithDepartments', {
187
+ * departmentCount: 3,
188
+ * employeesPerDepartment: 10
189
+ * });
190
+ *
191
+ * // Access eager loaded relations
192
+ * const companyWithRelations = await Company.query()
193
+ * .findById(company.id)
194
+ * .withGraphFetched('[departments.employees]');
195
+ * ```
196
+ */
197
+ seed(seedName: keyof Seeds, attrs: any = {}) {
56
198
  if (!(seedName in this.seeds)) {
57
199
  throw new Error(
58
200
  `Seed "${
@@ -1,9 +1,56 @@
1
1
  import { type Kysely, type MigrationProvider, Migrator } from 'kysely';
2
2
  import { PostgresMigrator } from './PostgresMigrator';
3
3
 
4
+ /**
5
+ * Default logger instance for migration operations.
6
+ */
4
7
  const logger = console;
5
8
 
9
+ /**
10
+ * PostgreSQL migrator implementation for Kysely ORM.
11
+ * Extends PostgresMigrator to provide Kysely-specific migration functionality.
12
+ * Automatically creates test databases and applies migrations for testing environments.
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { FileMigrationProvider } from 'kysely';
17
+ * import { PostgresKyselyMigrator } from '@geekmidas/testkit';
18
+ *
19
+ * // Create migration provider
20
+ * const provider = new FileMigrationProvider({
21
+ * fs: require('fs'),
22
+ * path: require('path'),
23
+ * migrationFolder: path.join(__dirname, 'migrations')
24
+ * });
25
+ *
26
+ * // Create Kysely instance
27
+ * const db = new Kysely<Database>({
28
+ * dialect: new PostgresDialect({
29
+ * pool: new Pool({ connectionString: uri })
30
+ * })
31
+ * });
32
+ *
33
+ * // Create and use migrator
34
+ * const migrator = new PostgresKyselyMigrator({
35
+ * uri: 'postgresql://localhost:5432/test_db',
36
+ * db,
37
+ * provider
38
+ * });
39
+ *
40
+ * const cleanup = await migrator.start();
41
+ * // Run tests...
42
+ * await cleanup();
43
+ * ```
44
+ */
6
45
  export class PostgresKyselyMigrator extends PostgresMigrator {
46
+ /**
47
+ * Creates a new PostgresKyselyMigrator instance.
48
+ *
49
+ * @param options - Configuration options
50
+ * @param options.uri - PostgreSQL connection URI
51
+ * @param options.db - Kysely database instance
52
+ * @param options.provider - Migration provider for locating migration files
53
+ */
7
54
  constructor(
8
55
  private options: {
9
56
  uri: string;
@@ -14,6 +61,13 @@ export class PostgresKyselyMigrator extends PostgresMigrator {
14
61
  super(options.uri);
15
62
  }
16
63
 
64
+ /**
65
+ * Executes Kysely migrations to the latest version.
66
+ * Implements the abstract migrate() method from PostgresMigrator.
67
+ *
68
+ * @throws Error if migrations fail to apply
69
+ * @returns Promise that resolves when all migrations are applied
70
+ */
17
71
  async migrate(): Promise<void> {
18
72
  const migrator = new Migrator({
19
73
  db: this.options.db,