@geekmidas/testkit 0.0.3 → 0.0.5

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 (72) hide show
  1. package/PostgresKyselyMigrator.spec +471 -0
  2. package/dist/{KyselyFactory-DiiWtMYe.cjs → KyselyFactory-BX7Kv2uP.cjs} +11 -12
  3. package/dist/{KyselyFactory-DZewtWtJ.mjs → KyselyFactory-pOMOFQWE.mjs} +11 -12
  4. package/dist/KyselyFactory.cjs +2 -1
  5. package/dist/KyselyFactory.mjs +2 -1
  6. package/dist/ObjectionFactory.cjs +1 -1
  7. package/dist/ObjectionFactory.mjs +1 -1
  8. package/dist/{PostgresKyselyMigrator-ChMJpPrQ.mjs → PostgresKyselyMigrator-D8fm35-s.mjs} +1 -1
  9. package/dist/{PostgresKyselyMigrator-rY3hO_-1.cjs → PostgresKyselyMigrator-JTY2LfwD.cjs} +3 -2
  10. package/dist/PostgresKyselyMigrator.cjs +2 -2
  11. package/dist/PostgresKyselyMigrator.mjs +2 -2
  12. package/dist/{PostgresMigrator-BJ2-5A_b.cjs → PostgresMigrator-Bz-tnjB6.cjs} +2 -39
  13. package/dist/PostgresMigrator.cjs +1 -1
  14. package/dist/PostgresMigrator.mjs +1 -1
  15. package/dist/VitestKyselyTransactionIsolator-BS3R-V0I.mjs +12 -0
  16. package/dist/VitestKyselyTransactionIsolator-DWSTKIe3.cjs +17 -0
  17. package/dist/VitestKyselyTransactionIsolator.cjs +4 -0
  18. package/dist/VitestKyselyTransactionIsolator.mjs +4 -0
  19. package/dist/VitestTransactionIsolator-BjVXqFs6.mjs +40 -0
  20. package/dist/VitestTransactionIsolator-Bx2c4OzK.cjs +52 -0
  21. package/dist/VitestTransactionIsolator.cjs +4 -0
  22. package/dist/VitestTransactionIsolator.mjs +3 -0
  23. package/dist/__tests__/Factory.spec.cjs +139 -0
  24. package/dist/__tests__/Factory.spec.mjs +138 -0
  25. package/dist/__tests__/KyselyFactory.spec.cjs +220 -15008
  26. package/dist/__tests__/KyselyFactory.spec.mjs +218 -15033
  27. package/dist/__tests__/ObjectionFactory.spec.cjs +386 -0
  28. package/dist/__tests__/ObjectionFactory.spec.mjs +385 -0
  29. package/dist/__tests__/PostgresMigrator.spec.cjs +256 -0
  30. package/dist/__tests__/PostgresMigrator.spec.mjs +255 -0
  31. package/dist/__tests__/faker.spec.cjs +115 -0
  32. package/dist/__tests__/faker.spec.mjs +114 -0
  33. package/dist/__tests__/integration.spec.cjs +279 -0
  34. package/dist/__tests__/integration.spec.mjs +278 -0
  35. package/dist/chunk-CUT6urMc.cjs +30 -0
  36. package/dist/example.cjs +2 -1
  37. package/dist/example.mjs +2 -1
  38. package/dist/faker-BwaXA_RF.mjs +85 -0
  39. package/dist/faker-caz-8zt8.cjs +121 -0
  40. package/dist/faker.cjs +8 -0
  41. package/dist/faker.mjs +3 -0
  42. package/dist/helpers-B9Jdk_C7.cjs +31 -0
  43. package/dist/helpers-BfuX-cjN.mjs +111 -0
  44. package/dist/helpers-DKEBHABj.cjs +135 -0
  45. package/dist/helpers-DOiGIkaU.mjs +19 -0
  46. package/dist/helpers.cjs +6 -0
  47. package/dist/helpers.mjs +5 -0
  48. package/dist/kysely.cjs +15 -4
  49. package/dist/kysely.mjs +14 -4
  50. package/dist/objection.cjs +1 -1
  51. package/dist/objection.mjs +1 -1
  52. package/package.json +8 -2
  53. package/src/Factory.ts +3 -1
  54. package/src/KyselyFactory.ts +30 -36
  55. package/src/VitestKyselyTransactionIsolator.ts +23 -0
  56. package/src/VitestTransactionIsolator.ts +70 -0
  57. package/src/__tests__/Factory.spec.ts +164 -0
  58. package/src/__tests__/KyselyFactory.spec.ts +432 -64
  59. package/src/__tests__/ObjectionFactory.spec.ts +532 -0
  60. package/src/__tests__/PostgresMigrator.spec.ts +366 -0
  61. package/src/__tests__/faker.spec.ts +142 -0
  62. package/src/__tests__/integration.spec.ts +442 -0
  63. package/src/faker.ts +112 -0
  64. package/src/helpers.ts +28 -0
  65. package/src/kysely.ts +14 -0
  66. package/test/globalSetup.ts +41 -40
  67. package/test/helpers.ts +273 -0
  68. package/dist/magic-string.es-CxbtJGk_.mjs +0 -1014
  69. package/dist/magic-string.es-KiPEzMtt.cjs +0 -1015
  70. /package/dist/{ObjectionFactory-DeFYWbzt.cjs → ObjectionFactory-BlkzSEqo.cjs} +0 -0
  71. /package/dist/{ObjectionFactory-MAf2m8LI.mjs → ObjectionFactory-ChuX8sZN.mjs} +0 -0
  72. /package/dist/{PostgresMigrator-BKaNTth5.mjs → PostgresMigrator-CEoRKTdq.mjs} +0 -0
@@ -0,0 +1,532 @@
1
+ import type { Knex } from 'knex';
2
+ import { Model } from 'objection';
3
+ import { afterAll, beforeAll, describe, expect, it } from 'vitest';
4
+ import { setupKnexTest } from '../../test/helpers';
5
+ import { ObjectionFactory } from '../ObjectionFactory';
6
+
7
+ // Define real Objection models for testing
8
+ class User extends Model {
9
+ static get tableName() {
10
+ return 'users';
11
+ }
12
+
13
+ id!: number;
14
+ name!: string;
15
+ email!: string;
16
+ role?: string;
17
+ createdAt!: Date;
18
+ updatedAt?: Date;
19
+ }
20
+
21
+ class Post extends Model {
22
+ static get tableName() {
23
+ return 'posts';
24
+ }
25
+
26
+ id!: number;
27
+ title!: string;
28
+ content!: string;
29
+ userId!: number;
30
+ published?: boolean;
31
+ createdAt!: Date;
32
+ updatedAt?: Date;
33
+ }
34
+
35
+ class Comment extends Model {
36
+ static get tableName() {
37
+ return 'comments';
38
+ }
39
+
40
+ id!: number;
41
+ content!: string;
42
+ postId!: number;
43
+ userId!: number;
44
+ createdAt!: Date;
45
+ }
46
+
47
+ describe.skip('ObjectionFactory', () => {
48
+ let factory: ObjectionFactory<any, any>;
49
+ let db: Knex;
50
+ let trx: Knex.Transaction;
51
+ let cleanup: () => Promise<void>;
52
+
53
+ beforeAll(async () => {
54
+ const setup = await setupKnexTest();
55
+ db = setup.db;
56
+ trx = setup.trx;
57
+ cleanup = setup.cleanup;
58
+
59
+ // Bind models to the transaction
60
+ User.knex(trx);
61
+ Post.knex(trx);
62
+ Comment.knex(trx);
63
+ });
64
+
65
+ afterAll(async () => {
66
+ await cleanup();
67
+ });
68
+
69
+ describe('constructor', () => {
70
+ it('should create an ObjectionFactory instance', () => {
71
+ const builders = {};
72
+ const seeds = {};
73
+
74
+ factory = new ObjectionFactory(builders, seeds, trx);
75
+
76
+ expect(factory).toBeInstanceOf(ObjectionFactory);
77
+ });
78
+ });
79
+
80
+ describe('insert method', () => {
81
+ it('should call builder and insert the record', async () => {
82
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
83
+ return User.fromJson({
84
+ name: attrs.name || 'Default Name',
85
+ email: attrs.email || `user${Date.now()}@example.com`,
86
+ role: attrs.role || 'user',
87
+ createdAt: new Date(),
88
+ updatedAt: new Date(),
89
+ });
90
+ };
91
+
92
+ const builders = {
93
+ user: userBuilder,
94
+ };
95
+
96
+ factory = new ObjectionFactory(builders, {}, trx);
97
+
98
+ const attrs = { name: 'John Doe', email: 'john@example.com' };
99
+ const result = await factory.insert('user', attrs);
100
+
101
+ expect(result).toBeInstanceOf(User);
102
+ expect(result.name).toBe('John Doe');
103
+ expect(result.email).toBe('john@example.com');
104
+ expect(result.id).toBeDefined();
105
+ });
106
+
107
+ it('should use empty object as default attributes', async () => {
108
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
109
+ return User.fromJson({
110
+ name: 'Default Name',
111
+ email: `user${Date.now()}@example.com`,
112
+ role: 'user',
113
+ createdAt: new Date(),
114
+ updatedAt: new Date(),
115
+ });
116
+ };
117
+
118
+ const builders = {
119
+ user: userBuilder,
120
+ };
121
+
122
+ factory = new ObjectionFactory(builders, {}, trx);
123
+
124
+ const result = await factory.insert('user');
125
+
126
+ expect(result).toBeInstanceOf(User);
127
+ expect(result.name).toBe('Default Name');
128
+ expect(result.id).toBeDefined();
129
+ });
130
+
131
+ it('should throw error for non-existent factory', async () => {
132
+ factory = new ObjectionFactory({}, {}, trx);
133
+
134
+ await expect(factory.insert('nonExistent')).rejects.toThrow(
135
+ 'Factory "nonExistent" does not exist',
136
+ );
137
+ });
138
+
139
+ it('should handle builder that returns a promise', async () => {
140
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
141
+ // Simulate async operation
142
+ await new Promise((resolve) => setTimeout(resolve, 10));
143
+ return User.fromJson({
144
+ name: attrs.name || 'Default Name',
145
+ email: `async${Date.now()}@example.com`,
146
+ role: 'user',
147
+ createdAt: new Date(),
148
+ updatedAt: new Date(),
149
+ });
150
+ };
151
+
152
+ const builders = {
153
+ user: userBuilder,
154
+ };
155
+
156
+ factory = new ObjectionFactory(builders, {}, trx);
157
+
158
+ const result = await factory.insert('user', { name: 'Jane' });
159
+
160
+ expect(result).toBeInstanceOf(User);
161
+ expect(result.name).toBe('Jane');
162
+ });
163
+ });
164
+
165
+ describe('insertMany method', () => {
166
+ it('should insert multiple records with same attributes', async () => {
167
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
168
+ return User.fromJson({
169
+ name: attrs.name || 'Default Name',
170
+ email: `user${Date.now()}-${Math.random()}@example.com`,
171
+ role: 'user',
172
+ createdAt: new Date(),
173
+ updatedAt: new Date(),
174
+ });
175
+ };
176
+
177
+ const builders = {
178
+ user: userBuilder,
179
+ };
180
+
181
+ factory = new ObjectionFactory(builders, {}, trx);
182
+
183
+ const attrs = { name: 'User' };
184
+ const results = await factory.insertMany(3, 'user', attrs);
185
+
186
+ expect(results).toHaveLength(3);
187
+ results.forEach((result) => {
188
+ expect(result).toBeInstanceOf(User);
189
+ expect(result.name).toBe('User');
190
+ expect(result.id).toBeDefined();
191
+ });
192
+ });
193
+
194
+ it('should insert multiple records with dynamic attributes', async () => {
195
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
196
+ return User.fromJson({
197
+ name: attrs.name || 'Default Name',
198
+ email: `user${Date.now()}-${Math.random()}@example.com`,
199
+ role: 'user',
200
+ createdAt: new Date(),
201
+ updatedAt: new Date(),
202
+ });
203
+ };
204
+
205
+ const builders = {
206
+ user: userBuilder,
207
+ };
208
+
209
+ factory = new ObjectionFactory(builders, {}, trx);
210
+
211
+ const attrsFn = (idx: number) => ({ name: `User ${idx}` });
212
+ const results = await factory.insertMany(2, 'user', attrsFn);
213
+
214
+ expect(results).toHaveLength(2);
215
+ expect(results[0].name).toBe('User 0');
216
+ expect(results[1].name).toBe('User 1');
217
+ results.forEach((result) => {
218
+ expect(result).toBeInstanceOf(User);
219
+ expect(result.id).toBeDefined();
220
+ });
221
+ });
222
+
223
+ it('should use empty object as default attributes for insertMany', async () => {
224
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
225
+ return User.fromJson({
226
+ name: 'Default Name',
227
+ email: `user${Date.now()}-${Math.random()}@example.com`,
228
+ role: 'user',
229
+ createdAt: new Date(),
230
+ updatedAt: new Date(),
231
+ });
232
+ };
233
+
234
+ const builders = {
235
+ user: userBuilder,
236
+ };
237
+
238
+ factory = new ObjectionFactory(builders, {}, trx);
239
+
240
+ const results = await factory.insertMany(2, 'user');
241
+
242
+ expect(results).toHaveLength(2);
243
+ results.forEach((result) => {
244
+ expect(result).toBeInstanceOf(User);
245
+ expect(result.name).toBe('Default Name');
246
+ expect(result.id).toBeDefined();
247
+ });
248
+ });
249
+
250
+ it('should throw error for non-existent builder in insertMany', async () => {
251
+ factory = new ObjectionFactory({}, {}, trx);
252
+
253
+ await expect(factory.insertMany(2, 'nonExistent')).rejects.toThrow(
254
+ 'Builder "nonExistent" is not registered',
255
+ );
256
+ });
257
+ });
258
+
259
+ describe('seed method', () => {
260
+ it('should execute seed function', async () => {
261
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
262
+ return User.fromJson({
263
+ name: attrs.name || 'Default Name',
264
+ email: attrs.email || `admin${Date.now()}@example.com`,
265
+ role: 'admin',
266
+ createdAt: new Date(),
267
+ updatedAt: new Date(),
268
+ });
269
+ };
270
+
271
+ const builders = { user: userBuilder };
272
+
273
+ const createAdminSeed = async (attrs: any, factory: any, db: Knex) => {
274
+ return await factory.insert('user', {
275
+ name: attrs.name || 'Admin User',
276
+ email: 'admin@example.com',
277
+ role: 'admin',
278
+ });
279
+ };
280
+
281
+ const seeds = {
282
+ createAdmin: createAdminSeed,
283
+ };
284
+
285
+ factory = new ObjectionFactory(builders, seeds, trx);
286
+
287
+ const attrs = { name: 'Super Admin' };
288
+ const result = await factory.seed('createAdmin', attrs);
289
+
290
+ expect(result).toBeInstanceOf(User);
291
+ expect(result.name).toBe('Super Admin');
292
+ expect(result.role).toBe('admin');
293
+ expect(result.id).toBeDefined();
294
+ });
295
+
296
+ it('should use empty object as default attributes for seed', async () => {
297
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
298
+ return User.fromJson({
299
+ name: 'Default Admin',
300
+ email: `admin${Date.now()}@example.com`,
301
+ role: 'admin',
302
+ createdAt: new Date(),
303
+ updatedAt: new Date(),
304
+ });
305
+ };
306
+
307
+ const builders = { user: userBuilder };
308
+
309
+ const createAdminSeed = async (attrs: any, factory: any, db: Knex) => {
310
+ return await factory.insert('user', {
311
+ name: 'Default Admin',
312
+ role: 'admin',
313
+ });
314
+ };
315
+
316
+ const seeds = {
317
+ createAdmin: createAdminSeed,
318
+ };
319
+
320
+ factory = new ObjectionFactory(builders, seeds, trx);
321
+
322
+ const result = await factory.seed('createAdmin');
323
+
324
+ expect(result).toBeInstanceOf(User);
325
+ expect(result.name).toBe('Default Admin');
326
+ expect(result.role).toBe('admin');
327
+ });
328
+
329
+ it('should throw error for non-existent seed', () => {
330
+ factory = new ObjectionFactory({}, {}, trx);
331
+
332
+ expect(() => factory.seed('nonExistent')).toThrow(
333
+ 'Seed "nonExistent" is not registered',
334
+ );
335
+ });
336
+
337
+ it('should pass factory and db to seed function', async () => {
338
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
339
+ return User.fromJson({
340
+ name: attrs.name || 'Test User',
341
+ email: `test${Date.now()}@example.com`,
342
+ role: 'user',
343
+ createdAt: new Date(),
344
+ updatedAt: new Date(),
345
+ });
346
+ };
347
+
348
+ const builders = { user: userBuilder };
349
+
350
+ const complexSeed = async (
351
+ attrs: any,
352
+ passedFactory: any,
353
+ passedDb: Knex,
354
+ ) => {
355
+ // Verify that factory and db are passed correctly
356
+ expect(passedFactory).toBe(factory);
357
+ expect(passedDb).toBe(trx);
358
+
359
+ return await passedFactory.insert('user', {
360
+ name: `Complex ${attrs.data}`,
361
+ });
362
+ };
363
+
364
+ const seeds = {
365
+ complexSeed,
366
+ };
367
+
368
+ factory = new ObjectionFactory(builders, seeds, trx);
369
+
370
+ const result = await factory.seed('complexSeed', { data: 'test' });
371
+
372
+ expect(result).toBeInstanceOf(User);
373
+ expect(result.name).toBe('Complex test');
374
+ });
375
+ });
376
+
377
+ describe('createSeed static method', () => {
378
+ it('should return the seed function unchanged', () => {
379
+ const seedFn = async (attrs: any, factory: any, db: any) => {
380
+ return { id: 1, name: 'test' };
381
+ };
382
+
383
+ const result = ObjectionFactory.createSeed(seedFn);
384
+
385
+ expect(result).toBe(seedFn);
386
+ });
387
+ });
388
+
389
+ describe('error handling', () => {
390
+ it('should handle builder errors gracefully', async () => {
391
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
392
+ throw new Error('Builder failed');
393
+ };
394
+
395
+ const builders = {
396
+ user: userBuilder,
397
+ };
398
+
399
+ factory = new ObjectionFactory(builders, {}, trx);
400
+
401
+ await expect(factory.insert('user')).rejects.toThrow('Builder failed');
402
+ });
403
+
404
+ it('should handle invalid model data gracefully', async () => {
405
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
406
+ // Return invalid model data that will fail validation
407
+ return User.fromJson({
408
+ // Missing required fields
409
+ invalidField: 'invalid',
410
+ } as any);
411
+ };
412
+
413
+ const builders = {
414
+ user: userBuilder,
415
+ };
416
+
417
+ factory = new ObjectionFactory(builders, {}, trx);
418
+
419
+ await expect(factory.insert('user')).rejects.toThrow();
420
+ });
421
+
422
+ it('should handle seed function errors gracefully', async () => {
423
+ const failingSeed = async (attrs: any, factory: any, db: Knex) => {
424
+ throw new Error('Seed failed');
425
+ };
426
+
427
+ const seeds = {
428
+ failingSeed,
429
+ };
430
+
431
+ factory = new ObjectionFactory({}, seeds, trx);
432
+
433
+ await expect(factory.seed('failingSeed')).rejects.toThrow('Seed failed');
434
+ });
435
+ });
436
+
437
+ describe('type safety and integration', () => {
438
+ it('should work with typed builders and seeds', async () => {
439
+ interface UserInterface {
440
+ id: number;
441
+ name: string;
442
+ email: string;
443
+ }
444
+
445
+ type UserAttrs = Partial<Pick<UserInterface, 'name' | 'email'>>;
446
+
447
+ const userBuilder = async (attrs: UserAttrs, factory: any, db: Knex) => {
448
+ return User.fromJson({
449
+ name: attrs.name || 'Default User',
450
+ email: attrs.email || `user${Date.now()}@example.com`,
451
+ role: 'user',
452
+ createdAt: new Date(),
453
+ updatedAt: new Date(),
454
+ });
455
+ };
456
+
457
+ const adminSeed = async (
458
+ attrs: { isSuper?: boolean },
459
+ factory: any,
460
+ db: Knex,
461
+ ) => {
462
+ return factory.insert('user', {
463
+ name: 'Admin',
464
+ email: 'admin@example.com',
465
+ role: 'admin',
466
+ });
467
+ };
468
+
469
+ const builders = { user: userBuilder };
470
+ const seeds = { admin: adminSeed };
471
+
472
+ // This should compile without type errors
473
+ factory = new ObjectionFactory(builders, seeds, trx);
474
+
475
+ expect(factory).toBeInstanceOf(ObjectionFactory);
476
+
477
+ // Test actual functionality
478
+ const admin = await factory.seed('admin', { isSuper: true });
479
+ expect(admin).toBeInstanceOf(User);
480
+ expect(admin.name).toBe('Admin');
481
+ });
482
+
483
+ it('should handle complex builder scenarios', async () => {
484
+ const userBuilder = async (attrs: any, factory: any, db: Knex) => {
485
+ return User.fromJson({
486
+ name: attrs.name || 'Default User',
487
+ email: attrs.email || `user${Date.now()}@example.com`,
488
+ role: 'user',
489
+ createdAt: new Date(),
490
+ updatedAt: new Date(),
491
+ });
492
+ };
493
+
494
+ const postBuilder = async (attrs: any, factory: any, db: Knex) => {
495
+ // If no userId provided, create a user
496
+ if (!attrs.userId) {
497
+ const user = await factory.insert('user');
498
+ return Post.fromJson({
499
+ title: attrs.title || 'Default Post',
500
+ content: attrs.content || 'Default content',
501
+ userId: user.id,
502
+ published: attrs.published || false,
503
+ createdAt: new Date(),
504
+ updatedAt: new Date(),
505
+ });
506
+ }
507
+ return Post.fromJson({
508
+ title: attrs.title || 'Default Post',
509
+ content: attrs.content || 'Default content',
510
+ userId: attrs.userId,
511
+ published: attrs.published || false,
512
+ createdAt: new Date(),
513
+ updatedAt: new Date(),
514
+ });
515
+ };
516
+
517
+ const builders = {
518
+ user: userBuilder,
519
+ post: postBuilder,
520
+ };
521
+
522
+ factory = new ObjectionFactory(builders, {}, trx);
523
+
524
+ const post = await factory.insert('post', { title: 'Test Post' });
525
+
526
+ expect(post).toBeInstanceOf(Post);
527
+ expect(post.title).toBe('Test Post');
528
+ expect(post.userId).toBeDefined();
529
+ expect(typeof post.userId).toBe('number');
530
+ });
531
+ });
532
+ });