@geekmidas/testkit 0.0.6 → 0.0.7

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