@geekmidas/testkit 0.0.3 → 0.0.4

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 (78) hide show
  1. package/PostgresKyselyMigrator.spec +471 -0
  2. package/dist/Factory.mjs +1 -1
  3. package/dist/{KyselyFactory-DiiWtMYe.cjs → KyselyFactory-BGvSMLtd.cjs} +11 -12
  4. package/dist/{KyselyFactory-DZewtWtJ.mjs → KyselyFactory-ionH4gvk.mjs} +12 -13
  5. package/dist/KyselyFactory.cjs +2 -1
  6. package/dist/KyselyFactory.mjs +3 -2
  7. package/dist/{ObjectionFactory-MAf2m8LI.mjs → ObjectionFactory-CFrtXe7i.mjs} +1 -1
  8. package/dist/ObjectionFactory.cjs +1 -1
  9. package/dist/ObjectionFactory.mjs +2 -2
  10. package/dist/{PostgresKyselyMigrator-ChMJpPrQ.mjs → PostgresKyselyMigrator-CbtiZgfI.mjs} +1 -1
  11. package/dist/{PostgresKyselyMigrator-rY3hO_-1.cjs → PostgresKyselyMigrator-Cxf2Dp9y.cjs} +3 -2
  12. package/dist/PostgresKyselyMigrator.cjs +2 -2
  13. package/dist/PostgresKyselyMigrator.mjs +2 -2
  14. package/dist/{PostgresMigrator-BJ2-5A_b.cjs → PostgresMigrator-eqyAFSf-.cjs} +2 -39
  15. package/dist/PostgresMigrator.cjs +1 -1
  16. package/dist/PostgresMigrator.mjs +1 -1
  17. package/dist/VitestKyselyTransactionIsolator-DXjWQtDN.mjs +12 -0
  18. package/dist/VitestKyselyTransactionIsolator-Dh2AgJDd.cjs +17 -0
  19. package/dist/VitestKyselyTransactionIsolator.cjs +5 -0
  20. package/dist/VitestKyselyTransactionIsolator.mjs +5 -0
  21. package/dist/VitestTransactionIsolator-pLwsDo_A.mjs +40 -0
  22. package/dist/VitestTransactionIsolator-zK5NJ7DQ.cjs +51 -0
  23. package/dist/VitestTransactionIsolator.cjs +5 -0
  24. package/dist/VitestTransactionIsolator.mjs +4 -0
  25. package/dist/__tests__/Factory.spec.cjs +139 -0
  26. package/dist/__tests__/Factory.spec.mjs +139 -0
  27. package/dist/__tests__/KyselyFactory.spec.cjs +221 -15008
  28. package/dist/__tests__/KyselyFactory.spec.mjs +220 -15034
  29. package/dist/__tests__/ObjectionFactory.spec.cjs +387 -0
  30. package/dist/__tests__/ObjectionFactory.spec.mjs +386 -0
  31. package/dist/__tests__/PostgresMigrator.spec.cjs +257 -0
  32. package/dist/__tests__/PostgresMigrator.spec.mjs +256 -0
  33. package/dist/__tests__/faker.spec.cjs +115 -0
  34. package/dist/__tests__/faker.spec.mjs +115 -0
  35. package/dist/__tests__/integration.spec.cjs +279 -0
  36. package/dist/__tests__/integration.spec.mjs +279 -0
  37. package/dist/chunk-DWy1uDak.cjs +39 -0
  38. package/dist/dist-BM2KvLG1.mjs +5618 -0
  39. package/dist/dist-DE3gAxQI.cjs +5736 -0
  40. package/dist/example.cjs +2 -1
  41. package/dist/example.mjs +3 -2
  42. package/dist/faker-cGCFcrj2.mjs +85 -0
  43. package/dist/faker-h6CkRloU.cjs +121 -0
  44. package/dist/faker.cjs +8 -0
  45. package/dist/faker.mjs +3 -0
  46. package/dist/helpers-BnARb5Ap.mjs +19 -0
  47. package/dist/helpers-C2NH7xcz.cjs +135 -0
  48. package/dist/helpers-C_RZk04R.cjs +31 -0
  49. package/dist/helpers-CukcFAU9.mjs +111 -0
  50. package/dist/helpers.cjs +7 -0
  51. package/dist/helpers.mjs +6 -0
  52. package/dist/kysely.cjs +16 -4
  53. package/dist/kysely.mjs +16 -5
  54. package/dist/objection.cjs +1 -1
  55. package/dist/objection.mjs +2 -2
  56. package/dist/vi.bdSIJ99Y-BgRxGeO2.mjs +9382 -0
  57. package/dist/vi.bdSIJ99Y-CFuzUeY6.cjs +9393 -0
  58. package/package.json +7 -2
  59. package/src/Factory.ts +3 -1
  60. package/src/KyselyFactory.ts +30 -36
  61. package/src/VitestKyselyTransactionIsolator.ts +23 -0
  62. package/src/VitestTransactionIsolator.ts +70 -0
  63. package/src/__tests__/Factory.spec.ts +164 -0
  64. package/src/__tests__/KyselyFactory.spec.ts +432 -64
  65. package/src/__tests__/ObjectionFactory.spec.ts +532 -0
  66. package/src/__tests__/PostgresMigrator.spec.ts +366 -0
  67. package/src/__tests__/faker.spec.ts +142 -0
  68. package/src/__tests__/integration.spec.ts +442 -0
  69. package/src/faker.ts +112 -0
  70. package/src/helpers.ts +28 -0
  71. package/src/kysely.ts +14 -0
  72. package/test/globalSetup.ts +41 -40
  73. package/test/helpers.ts +273 -0
  74. /package/dist/{Factory-DlzMkMzb.mjs → Factory-D52Lsc6Z.mjs} +0 -0
  75. /package/dist/{ObjectionFactory-DeFYWbzt.cjs → ObjectionFactory-BlkzSEqo.cjs} +0 -0
  76. /package/dist/{PostgresMigrator-BKaNTth5.mjs → PostgresMigrator-DqeuPy-e.mjs} +0 -0
  77. /package/dist/{magic-string.es-CxbtJGk_.mjs → magic-string.es-C6yzoryu.mjs} +0 -0
  78. /package/dist/{magic-string.es-KiPEzMtt.cjs → magic-string.es-jdtJrR0A.cjs} +0 -0
@@ -1,83 +1,451 @@
1
- import {
2
- CamelCasePlugin,
3
- type ControlledTransaction,
4
- Kysely,
5
- PostgresDialect,
6
- } from 'kysely';
1
+ import type { ControlledTransaction, Kysely } from 'kysely';
7
2
  import pg from 'pg';
8
- import {
9
- afterAll,
10
- afterEach,
11
- beforeAll,
12
- beforeEach,
13
- describe,
14
- expect,
15
- it,
16
- } from 'vitest';
3
+ import { describe, expect } from 'vitest';
17
4
  import { TEST_DATABASE_CONFIG } from '../../test/globalSetup';
5
+ import { type TestDatabase, createTestTables } from '../../test/helpers';
18
6
  import { KyselyFactory } from '../KyselyFactory';
7
+ import { createKyselyDb, wrapVitestKyselyTransaction } from '../helpers';
19
8
 
9
+ const db = createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
10
+ const itWithTransaction = wrapVitestKyselyTransaction<TestDatabase>(
11
+ db,
12
+ createTestTables,
13
+ );
14
+
15
+ const int8TypeId = 20;
16
+ pg.types.setTypeParser(int8TypeId, (val) => {
17
+ return parseInt(val, 10);
18
+ });
20
19
  describe('KyselyFactory', () => {
21
- interface Database {
22
- users: {
23
- id: number;
24
- name: string;
25
- email: string;
26
- createdAt: Date;
27
- };
28
- posts: {
29
- id: number;
30
- title: string;
31
- content: string;
32
- userId: number;
33
- createdAt: Date;
34
- };
35
- }
36
-
37
- let db: Kysely<Database>;
38
- let trx: ControlledTransaction<Database, []>;
39
- beforeAll(async () => {
40
- db = new Kysely({
41
- dialect: new PostgresDialect({
42
- pool: new pg.Pool(TEST_DATABASE_CONFIG),
43
- }),
44
- plugins: [new CamelCasePlugin()],
20
+ let db: Kysely<TestDatabase>;
21
+ let trx: ControlledTransaction<TestDatabase, []>;
22
+
23
+ describe('KyselyFactory.insert', () => {
24
+ itWithTransaction(
25
+ 'should insert a record with defaults',
26
+ async ({ trx }) => {
27
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
28
+ 'users',
29
+ async (attrs) => ({
30
+ name: 'John Doe',
31
+ email: `user${Date.now()}@example.com`,
32
+ createdAt: new Date(),
33
+ }),
34
+ );
35
+
36
+ const builders = {
37
+ user: userBuilder,
38
+ };
39
+
40
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
41
+ builders,
42
+ {},
43
+ trx,
44
+ );
45
+
46
+ const user = await factory.insert('user');
47
+
48
+ expect(user).toBeDefined();
49
+ expect(user.id).toBeDefined();
50
+ expect(user.name).toBe('John Doe');
51
+ expect(user.email).toContain('user');
52
+ expect(user.email).toContain('@example.com');
53
+ expect(user.createdAt).toBeInstanceOf(Date);
54
+ },
55
+ );
56
+
57
+ itWithTransaction(
58
+ 'should override defaults with provided attributes',
59
+ async ({ trx }) => {
60
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
61
+ 'users',
62
+ async (attrs) => ({
63
+ name: 'John Doe',
64
+ email: `user${Date.now()}@example.com`,
65
+ createdAt: new Date(),
66
+ }),
67
+ );
68
+
69
+ const builders = {
70
+ user: userBuilder,
71
+ };
72
+
73
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
74
+ builders,
75
+ {},
76
+ trx,
77
+ );
78
+
79
+ const customEmail = 'custom@test.com';
80
+ const customName = 'Jane Smith';
81
+ const user = await factory.insert('user', {
82
+ email: customEmail,
83
+ name: customName,
84
+ });
85
+
86
+ expect(user.name).toBe(customName);
87
+ expect(user.email).toBe(customEmail);
88
+ },
89
+ );
90
+
91
+ itWithTransaction('should handle relations', async ({ trx }) => {
92
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
93
+ 'users',
94
+ async (attrs) => ({
95
+ name: 'John Doe',
96
+ email: `user${Date.now()}@example.com`,
97
+ createdAt: new Date(),
98
+ }),
99
+ );
100
+
101
+ const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
102
+ 'posts',
103
+ async (attrs, factory) => {
104
+ // Create a user if userId not provided
105
+ if (!attrs.userId) {
106
+ const user = await factory.insert('user');
107
+ return {
108
+ title: 'Default Post',
109
+ content: 'Default content',
110
+ userId: user.id,
111
+ createdAt: new Date(),
112
+ };
113
+ }
114
+ return {
115
+ title: 'Default Post',
116
+ content: 'Default content',
117
+ createdAt: new Date(),
118
+ };
119
+ },
120
+ );
121
+
122
+ const builders = {
123
+ user: userBuilder,
124
+ post: postBuilder,
125
+ };
126
+
127
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
128
+ builders,
129
+ {},
130
+ trx,
131
+ );
132
+
133
+ const post = await factory.insert('post', {
134
+ title: 'Test Post',
135
+ });
136
+
137
+ expect(post).toBeDefined();
138
+ expect(post.title).toBe('Test Post');
139
+ expect(post.userId).toBeDefined();
140
+ expect(typeof post.userId).toBe('number');
45
141
  });
46
- });
47
142
 
48
- beforeEach(async () => {
49
- trx = await db.startTransaction().execute();
143
+ itWithTransaction(
144
+ 'should throw error for non-existent builder',
145
+ async ({ trx }) => {
146
+ const factory = new KyselyFactory<TestDatabase, any, {}>({}, {}, trx);
147
+
148
+ await expect(factory.insert('nonExistent' as any)).rejects.toThrow(
149
+ 'Factory "nonExistent" does not exist',
150
+ );
151
+ },
152
+ );
50
153
  });
51
- afterEach(async () => {
52
- await trx.rollback().execute();
154
+
155
+ describe('KyselyFactory.insertMany', () => {
156
+ itWithTransaction(
157
+ 'should insert multiple records with same attributes',
158
+ async ({ trx }) => {
159
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
160
+ 'users',
161
+ async (attrs) => ({
162
+ name: 'John Doe',
163
+ email: `user${Date.now()}-${Math.random()}@example.com`,
164
+ createdAt: new Date(),
165
+ }),
166
+ );
167
+
168
+ const builders = {
169
+ user: userBuilder,
170
+ };
171
+
172
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
173
+ builders,
174
+ {},
175
+ trx,
176
+ );
177
+
178
+ const users = await factory.insertMany(3, 'user');
179
+
180
+ expect(users).toHaveLength(3);
181
+ users.forEach((user, index) => {
182
+ expect(user.id).toBeDefined();
183
+ expect(user.name).toBe('John Doe');
184
+ expect(user.email).toContain('@example.com');
185
+ });
186
+ },
187
+ );
188
+
189
+ itWithTransaction(
190
+ 'should insert multiple records with dynamic attributes',
191
+ async ({ trx }) => {
192
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
193
+ 'users',
194
+ async (attrs) => ({
195
+ email: `user${Date.now()}-${Math.random()}@example.com`,
196
+ createdAt: new Date(),
197
+ }),
198
+ );
199
+
200
+ const builders = {
201
+ user: userBuilder,
202
+ };
203
+
204
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
205
+ builders,
206
+ {},
207
+ trx,
208
+ );
209
+
210
+ const users = await factory.insertMany(3, 'user', (idx) => ({
211
+ name: `User ${idx}`,
212
+ }));
213
+
214
+ expect(users).toHaveLength(3);
215
+ users.forEach((user, index) => {
216
+ expect(user.name).toBe(`User ${index}`);
217
+ });
218
+ },
219
+ );
220
+
221
+ itWithTransaction(
222
+ 'should throw error for non-existent builder',
223
+ async ({ trx }) => {
224
+ const factory = new KyselyFactory<TestDatabase, any, {}>({}, {}, trx);
225
+
226
+ await expect(
227
+ factory.insertMany(2, 'nonExistent' as any),
228
+ ).rejects.toThrow('Builder "nonExistent" is not registered');
229
+ },
230
+ );
53
231
  });
54
232
 
55
- afterAll(async () => {
56
- await db.destroy();
233
+ describe('KyselyFactory.createBuilder', () => {
234
+ itWithTransaction('should work with async defaults', async ({ trx }) => {
235
+ let counter = 0;
236
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
237
+ 'users',
238
+ async (attrs) => {
239
+ // Simulate async operation
240
+ await new Promise((resolve) => setTimeout(resolve, 10));
241
+ counter++;
242
+ return {
243
+ name: `Async User ${counter}`,
244
+ email: `user${counter}@example.com`,
245
+ createdAt: new Date(),
246
+ };
247
+ },
248
+ );
249
+
250
+ const builders = {
251
+ user: userBuilder,
252
+ };
253
+
254
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
255
+ builders,
256
+ {},
257
+ trx,
258
+ );
259
+
260
+ const user1 = await factory.insert('user');
261
+ const user2 = await factory.insert('user');
262
+
263
+ expect(user1.name).toBe('Async User 1');
264
+ expect(user2.name).toBe('Async User 2');
265
+ });
57
266
  });
58
- it('KyselyFactory.insert', async () => {
59
- const userBuilder = KyselyFactory.createBuilder<Database, 'users'>({
60
- table: 'users',
61
- defaults: async (attrs) => ({
62
- name: 'John Doe',
63
- }),
267
+
268
+ describe('KyselyFactory.seed', () => {
269
+ itWithTransaction('should execute seed functions', async ({ trx }) => {
270
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
271
+ 'users',
272
+ async (attrs) => ({
273
+ name: 'John Doe',
274
+ email: `user${Date.now()}@example.com`,
275
+ createdAt: new Date(),
276
+ }),
277
+ );
278
+
279
+ const builders = {
280
+ user: userBuilder,
281
+ };
282
+
283
+ const seeds = {
284
+ createAdminUser: KyselyFactory.createSeed(
285
+ async (attrs: { name?: string }, factory: any, db: any) => {
286
+ return await factory.insert('user', {
287
+ name: attrs.name || 'Admin User',
288
+ email: 'admin@example.com',
289
+ });
290
+ },
291
+ ),
292
+ };
293
+
294
+ const factory = new KyselyFactory<
295
+ TestDatabase,
296
+ typeof builders,
297
+ typeof seeds
298
+ >(builders, seeds, trx);
299
+
300
+ const adminUser = await factory.seed('createAdminUser');
301
+
302
+ expect(adminUser).toBeDefined();
303
+ expect(adminUser.name).toBe('Admin User');
304
+ expect(adminUser.email).toBe('admin@example.com');
64
305
  });
65
306
 
66
- const builders = {
67
- user: userBuilder,
68
- };
307
+ itWithTransaction(
308
+ 'should pass attributes to seed functions',
309
+ async ({ trx }) => {
310
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
311
+ 'users',
312
+ async (attrs) => ({
313
+ name: 'John Doe',
314
+ email: `user${Date.now()}@example.com`,
315
+ createdAt: new Date(),
316
+ }),
317
+ );
318
+
319
+ const builders = {
320
+ user: userBuilder,
321
+ };
69
322
 
70
- const factory = new KyselyFactory<Database, typeof builders, {}>(
71
- builders,
72
- {},
73
- trx,
323
+ const seeds = {
324
+ createCustomUser: KyselyFactory.createSeed(
325
+ async (
326
+ attrs: { name: string; email: string },
327
+ factory: any,
328
+ db: any,
329
+ ) => {
330
+ return await factory.insert('user', attrs);
331
+ },
332
+ ),
333
+ };
334
+
335
+ const factory = new KyselyFactory<
336
+ TestDatabase,
337
+ typeof builders,
338
+ typeof seeds
339
+ >(builders, seeds, trx);
340
+
341
+ const customUser = await factory.seed('createCustomUser', {
342
+ name: 'Custom User',
343
+ email: 'custom@test.com',
344
+ });
345
+
346
+ expect(customUser.name).toBe('Custom User');
347
+ expect(customUser.email).toBe('custom@test.com');
348
+ },
74
349
  );
75
350
 
76
- const user = await factory.insert('user', {
77
- email: `user${Date.now()}@example.com`,
78
- createdAt: new Date(),
79
- });
351
+ itWithTransaction(
352
+ 'should throw error for non-existent seed',
353
+ async ({ trx }) => {
354
+ const factory = new KyselyFactory<TestDatabase, any, any>({}, {}, trx);
80
355
 
81
- expect(user).toBeDefined();
356
+ expect(() => factory.seed('nonExistent' as any)).toThrow(
357
+ 'Seed "nonExistent" is not registered',
358
+ );
359
+ },
360
+ );
361
+ });
362
+
363
+ describe('Factory integration', () => {
364
+ itWithTransaction(
365
+ 'should work with controlled transactions',
366
+ async ({ trx }) => {
367
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
368
+ 'users',
369
+ async (attrs) => ({
370
+ name: 'John Doe',
371
+ email: `user${Date.now()}@example.com`,
372
+ createdAt: new Date(),
373
+ }),
374
+ );
375
+
376
+ const builders = {
377
+ user: userBuilder,
378
+ };
379
+
380
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
381
+ builders,
382
+ {},
383
+ trx,
384
+ );
385
+
386
+ const user = await factory.insert('user');
387
+
388
+ // Verify the user exists in the transaction
389
+ const foundUser = await trx
390
+ .selectFrom('users')
391
+ .selectAll()
392
+ .where('id', '=', user.id)
393
+ .executeTakeFirst();
394
+
395
+ expect(foundUser).toBeDefined();
396
+ expect(foundUser?.id).toBe(user.id);
397
+ },
398
+ );
399
+
400
+ itWithTransaction(
401
+ 'should work with factory passed to defaults',
402
+ async ({ trx }) => {
403
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
404
+ 'users',
405
+ async (attrs) => ({
406
+ name: 'John Doe',
407
+ email: `user${Date.now()}@example.com`,
408
+ createdAt: new Date(),
409
+ }),
410
+ );
411
+
412
+ const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
413
+ 'posts',
414
+ async (attrs, factory) => {
415
+ const user = await factory.insert('user');
416
+ return {
417
+ title: 'Default Post',
418
+ content: 'Default content',
419
+ userId: user.id,
420
+ createdAt: new Date(),
421
+ };
422
+ },
423
+ );
424
+
425
+ const builders = {
426
+ user: userBuilder,
427
+ post: postBuilder,
428
+ };
429
+
430
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
431
+ builders,
432
+ {},
433
+ trx,
434
+ );
435
+
436
+ const post = await factory.insert('post');
437
+
438
+ expect(post.userId).toBeDefined();
439
+
440
+ // Verify the related user exists
441
+ const relatedUser = await trx
442
+ .selectFrom('users')
443
+ .selectAll()
444
+ .where('id', '=', post.userId)
445
+ .executeTakeFirst();
446
+
447
+ expect(relatedUser).toBeDefined();
448
+ },
449
+ );
82
450
  });
83
451
  });