@geekmidas/testkit 0.0.6 → 0.0.8
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.
- package/dist/Factory-WMhTNZ9S.cjs +56 -0
- package/dist/Factory-z2m01hMj.mjs +50 -0
- package/dist/Factory.cjs +1 -1
- package/dist/Factory.mjs +1 -1
- package/dist/KyselyFactory-Bdq1s1Go.cjs +215 -0
- package/dist/KyselyFactory-ELiHgHVv.mjs +210 -0
- package/dist/KyselyFactory.cjs +3 -3
- package/dist/KyselyFactory.mjs +3 -3
- package/dist/ObjectionFactory-89p-FFEw.mjs +178 -0
- package/dist/ObjectionFactory-C47B03Ot.cjs +183 -0
- package/dist/ObjectionFactory.cjs +2 -2
- package/dist/ObjectionFactory.mjs +2 -2
- package/dist/PostgresKyselyMigrator-Bs31emFd.cjs +87 -0
- package/dist/PostgresKyselyMigrator-ChIpZFYB.mjs +81 -0
- package/dist/PostgresKyselyMigrator.cjs +2 -2
- package/dist/PostgresKyselyMigrator.mjs +2 -2
- package/dist/PostgresMigrator-BtAWdLss.cjs +151 -0
- package/dist/PostgresMigrator-BzqksJcW.mjs +145 -0
- package/dist/PostgresMigrator.cjs +1 -1
- package/dist/PostgresMigrator.mjs +1 -1
- package/dist/VitestKyselyTransactionIsolator-AfxPJEwR.mjs +58 -0
- package/dist/VitestKyselyTransactionIsolator-YWnSJiIH.cjs +63 -0
- package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
- package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
- package/dist/VitestObjectionTransactionIsolator-0uX6DW5G.cjs +66 -0
- package/dist/VitestObjectionTransactionIsolator-BZRYy8iW.mjs +61 -0
- package/dist/VitestObjectionTransactionIsolator.cjs +4 -0
- package/dist/VitestObjectionTransactionIsolator.mjs +4 -0
- package/dist/VitestTransactionIsolator-DcOz0LZF.cjs +129 -0
- package/dist/VitestTransactionIsolator-kFL36T8x.mjs +117 -0
- package/dist/VitestTransactionIsolator.cjs +1 -1
- package/dist/VitestTransactionIsolator.mjs +1 -1
- package/dist/__tests__/Factory.spec.cjs +1 -1
- package/dist/__tests__/Factory.spec.mjs +1 -1
- package/dist/__tests__/KyselyFactory.spec.cjs +10 -10
- package/dist/__tests__/KyselyFactory.spec.mjs +10 -10
- package/dist/__tests__/ObjectionFactory.spec.cjs +3 -3
- package/dist/__tests__/ObjectionFactory.spec.mjs +3 -3
- package/dist/__tests__/PostgresMigrator.spec.cjs +2 -2
- package/dist/__tests__/PostgresMigrator.spec.mjs +2 -2
- package/dist/__tests__/faker.spec.cjs +1 -1
- package/dist/__tests__/faker.spec.mjs +1 -1
- package/dist/__tests__/integration.spec.cjs +10 -10
- package/dist/__tests__/integration.spec.mjs +10 -10
- package/dist/example.cjs +3 -3
- package/dist/example.mjs +3 -3
- package/dist/faker-CxKkEeYi.mjs +227 -0
- package/dist/faker-SMN4ira4.cjs +263 -0
- package/dist/faker.cjs +1 -1
- package/dist/faker.mjs +1 -1
- package/dist/helpers-CKMlwSYT.mjs +47 -0
- package/dist/helpers-H4hO5SZR.cjs +53 -0
- package/dist/helpers.cjs +1 -1
- package/dist/helpers.mjs +1 -1
- package/dist/kysely-B-GOhABm.cjs +72 -0
- package/dist/kysely-CqfoKVXs.mjs +67 -0
- package/dist/kysely.cjs +10 -8
- package/dist/kysely.mjs +9 -9
- package/dist/objection.cjs +86 -3
- package/dist/objection.mjs +83 -3
- package/package.json +2 -2
- package/src/Factory.ts +97 -0
- package/src/KyselyFactory.ts +180 -0
- package/src/ObjectionFactory.ts +145 -3
- package/src/PostgresKyselyMigrator.ts +54 -0
- package/src/PostgresMigrator.ts +90 -0
- package/src/VitestKyselyTransactionIsolator.ts +46 -0
- package/src/VitestObjectionTransactionIsolator.ts +74 -0
- package/src/VitestTransactionIsolator.ts +95 -0
- package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +144 -0
- package/src/faker.ts +158 -7
- package/src/helpers.ts +34 -0
- package/src/kysely.ts +63 -0
- package/src/objection.ts +95 -0
- package/test/helpers.ts +3 -1
- package/dist/Factory-DREHoms3.cjs +0 -15
- package/dist/Factory-DlzMkMzb.mjs +0 -9
- package/dist/KyselyFactory-BX7Kv2uP.cjs +0 -65
- package/dist/KyselyFactory-pOMOFQWE.mjs +0 -60
- package/dist/ObjectionFactory-BlkzSEqo.cjs +0 -41
- package/dist/ObjectionFactory-ChuX8sZN.mjs +0 -36
- package/dist/PostgresKyselyMigrator-D8fm35-s.mjs +0 -27
- package/dist/PostgresKyselyMigrator-JTY2LfwD.cjs +0 -33
- package/dist/PostgresMigrator-Bz-tnjB6.cjs +0 -67
- package/dist/PostgresMigrator-CEoRKTdq.mjs +0 -61
- package/dist/VitestKyselyTransactionIsolator-D-qpeVKO.mjs +0 -12
- package/dist/VitestKyselyTransactionIsolator-jF6Ohyu_.cjs +0 -17
- package/dist/VitestTransactionIsolator-BK9UsrKt.cjs +0 -53
- package/dist/VitestTransactionIsolator-e-R3p_X8.mjs +0 -41
- package/dist/faker-BwaXA_RF.mjs +0 -85
- package/dist/faker-caz-8zt8.cjs +0 -121
- package/dist/helpers-DN4sJO4i.mjs +0 -13
- package/dist/helpers-DOtYCEvZ.cjs +0 -19
- package/dist/kysely-C1-aHdnU.mjs +0 -11
- package/dist/kysely-DL3C2eM4.cjs +0 -16
- /package/dist/{helpers-B2CfbaTC.cjs → helpers-Bnm3Jy9X.cjs} +0 -0
- /package/dist/{helpers-Rf5F71r9.mjs → helpers-CukcFAU9.mjs} +0 -0
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { IsolationLevel } from "./VitestTransactionIsolator-kFL36T8x.mjs";
|
|
2
|
+
import { VitestKyselyTransactionIsolator } from "./VitestKyselyTransactionIsolator-AfxPJEwR.mjs";
|
|
3
|
+
|
|
4
|
+
//#region src/kysely.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates a wrapped Vitest test API with automatic transaction rollback for Kysely.
|
|
7
|
+
* Each test runs in an isolated database transaction that is rolled back after completion.
|
|
8
|
+
* This ensures tests don't affect each other's data and run faster than truncating tables.
|
|
9
|
+
*
|
|
10
|
+
* @template Database - The database schema type
|
|
11
|
+
* @param api - The Vitest test API (usually `test` from vitest)
|
|
12
|
+
* @param db - The Kysely database instance
|
|
13
|
+
* @param setup - Optional setup function to run before each test in the transaction
|
|
14
|
+
* @param level - Transaction isolation level (defaults to REPEATABLE_READ)
|
|
15
|
+
* @returns A wrapped test API that provides transaction isolation
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* import { test } from 'vitest';
|
|
20
|
+
* import { wrapVitestKyselyTransaction } from '@geekmidas/testkit/kysely';
|
|
21
|
+
* import { db } from './database';
|
|
22
|
+
*
|
|
23
|
+
* // Create isolated test with automatic rollback
|
|
24
|
+
* const isolatedTest = wrapVitestKyselyTransaction(test, db);
|
|
25
|
+
*
|
|
26
|
+
* // Use in tests - each test gets its own transaction
|
|
27
|
+
* isolatedTest('should create user', async ({ trx }) => {
|
|
28
|
+
* const user = await trx
|
|
29
|
+
* .insertInto('users')
|
|
30
|
+
* .values({ name: 'Test User', email: 'test@example.com' })
|
|
31
|
+
* .returningAll()
|
|
32
|
+
* .executeTakeFirst();
|
|
33
|
+
*
|
|
34
|
+
* expect(user).toBeDefined();
|
|
35
|
+
* // User is automatically rolled back after test
|
|
36
|
+
* });
|
|
37
|
+
*
|
|
38
|
+
* // With setup function for common test data
|
|
39
|
+
* const testWithSetup = wrapVitestKyselyTransaction(
|
|
40
|
+
* test,
|
|
41
|
+
* db,
|
|
42
|
+
* async (trx) => {
|
|
43
|
+
* // Create common test data
|
|
44
|
+
* await trx.insertInto('settings')
|
|
45
|
+
* .values({ key: 'test_mode', value: 'true' })
|
|
46
|
+
* .execute();
|
|
47
|
+
* }
|
|
48
|
+
* );
|
|
49
|
+
*
|
|
50
|
+
* testWithSetup('should have test settings', async ({ trx }) => {
|
|
51
|
+
* const setting = await trx
|
|
52
|
+
* .selectFrom('settings')
|
|
53
|
+
* .where('key', '=', 'test_mode')
|
|
54
|
+
* .selectAll()
|
|
55
|
+
* .executeTakeFirst();
|
|
56
|
+
*
|
|
57
|
+
* expect(setting?.value).toBe('true');
|
|
58
|
+
* });
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
function wrapVitestKyselyTransaction(api, db, setup, level = IsolationLevel.REPEATABLE_READ) {
|
|
62
|
+
const wrapper = new VitestKyselyTransactionIsolator(api);
|
|
63
|
+
return wrapper.wrapVitestWithTransaction(db, setup, level);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
//#endregion
|
|
67
|
+
export { wrapVitestKyselyTransaction };
|
package/dist/kysely.cjs
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
require('./Factory-
|
|
2
|
-
require('./faker-
|
|
3
|
-
const require_KyselyFactory = require('./KyselyFactory-
|
|
4
|
-
require('./PostgresMigrator-
|
|
5
|
-
const require_PostgresKyselyMigrator = require('./PostgresKyselyMigrator-
|
|
6
|
-
require('./VitestTransactionIsolator-
|
|
7
|
-
require('./VitestKyselyTransactionIsolator-
|
|
8
|
-
const require_kysely = require('./kysely-
|
|
1
|
+
require('./Factory-WMhTNZ9S.cjs');
|
|
2
|
+
require('./faker-SMN4ira4.cjs');
|
|
3
|
+
const require_KyselyFactory = require('./KyselyFactory-Bdq1s1Go.cjs');
|
|
4
|
+
require('./PostgresMigrator-BtAWdLss.cjs');
|
|
5
|
+
const require_PostgresKyselyMigrator = require('./PostgresKyselyMigrator-Bs31emFd.cjs');
|
|
6
|
+
const require_VitestTransactionIsolator = require('./VitestTransactionIsolator-DcOz0LZF.cjs');
|
|
7
|
+
const require_VitestKyselyTransactionIsolator = require('./VitestKyselyTransactionIsolator-YWnSJiIH.cjs');
|
|
8
|
+
const require_kysely = require('./kysely-B-GOhABm.cjs');
|
|
9
9
|
|
|
10
|
+
exports.IsolationLevel = require_VitestTransactionIsolator.IsolationLevel;
|
|
10
11
|
exports.KyselyFactory = require_KyselyFactory.KyselyFactory;
|
|
11
12
|
exports.PostgresKyselyMigrator = require_PostgresKyselyMigrator.PostgresKyselyMigrator;
|
|
13
|
+
exports.VitestKyselyTransactionIsolator = require_VitestKyselyTransactionIsolator.VitestKyselyTransactionIsolator;
|
|
12
14
|
exports.wrapVitestKyselyTransaction = require_kysely.wrapVitestKyselyTransaction;
|
package/dist/kysely.mjs
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import "./Factory-
|
|
2
|
-
import "./faker-
|
|
3
|
-
import { KyselyFactory } from "./KyselyFactory-
|
|
4
|
-
import "./PostgresMigrator-
|
|
5
|
-
import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-
|
|
6
|
-
import "./VitestTransactionIsolator-
|
|
7
|
-
import "./VitestKyselyTransactionIsolator-
|
|
8
|
-
import { wrapVitestKyselyTransaction } from "./kysely-
|
|
1
|
+
import "./Factory-z2m01hMj.mjs";
|
|
2
|
+
import "./faker-CxKkEeYi.mjs";
|
|
3
|
+
import { KyselyFactory } from "./KyselyFactory-ELiHgHVv.mjs";
|
|
4
|
+
import "./PostgresMigrator-BzqksJcW.mjs";
|
|
5
|
+
import { PostgresKyselyMigrator } from "./PostgresKyselyMigrator-ChIpZFYB.mjs";
|
|
6
|
+
import { IsolationLevel } from "./VitestTransactionIsolator-kFL36T8x.mjs";
|
|
7
|
+
import { VitestKyselyTransactionIsolator } from "./VitestKyselyTransactionIsolator-AfxPJEwR.mjs";
|
|
8
|
+
import { wrapVitestKyselyTransaction } from "./kysely-CqfoKVXs.mjs";
|
|
9
9
|
|
|
10
|
-
export { KyselyFactory, PostgresKyselyMigrator, wrapVitestKyselyTransaction };
|
|
10
|
+
export { IsolationLevel, KyselyFactory, PostgresKyselyMigrator, VitestKyselyTransactionIsolator, wrapVitestKyselyTransaction };
|
package/dist/objection.cjs
CHANGED
|
@@ -1,4 +1,87 @@
|
|
|
1
|
-
require('./Factory-
|
|
2
|
-
const require_ObjectionFactory = require('./ObjectionFactory-
|
|
1
|
+
require('./Factory-WMhTNZ9S.cjs');
|
|
2
|
+
const require_ObjectionFactory = require('./ObjectionFactory-C47B03Ot.cjs');
|
|
3
|
+
const require_VitestTransactionIsolator = require('./VitestTransactionIsolator-DcOz0LZF.cjs');
|
|
4
|
+
const require_VitestObjectionTransactionIsolator = require('./VitestObjectionTransactionIsolator-0uX6DW5G.cjs');
|
|
3
5
|
|
|
4
|
-
|
|
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 = require_VitestTransactionIsolator.IsolationLevel.REPEATABLE_READ) {
|
|
79
|
+
const wrapper = new require_VitestObjectionTransactionIsolator.VitestObjectionTransactionIsolator(api);
|
|
80
|
+
return wrapper.wrapVitestWithTransaction(knex, setup, level);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
//#endregion
|
|
84
|
+
exports.IsolationLevel = require_VitestTransactionIsolator.IsolationLevel;
|
|
85
|
+
exports.ObjectionFactory = require_ObjectionFactory.ObjectionFactory;
|
|
86
|
+
exports.VitestObjectionTransactionIsolator = require_VitestObjectionTransactionIsolator.VitestObjectionTransactionIsolator;
|
|
87
|
+
exports.wrapVitestObjectionTransaction = wrapVitestObjectionTransaction;
|
package/dist/objection.mjs
CHANGED
|
@@ -1,4 +1,84 @@
|
|
|
1
|
-
import "./Factory-
|
|
2
|
-
import { ObjectionFactory } from "./ObjectionFactory-
|
|
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
|
-
|
|
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.
|
|
3
|
+
"version": "0.0.8",
|
|
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.
|
|
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,
|
package/src/KyselyFactory.ts
CHANGED
|
@@ -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],
|