@geekmidas/testkit 9.0.2 → 10.0.0-alpha.1

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 (129) hide show
  1. package/dist/{Factory-CUM2767q.d.cts → Factory-Cwzho3c8.d.cts} +2 -2
  2. package/dist/{Factory-ClyZLRsO.d.mts.map → Factory-Cwzho3c8.d.cts.map} +1 -1
  3. package/dist/{Factory-ClyZLRsO.d.mts → Factory-SFupxRC2.d.mts} +2 -2
  4. package/dist/{Factory-CUM2767q.d.cts.map → Factory-SFupxRC2.d.mts.map} +1 -1
  5. package/dist/Factory.d.cts +2 -2
  6. package/dist/Factory.d.mts +2 -2
  7. package/dist/{KyselyFactory-OMwcuAX_.d.cts → KyselyFactory-hMcVtvJq.d.cts} +3 -3
  8. package/dist/{KyselyFactory-CTZmiGI9.d.mts.map → KyselyFactory-hMcVtvJq.d.cts.map} +1 -1
  9. package/dist/{KyselyFactory-CTZmiGI9.d.mts → KyselyFactory-vAxYodck.d.mts} +3 -3
  10. package/dist/{KyselyFactory-OMwcuAX_.d.cts.map → KyselyFactory-vAxYodck.d.mts.map} +1 -1
  11. package/dist/KyselyFactory.d.cts +3 -3
  12. package/dist/KyselyFactory.d.mts +3 -3
  13. package/dist/{ObjectionFactory-DDrKwAoO.d.mts → ObjectionFactory-BWjB49-i.d.mts} +3 -3
  14. package/dist/{ObjectionFactory-DDrKwAoO.d.mts.map → ObjectionFactory-BWjB49-i.d.mts.map} +1 -1
  15. package/dist/{ObjectionFactory-DvmZVHhe.d.cts → ObjectionFactory-DW-qwnqO.d.cts} +3 -3
  16. package/dist/{ObjectionFactory-DvmZVHhe.d.cts.map → ObjectionFactory-DW-qwnqO.d.cts.map} +1 -1
  17. package/dist/ObjectionFactory.d.cts +3 -3
  18. package/dist/ObjectionFactory.d.mts +3 -3
  19. package/dist/{VitestKyselyTransactionIsolator-CfSMOFlN.mjs → VitestKyselyTransactionIsolator-1Saieke7.mjs} +10 -3
  20. package/dist/VitestKyselyTransactionIsolator-1Saieke7.mjs.map +1 -0
  21. package/dist/{VitestKyselyTransactionIsolator-0CqOsuc9.cjs → VitestKyselyTransactionIsolator-BjJSXryR.cjs} +10 -3
  22. package/dist/VitestKyselyTransactionIsolator-BjJSXryR.cjs.map +1 -0
  23. package/dist/{VitestKyselyTransactionIsolator-CduJlHoT.d.cts → VitestKyselyTransactionIsolator-CwlTo6yf.d.mts} +8 -3
  24. package/dist/VitestKyselyTransactionIsolator-CwlTo6yf.d.mts.map +1 -0
  25. package/dist/{VitestKyselyTransactionIsolator-Cswnnj0k.d.mts → VitestKyselyTransactionIsolator-DJasVuIZ.d.cts} +8 -3
  26. package/dist/VitestKyselyTransactionIsolator-DJasVuIZ.d.cts.map +1 -0
  27. package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
  28. package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
  29. package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
  30. package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
  31. package/dist/{VitestObjectionTransactionIsolator-CNo40EdC.mjs → VitestObjectionTransactionIsolator-B_VSWFum.mjs} +2 -2
  32. package/dist/{VitestObjectionTransactionIsolator-CNo40EdC.mjs.map → VitestObjectionTransactionIsolator-B_VSWFum.mjs.map} +1 -1
  33. package/dist/{VitestObjectionTransactionIsolator-BXoR6xdG.d.cts → VitestObjectionTransactionIsolator-CTTjvTPO.d.cts} +2 -2
  34. package/dist/{VitestObjectionTransactionIsolator-BXoR6xdG.d.cts.map → VitestObjectionTransactionIsolator-CTTjvTPO.d.cts.map} +1 -1
  35. package/dist/{VitestObjectionTransactionIsolator-x6hY5j4u.d.mts → VitestObjectionTransactionIsolator-CyZG6nq4.d.mts} +2 -2
  36. package/dist/{VitestObjectionTransactionIsolator-x6hY5j4u.d.mts.map → VitestObjectionTransactionIsolator-CyZG6nq4.d.mts.map} +1 -1
  37. package/dist/{VitestObjectionTransactionIsolator-B5J9jjKq.cjs → VitestObjectionTransactionIsolator-DDoJTu7e.cjs} +2 -2
  38. package/dist/{VitestObjectionTransactionIsolator-B5J9jjKq.cjs.map → VitestObjectionTransactionIsolator-DDoJTu7e.cjs.map} +1 -1
  39. package/dist/VitestObjectionTransactionIsolator.cjs +2 -2
  40. package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
  41. package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
  42. package/dist/VitestObjectionTransactionIsolator.mjs +2 -2
  43. package/dist/{VitestTransactionIsolator-BNWJqh9f.d.mts → VitestTransactionIsolator-BLaw80cx.d.mts} +20 -4
  44. package/dist/VitestTransactionIsolator-BLaw80cx.d.mts.map +1 -0
  45. package/dist/{VitestTransactionIsolator-D6ZxLWys.mjs → VitestTransactionIsolator-CvwPecpl.mjs} +17 -4
  46. package/dist/VitestTransactionIsolator-CvwPecpl.mjs.map +1 -0
  47. package/dist/{VitestTransactionIsolator-CtZaOUjH.cjs → VitestTransactionIsolator-DyiX-QtK.cjs} +17 -4
  48. package/dist/VitestTransactionIsolator-DyiX-QtK.cjs.map +1 -0
  49. package/dist/{VitestTransactionIsolator-CSroc7Df.d.cts → VitestTransactionIsolator-glcxyS_R.d.cts} +20 -4
  50. package/dist/VitestTransactionIsolator-glcxyS_R.d.cts.map +1 -0
  51. package/dist/VitestTransactionIsolator.cjs +1 -1
  52. package/dist/VitestTransactionIsolator.d.cts +2 -2
  53. package/dist/VitestTransactionIsolator.d.mts +2 -2
  54. package/dist/VitestTransactionIsolator.mjs +1 -1
  55. package/dist/better-auth.d.cts +2 -2
  56. package/dist/better-auth.d.mts +2 -2
  57. package/dist/{directory-DGOcVlKD.d.cts → directory-Bpz5c5pj.d.cts} +3 -3
  58. package/dist/{directory-DGOcVlKD.d.cts.map → directory-Bpz5c5pj.d.cts.map} +1 -1
  59. package/dist/{faker-BcjUfHxx.d.mts → faker-DHh7xs4u.d.mts} +3 -3
  60. package/dist/{faker-BcjUfHxx.d.mts.map → faker-DHh7xs4u.d.mts.map} +1 -1
  61. package/dist/{faker-zVCm31nU.d.cts → faker-Dg3trU4a.d.cts} +3 -3
  62. package/dist/{faker-zVCm31nU.d.cts.map → faker-Dg3trU4a.d.cts.map} +1 -1
  63. package/dist/faker.d.cts +1 -1
  64. package/dist/faker.d.mts +1 -1
  65. package/dist/kysely.cjs +2 -2
  66. package/dist/kysely.d.cts +5 -5
  67. package/dist/kysely.d.mts +5 -5
  68. package/dist/kysely.mjs +2 -2
  69. package/dist/objection.cjs +2 -2
  70. package/dist/objection.d.cts +5 -5
  71. package/dist/objection.d.mts +5 -5
  72. package/dist/objection.mjs +2 -2
  73. package/dist/os/directory.d.cts +1 -1
  74. package/dist/os/index.d.cts +1 -1
  75. package/dist/requestContext.cjs +1 -1
  76. package/dist/requestContext.d.cts +1 -1
  77. package/dist/requestContext.d.mts +1 -1
  78. package/dist/requestContext.mjs +1 -1
  79. package/package.json +10 -7
  80. package/CHANGELOG.md +0 -151
  81. package/dist/VitestKyselyTransactionIsolator-0CqOsuc9.cjs.map +0 -1
  82. package/dist/VitestKyselyTransactionIsolator-CduJlHoT.d.cts.map +0 -1
  83. package/dist/VitestKyselyTransactionIsolator-CfSMOFlN.mjs.map +0 -1
  84. package/dist/VitestKyselyTransactionIsolator-Cswnnj0k.d.mts.map +0 -1
  85. package/dist/VitestTransactionIsolator-BNWJqh9f.d.mts.map +0 -1
  86. package/dist/VitestTransactionIsolator-CSroc7Df.d.cts.map +0 -1
  87. package/dist/VitestTransactionIsolator-CtZaOUjH.cjs.map +0 -1
  88. package/dist/VitestTransactionIsolator-D6ZxLWys.mjs.map +0 -1
  89. package/src/Factory.ts +0 -174
  90. package/src/KyselyFactory.ts +0 -391
  91. package/src/ObjectionFactory.ts +0 -412
  92. package/src/PostgresKyselyMigrator.ts +0 -104
  93. package/src/PostgresMigrator.ts +0 -193
  94. package/src/PostgresObjectionMigrator.ts +0 -138
  95. package/src/VitestKyselyTransactionIsolator.ts +0 -72
  96. package/src/VitestObjectionTransactionIsolator.ts +0 -75
  97. package/src/VitestTransactionIsolator.ts +0 -348
  98. package/src/__tests__/Factory.spec.ts +0 -178
  99. package/src/__tests__/KyselyFactory.spec.ts +0 -458
  100. package/src/__tests__/ObjectionFactory.spec.ts +0 -586
  101. package/src/__tests__/PostgresKyselyMigrator.spec.ts +0 -794
  102. package/src/__tests__/PostgresMigrator.spec.ts +0 -471
  103. package/src/__tests__/PostgresObjectionMigrator.spec.ts +0 -634
  104. package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +0 -138
  105. package/src/__tests__/benchmark.spec.ts +0 -140
  106. package/src/__tests__/better-auth.spec.ts +0 -21
  107. package/src/__tests__/faker.spec.ts +0 -231
  108. package/src/__tests__/initScript.spec.ts +0 -332
  109. package/src/__tests__/integration.spec.ts +0 -610
  110. package/src/__tests__/requestContext.spec.ts +0 -113
  111. package/src/__tests__/utilities.spec.ts +0 -211
  112. package/src/aws.ts +0 -131
  113. package/src/benchmark.ts +0 -48
  114. package/src/better-auth.ts +0 -310
  115. package/src/faker.ts +0 -349
  116. package/src/helpers.ts +0 -45
  117. package/src/initScript.ts +0 -122
  118. package/src/kysely.ts +0 -166
  119. package/src/logger.ts +0 -18
  120. package/src/objection.ts +0 -157
  121. package/src/os/directory.ts +0 -26
  122. package/src/os/index.ts +0 -1
  123. package/src/requestContext.ts +0 -119
  124. package/src/timer.ts +0 -3
  125. package/test/globalSetup.ts +0 -54
  126. package/test/helpers.ts +0 -268
  127. package/test/migrations/1749664623372_user.ts +0 -22
  128. package/tsconfig.json +0 -9
  129. package/vitest.config.ts +0 -8
@@ -1,412 +0,0 @@
1
- import type { Knex } from 'knex';
2
- import type { Model } from 'objection';
3
- import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory';
4
- import { type FakerFactory, faker } from './faker';
5
-
6
- /**
7
- * Factory implementation for Objection.js ORM, providing test data creation utilities.
8
- * Extends the base Factory class with Objection.js-specific database operations.
9
- *
10
- * @template Builders - Record of builder functions for creating entities
11
- * @template Seeds - Record of seed functions for complex test scenarios
12
- *
13
- * @example
14
- * ```typescript
15
- * // Define your models with Objection.js
16
- * class User extends Model {
17
- * static tableName = 'users';
18
- * }
19
- *
20
- * // Create builders
21
- * const builders = {
22
- * user: ObjectionFactory.createBuilder(User, ({ attrs, faker }) => ({
23
- * id: faker.string.uuid(),
24
- * name: faker.person.fullName(),
25
- * email: faker.internet.email(),
26
- * ...attrs
27
- * })),
28
- * post: ObjectionFactory.createBuilder(Post, ({ attrs }) => ({
29
- * title: 'Test Post',
30
- * content: 'Test content',
31
- * ...attrs
32
- * })),
33
- * };
34
- *
35
- * // Create factory instance
36
- * const factory = new ObjectionFactory(builders, seeds, knex);
37
- *
38
- * // Use in tests
39
- * const user = await factory.insert('user', { name: 'John Doe' });
40
- * ```
41
- */
42
- export class ObjectionFactory<
43
- Builders extends Record<string, any>,
44
- Seeds extends Record<string, any>,
45
- > extends Factory<Builders, Seeds> {
46
- /**
47
- * Creates a typed seed function with proper type inference.
48
- * Inherits from the base Factory class implementation.
49
- *
50
- * @template Seed - The seed function type
51
- * @param seedFn - The seed function to wrap (receives { attrs, factory, db } object)
52
- * @returns The same seed function with proper typing
53
- *
54
- * @example
55
- * ```typescript
56
- * const seeds = {
57
- * userWithPosts: ObjectionFactory.createSeed(
58
- * async ({ attrs, factory }) => {
59
- * const user = await factory.insert('user', attrs);
60
- * await factory.insertMany(3, 'post', { userId: user.id });
61
- * return user;
62
- * },
63
- * ),
64
- * };
65
- * ```
66
- */
67
- static override createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
68
- return Factory.createSeed(seedFn);
69
- }
70
-
71
- /**
72
- * Creates a typed builder function for Objection.js models.
73
- * This is a utility method that helps create builders with proper type inference.
74
- *
75
- * @template TModel - The Objection.js Model class type
76
- * @template Attrs - The attributes type for the builder (defaults to Partial of model)
77
- * @template Factory - The factory instance type
78
- * @template Result - The result type (defaults to the model instance)
79
- *
80
- * @param ModelClass - The Objection.js Model class
81
- * @param defaults - Optional function to provide default values (receives destructured context)
82
- * @param autoInsert - Whether to automatically insert the record (default: true)
83
- * @returns A builder function that creates and optionally inserts records
84
- *
85
- * @example
86
- * ```typescript
87
- * // Create a simple builder with defaults - destructure only what you need
88
- * const userBuilder = ObjectionFactory.createBuilder(User,
89
- * ({ attrs, faker }) => ({
90
- * id: faker.string.uuid(),
91
- * name: faker.person.fullName(),
92
- * email: faker.internet.email(),
93
- * createdAt: new Date(),
94
- * ...attrs
95
- * })
96
- * );
97
- *
98
- * // Only need faker? Just destructure that
99
- * const leaveTypeBuilder = ObjectionFactory.createBuilder(LeaveType,
100
- * ({ faker }) => ({
101
- * name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
102
- * code: faker.string.alpha({ length: 3, casing: 'upper' }),
103
- * })
104
- * );
105
- *
106
- * // Create a builder that doesn't auto-insert (useful for nested inserts)
107
- * const addressBuilder = ObjectionFactory.createBuilder(Address,
108
- * ({ attrs }) => ({
109
- * street: '123 Main St',
110
- * city: 'Anytown',
111
- * ...attrs
112
- * }),
113
- * false // Don't auto-insert
114
- * );
115
- *
116
- * // Use with relations
117
- * const postBuilder = ObjectionFactory.createBuilder(Post,
118
- * async ({ attrs, factory, faker }) => ({
119
- * title: faker.lorem.sentence(),
120
- * content: faker.lorem.paragraphs(),
121
- * authorId: attrs.authorId || (await factory.insert('user')).id,
122
- * ...attrs
123
- * })
124
- * );
125
- * ```
126
- */
127
- static createBuilder<
128
- TModel extends typeof Model,
129
- Attrs extends Partial<InstanceType<TModel>> = Partial<InstanceType<TModel>>,
130
- Factory = any,
131
- Result = InstanceType<TModel>,
132
- >(
133
- ModelClass: TModel,
134
- defaults?: (context: {
135
- attrs: Attrs;
136
- factory: Factory;
137
- db: Knex;
138
- faker: FakerFactory;
139
- }) =>
140
- | Partial<InstanceType<TModel>>
141
- | Promise<Partial<InstanceType<TModel>>>,
142
- autoInsert?: boolean,
143
- ): (
144
- attrs: Attrs,
145
- factory: Factory,
146
- db: Knex,
147
- faker: FakerFactory,
148
- ) => Promise<Result> {
149
- return async (
150
- attrs: Attrs,
151
- factory: Factory,
152
- db: Knex,
153
- fakerInstance: FakerFactory,
154
- ) => {
155
- // Start with attributes
156
- let data: Partial<InstanceType<TModel>> = { ...attrs };
157
-
158
- // Apply defaults
159
- if (defaults) {
160
- const defaultValues = await defaults({
161
- attrs,
162
- factory,
163
- db,
164
- faker: fakerInstance,
165
- });
166
- data = { ...defaultValues, ...data };
167
- }
168
-
169
- // Create model instance
170
- const model = ModelClass.fromJson(data) as InstanceType<TModel>;
171
-
172
- // Handle insertion based on autoInsert flag
173
- if (autoInsert !== false) {
174
- // Auto insert is enabled by default
175
- // Extract only defined values for insertion
176
- const insertData = Object.entries(model).reduce((acc, [key, value]) => {
177
- if (value !== undefined && key !== 'id') {
178
- acc[key] = value;
179
- }
180
- return acc;
181
- }, {} as any);
182
-
183
- // Use static query method to insert data directly
184
- const result = await ModelClass.query(db).insert(insertData);
185
- return result as Result;
186
- } else {
187
- // Return model for factory to handle insertion
188
- return model as Result;
189
- }
190
- };
191
- }
192
-
193
- /**
194
- * Creates a new ObjectionFactory instance.
195
- *
196
- * @param builders - Record of builder functions for creating individual entities
197
- * @param seeds - Record of seed functions for creating complex test scenarios
198
- * @param db - Knex database connection instance
199
- */
200
- constructor(
201
- private builders: Builders,
202
- private seeds: Seeds,
203
- private db: Knex,
204
- ) {
205
- super();
206
- }
207
-
208
- /**
209
- * Inserts a single record into the database using the specified builder.
210
- * Uses Objection.js's insertGraph method to handle nested relations.
211
- *
212
- * @template K - The builder name (must be a key of Builders)
213
- * @param builderName - The name of the builder to use
214
- * @param attrs - Optional attributes to override builder defaults
215
- * @returns A promise resolving to the inserted record with all relations
216
- * @throws Error if the specified builder doesn't exist
217
- *
218
- * @example
219
- * ```typescript
220
- * // Insert with defaults
221
- * const user = await factory.insert('user');
222
- *
223
- * // Insert with overrides
224
- * const adminUser = await factory.insert('user', {
225
- * email: 'admin@example.com',
226
- * role: 'admin'
227
- * });
228
- *
229
- * // Insert with nested relations
230
- * const userWithProfile = await factory.insert('user', {
231
- * name: 'John Doe',
232
- * profile: {
233
- * bio: 'Software Developer',
234
- * avatar: 'avatar.jpg'
235
- * }
236
- * });
237
- * ```
238
- */
239
- async insert<K extends keyof Builders>(
240
- builderName: K,
241
- attrs?: Parameters<Builders[K]>[0],
242
- ): Promise<Awaited<ReturnType<Builders[K]>>> {
243
- if (!(builderName in this.builders)) {
244
- throw new Error(
245
- `Factory "${
246
- builderName as string
247
- }" does not exist. Make sure it is correct and registered in src/test/setup.ts`,
248
- );
249
- }
250
-
251
- const result = await this.builders[builderName](
252
- attrs || {},
253
- this,
254
- this.db,
255
- faker,
256
- );
257
-
258
- // If the builder returns a model instance, insert it
259
- if (result && typeof result.$query === 'function') {
260
- // Extract data from model, excluding undefined values and id
261
- const insertData = Object.entries(result).reduce((acc, [key, value]) => {
262
- if (value !== undefined && key !== 'id') {
263
- acc[key] = value;
264
- }
265
- return acc;
266
- }, {} as any);
267
-
268
- // Use the model's constructor to get the query builder
269
- return await result.constructor.query(this.db).insert(insertData);
270
- }
271
-
272
- // Otherwise, assume the builder handled insertion itself
273
- return result;
274
- }
275
- /**
276
- * Inserts multiple records into the database using the specified builder.
277
- * Supports both static attributes and dynamic attribute generation via a function.
278
- *
279
- * @param count - The number of records to insert
280
- * @param builderName - The name of the builder to use
281
- * @param attrs - Static attributes or a function that generates attributes for each record
282
- * @returns A promise resolving to an array of inserted records
283
- * @throws Error if the specified builder doesn't exist
284
- *
285
- * @example
286
- * ```typescript
287
- * // Insert multiple with same attributes
288
- * const users = await factory.insertMany(5, 'user', { role: 'member' });
289
- *
290
- * // Insert multiple with dynamic attributes
291
- * const posts = await factory.insertMany(10, 'post', (idx) => ({
292
- * title: `Post ${idx + 1}`,
293
- * content: `Content for post ${idx + 1}`,
294
- * publishedAt: new Date()
295
- * }));
296
- *
297
- * // Create users with sequential emails
298
- * const admins = await factory.insertMany(3, 'user', (idx) => ({
299
- * email: `admin${idx + 1}@example.com`,
300
- * role: 'admin'
301
- * }));
302
- * ```
303
- */
304
- // Method overloads for better type inference
305
- async insertMany<K extends keyof Builders>(
306
- count: number,
307
- builderName: K,
308
- attrs?: Parameters<Builders[K]>[0],
309
- ): Promise<Awaited<ReturnType<Builders[K]>>[]>;
310
- async insertMany<K extends keyof Builders>(
311
- count: number,
312
- builderName: K,
313
- attrs: (idx: number, faker: FakerFactory) => Parameters<Builders[K]>[0],
314
- ): Promise<Awaited<ReturnType<Builders[K]>>[]>;
315
- async insertMany<K extends keyof Builders>(
316
- count: number,
317
- builderName: K,
318
- attrs?: any,
319
- ): Promise<Awaited<ReturnType<Builders[K]>>[]> {
320
- if (!(builderName in this.builders)) {
321
- throw new Error(
322
- `Builder "${
323
- builderName as string
324
- }" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
325
- );
326
- }
327
-
328
- const records: any[] = [];
329
- for (let i = 0; i < count; i++) {
330
- const newAttrs =
331
- typeof attrs === 'function' ? await (attrs as any)(i, faker) : attrs;
332
-
333
- records.push(
334
- this.builders[builderName](newAttrs, this, this.db, faker).then(
335
- (record: any) => {
336
- // If the builder returns a model instance, insert it
337
- if (record && typeof record.$query === 'function') {
338
- // Extract data from model, excluding undefined values and id
339
- const insertData = Object.entries(record).reduce(
340
- (acc, [key, value]) => {
341
- if (value !== undefined && key !== 'id') {
342
- acc[key] = value;
343
- }
344
- return acc;
345
- },
346
- {} as any,
347
- );
348
-
349
- // Use the model's constructor to get the query builder
350
- return record.constructor.query(this.db).insert(insertData);
351
- }
352
- // Otherwise, assume the builder handled insertion itself
353
- return record;
354
- },
355
- ),
356
- );
357
- }
358
-
359
- return Promise.all(records);
360
- }
361
- /**
362
- * Executes a seed function to create complex test scenarios with multiple related records.
363
- * Seeds are useful for setting up complete test environments with realistic data relationships.
364
- *
365
- * @template K - The seed name (must be a key of Seeds)
366
- * @param seedName - The name of the seed to execute
367
- * @param attrs - Optional configuration attributes for the seed
368
- * @returns The result of the seed function (typically the primary record created)
369
- * @throws Error if the specified seed doesn't exist
370
- *
371
- * @example
372
- * ```typescript
373
- * // Execute a simple seed
374
- * const user = await factory.seed('userWithProfile');
375
- *
376
- * // Execute a seed with configuration
377
- * const author = await factory.seed('authorWithBooks', {
378
- * bookCount: 5,
379
- * includeReviews: true
380
- * });
381
- *
382
- * // Use seed result in tests with Objection.js relations
383
- * const company = await factory.seed('companyWithDepartments', {
384
- * departmentCount: 3,
385
- * employeesPerDepartment: 10
386
- * });
387
- *
388
- * // Access eager loaded relations
389
- * const companyWithRelations = await Company.query()
390
- * .findById(company.id)
391
- * .withGraphFetched('[departments.employees]');
392
- * ```
393
- */
394
- seed<K extends keyof Seeds>(
395
- seedName: K,
396
- attrs?: ExtractSeedAttrs<Seeds[K]>,
397
- ): ReturnType<Seeds[K]> {
398
- if (!(seedName in this.seeds)) {
399
- throw new Error(
400
- `Seed "${
401
- seedName as string
402
- }" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
403
- );
404
- }
405
-
406
- return this.seeds[seedName]({
407
- attrs: attrs || {},
408
- factory: this,
409
- db: this.db,
410
- });
411
- }
412
- }
@@ -1,104 +0,0 @@
1
- import type { Kysely } from 'kysely';
2
- // `Migrator` and the `MigrationProvider` type moved from the root barrel ('kysely')
3
- // to the 'kysely/migration' subpath in kysely 0.29+.
4
- import { type MigrationProvider, Migrator } from 'kysely/migration';
5
- import { PostgresMigrator } from './PostgresMigrator';
6
-
7
- /**
8
- * Default logger instance for migration operations.
9
- */
10
- const logger = console;
11
-
12
- /**
13
- * PostgreSQL migrator implementation for Kysely ORM.
14
- * Extends PostgresMigrator to provide Kysely-specific migration functionality.
15
- * Automatically creates test databases and applies migrations for testing environments.
16
- *
17
- * @example
18
- * ```typescript
19
- * import { FileMigrationProvider } from 'kysely';
20
- * import { PostgresKyselyMigrator } from '@geekmidas/testkit';
21
- *
22
- * // Create migration provider
23
- * const provider = new FileMigrationProvider({
24
- * fs: require('fs'),
25
- * path: require('path'),
26
- * migrationFolder: path.join(__dirname, 'migrations')
27
- * });
28
- *
29
- * // Create Kysely instance
30
- * const db = new Kysely<Database>({
31
- * dialect: new PostgresDialect({
32
- * pool: new Pool({ connectionString: uri })
33
- * })
34
- * });
35
- *
36
- * // Create and use migrator
37
- * const migrator = new PostgresKyselyMigrator({
38
- * uri: 'postgresql://localhost:5432/test_db',
39
- * db,
40
- * provider
41
- * });
42
- *
43
- * const cleanup = await migrator.start();
44
- * // Run tests...
45
- * await cleanup();
46
- *
47
- * // With afterCreate hook to create per-app users
48
- * import { runInitScript } from '@geekmidas/testkit/postgres';
49
- *
50
- * const migrator = new PostgresKyselyMigrator({
51
- * uri: 'postgresql://localhost:5432/test_db',
52
- * db,
53
- * provider,
54
- * afterCreate: async (uri) => {
55
- * await runInitScript('docker/postgres/init.sh', uri, env);
56
- * },
57
- * });
58
- * ```
59
- */
60
- export class PostgresKyselyMigrator extends PostgresMigrator {
61
- /**
62
- * Creates a new PostgresKyselyMigrator instance.
63
- *
64
- * @param options - Configuration options
65
- * @param options.uri - PostgreSQL connection URI
66
- * @param options.db - Kysely database instance
67
- * @param options.provider - Migration provider for locating migration files
68
- * @param options.afterCreate - Optional hook called after database creation but before migrations
69
- */
70
- constructor(
71
- private options: {
72
- uri: string;
73
- db: Kysely<any>;
74
- provider: MigrationProvider;
75
- afterCreate?: (uri: string) => Promise<void>;
76
- },
77
- ) {
78
- super(options.uri, options.afterCreate);
79
- }
80
-
81
- /**
82
- * Executes Kysely migrations to the latest version.
83
- * Implements the abstract migrate() method from PostgresMigrator.
84
- *
85
- * @throws Error if migrations fail to apply
86
- * @returns Promise that resolves when all migrations are applied
87
- */
88
- async migrate(): Promise<void> {
89
- const migrator = new Migrator({
90
- db: this.options.db,
91
- provider: this.options.provider,
92
- });
93
- const migrations = await migrator.migrateToLatest();
94
-
95
- if (migrations.error) {
96
- logger.error(migrations.error, `Failed to apply migrations`);
97
- throw migrations.error;
98
- }
99
-
100
- await this.options.db.destroy();
101
-
102
- logger.log(`Applied ${migrations.results?.length} migrations successfully`);
103
- }
104
- }
@@ -1,193 +0,0 @@
1
- import pg from 'pg';
2
-
3
- const { Client } = pg;
4
-
5
- /**
6
- * Creates a PostgreSQL client connected to the 'postgres' database.
7
- * Extracts connection details from the provided URI.
8
- *
9
- * @param uri - PostgreSQL connection URI
10
- * @returns Object containing the target database name and client instance
11
- *
12
- * @example
13
- * ```typescript
14
- * const { database, db } = await setupClient('postgresql://user:pass@localhost:5432/mydb');
15
- * // database = 'mydb'
16
- * // db = Client instance connected to 'postgres' database
17
- * ```
18
- */
19
- async function setupClient(uri: string) {
20
- const url = new URL(uri);
21
-
22
- const db = new Client({
23
- user: url.username,
24
- password: url.password,
25
- host: url.hostname,
26
- port: parseInt(url.port, 10),
27
- database: 'postgres',
28
- });
29
-
30
- let database = url.pathname.slice(1);
31
- if (database.includes('?')) {
32
- database = database.substring(0, database.indexOf('?'));
33
- }
34
- return { database, db };
35
- }
36
-
37
- /**
38
- * Default logger instance for migration operations.
39
- */
40
- const logger = console;
41
-
42
- /**
43
- * Abstract base class for PostgreSQL database migration utilities.
44
- * Provides database creation, migration, and cleanup functionality for testing.
45
- * Subclasses must implement the migrate() method to define migration logic.
46
- *
47
- * @example
48
- * ```typescript
49
- * class MyMigrator extends PostgresMigrator {
50
- * async migrate(): Promise<void> {
51
- * // Run your migrations here
52
- * await this.runMigrations();
53
- * }
54
- * }
55
- *
56
- * // Use in tests
57
- * const migrator = new MyMigrator('postgresql://localhost:5432/test_db');
58
- * const cleanup = await migrator.start();
59
- *
60
- * // With afterCreate hook (e.g. run init script to create per-app users)
61
- * import { runInitScript } from '@geekmidas/testkit/postgres';
62
- *
63
- * const migrator = new MyMigrator(
64
- * 'postgresql://localhost:5432/test_db',
65
- * async (uri) => {
66
- * await runInitScript('docker/postgres/init.sh', uri, env);
67
- * },
68
- * );
69
- * const cleanup = await migrator.start();
70
- *
71
- * // Run tests...
72
- *
73
- * // Clean up
74
- * await cleanup();
75
- * ```
76
- */
77
- export abstract class PostgresMigrator {
78
- /**
79
- * Creates a new PostgresMigrator instance.
80
- *
81
- * @param uri - PostgreSQL connection URI
82
- * @param afterCreate - Optional hook called after database creation but before migrations
83
- */
84
- constructor(
85
- private uri: string,
86
- private afterCreate?: (uri: string) => Promise<void>,
87
- ) {}
88
-
89
- /**
90
- * Abstract method to be implemented by subclasses.
91
- * Should contain the migration logic for setting up database schema.
92
- *
93
- * @returns Promise that resolves when migrations are complete
94
- */
95
- abstract migrate(): Promise<void>;
96
-
97
- /**
98
- * Creates a PostgreSQL database if it doesn't already exist.
99
- * Connects to the 'postgres' database to check and create the target database.
100
- *
101
- * @param uri - PostgreSQL connection URI
102
- * @returns Object indicating whether the database already existed
103
- * @private
104
- */
105
- private static async create(
106
- uri: string,
107
- ): Promise<{ alreadyExisted: boolean }> {
108
- const { database, db } = await setupClient(uri);
109
- try {
110
- await db.connect();
111
- const result = await db.query(
112
- `SELECT * FROM pg_catalog.pg_database WHERE datname = '${database}'`,
113
- );
114
-
115
- if (result.rowCount === 0) {
116
- try {
117
- await db.query(`CREATE DATABASE "${database}"`);
118
- } catch (error: any) {
119
- // 42P04 = duplicate_database — another process created it between our check and create
120
- if (error?.code === '42P04') {
121
- return { alreadyExisted: true };
122
- }
123
- throw error;
124
- }
125
- }
126
-
127
- return {
128
- alreadyExisted: result.rowCount ? result.rowCount > 0 : false,
129
- };
130
- } finally {
131
- await db.end();
132
- }
133
- }
134
-
135
- /**
136
- * Drops a PostgreSQL database.
137
- * Used for cleanup after tests are complete.
138
- *
139
- * @param uri - PostgreSQL connection URI
140
- * @throws Error if database cannot be dropped
141
- * @private
142
- */
143
- private static async drop(uri: string): Promise<void> {
144
- const { database, db } = await setupClient(uri);
145
- try {
146
- await db.connect();
147
- await db.query(`DROP DATABASE "${database}"`);
148
- } finally {
149
- await db.end();
150
- }
151
- }
152
-
153
- /**
154
- * Starts the migration process by creating the database and running migrations.
155
- * Returns a cleanup function that will drop the database when called.
156
- *
157
- * @returns Async cleanup function that drops the created database
158
- *
159
- * @example
160
- * ```typescript
161
- * const migrator = new MyMigrator('postgresql://localhost:5432/test_db');
162
- *
163
- * // Start migrations and get cleanup function
164
- * const cleanup = await migrator.start();
165
- *
166
- * try {
167
- * // Run your tests here
168
- * await runTests();
169
- * } finally {
170
- * // Always clean up
171
- * await cleanup();
172
- * }
173
- * ```
174
- */
175
- async start() {
176
- const { database, db } = await setupClient(this.uri);
177
- try {
178
- await PostgresMigrator.create(this.uri);
179
- if (this.afterCreate) {
180
- await this.afterCreate(this.uri);
181
- }
182
- await this.migrate();
183
- logger.log(`Migrating database: ${database}`);
184
- // Example: await db.query('CREATE TABLE example (id SERIAL PRIMARY KEY)');
185
- } finally {
186
- await db.end();
187
- }
188
-
189
- return async () => {
190
- await PostgresMigrator.drop(this.uri);
191
- };
192
- }
193
- }