@geekmidas/testkit 0.0.5 → 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 +41 -40
  36. package/dist/__tests__/KyselyFactory.spec.mjs +42 -41
  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 +11 -8
  44. package/dist/__tests__/integration.spec.mjs +12 -9
  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 +2 -5
  54. package/dist/helpers.mjs +2 -4
  55. package/dist/kysely-B-GOhABm.cjs +72 -0
  56. package/dist/kysely-CqfoKVXs.mjs +67 -0
  57. package/dist/kysely.cjs +11 -15
  58. package/dist/kysely.mjs +9 -15
  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 +99 -2
  70. package/src/__tests__/KyselyFactory.spec.ts +4 -6
  71. package/src/__tests__/integration.spec.ts +8 -3
  72. package/src/faker.ts +158 -7
  73. package/src/helpers.ts +35 -18
  74. package/src/kysely.ts +66 -1
  75. package/src/objection.ts +95 -0
  76. package/dist/Factory-DREHoms3.cjs +0 -15
  77. package/dist/Factory-DlzMkMzb.mjs +0 -9
  78. package/dist/KyselyFactory-BX7Kv2uP.cjs +0 -65
  79. package/dist/KyselyFactory-pOMOFQWE.mjs +0 -60
  80. package/dist/ObjectionFactory-BlkzSEqo.cjs +0 -41
  81. package/dist/ObjectionFactory-ChuX8sZN.mjs +0 -36
  82. package/dist/PostgresKyselyMigrator-D8fm35-s.mjs +0 -27
  83. package/dist/PostgresKyselyMigrator-JTY2LfwD.cjs +0 -33
  84. package/dist/PostgresMigrator-Bz-tnjB6.cjs +0 -67
  85. package/dist/PostgresMigrator-CEoRKTdq.mjs +0 -61
  86. package/dist/VitestKyselyTransactionIsolator-BS3R-V0I.mjs +0 -12
  87. package/dist/VitestKyselyTransactionIsolator-DWSTKIe3.cjs +0 -17
  88. package/dist/VitestTransactionIsolator-BjVXqFs6.mjs +0 -40
  89. package/dist/VitestTransactionIsolator-Bx2c4OzK.cjs +0 -52
  90. package/dist/faker-BwaXA_RF.mjs +0 -85
  91. package/dist/faker-caz-8zt8.cjs +0 -121
  92. package/dist/helpers-B9Jdk_C7.cjs +0 -31
  93. package/dist/helpers-DOiGIkaU.mjs +0 -19
  94. /package/dist/{helpers-DKEBHABj.cjs → helpers-Bnm3Jy9X.cjs} +0 -0
  95. /package/dist/{helpers-BfuX-cjN.mjs → helpers-CukcFAU9.mjs} +0 -0
@@ -1,5 +1,19 @@
1
1
  import { Client } from 'pg';
2
2
 
3
+ /**
4
+ * Creates a PostgreSQL client connected to the 'postgres' database.
5
+ * Extracts connection details from the provided URI.
6
+ *
7
+ * @param uri - PostgreSQL connection URI
8
+ * @returns Object containing the target database name and client instance
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const { database, db } = await setupClient('postgresql://user:pass@localhost:5432/mydb');
13
+ * // database = 'mydb'
14
+ * // db = Client instance connected to 'postgres' database
15
+ * ```
16
+ */
3
17
  async function setupClient(uri: string) {
4
18
  const url = new URL(uri);
5
19
 
@@ -18,13 +32,59 @@ async function setupClient(uri: string) {
18
32
  return { database, db };
19
33
  }
20
34
 
35
+ /**
36
+ * Default logger instance for migration operations.
37
+ */
21
38
  const logger = console;
22
39
 
40
+ /**
41
+ * Abstract base class for PostgreSQL database migration utilities.
42
+ * Provides database creation, migration, and cleanup functionality for testing.
43
+ * Subclasses must implement the migrate() method to define migration logic.
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * class MyMigrator extends PostgresMigrator {
48
+ * async migrate(): Promise<void> {
49
+ * // Run your migrations here
50
+ * await this.runMigrations();
51
+ * }
52
+ * }
53
+ *
54
+ * // Use in tests
55
+ * const migrator = new MyMigrator('postgresql://localhost:5432/test_db');
56
+ * const cleanup = await migrator.start();
57
+ *
58
+ * // Run tests...
59
+ *
60
+ * // Clean up
61
+ * await cleanup();
62
+ * ```
63
+ */
23
64
  export abstract class PostgresMigrator {
65
+ /**
66
+ * Creates a new PostgresMigrator instance.
67
+ *
68
+ * @param uri - PostgreSQL connection URI
69
+ */
24
70
  constructor(private uri: string) {}
25
71
 
72
+ /**
73
+ * Abstract method to be implemented by subclasses.
74
+ * Should contain the migration logic for setting up database schema.
75
+ *
76
+ * @returns Promise that resolves when migrations are complete
77
+ */
26
78
  abstract migrate(): Promise<void>;
27
79
 
80
+ /**
81
+ * Creates a PostgreSQL database if it doesn't already exist.
82
+ * Connects to the 'postgres' database to check and create the target database.
83
+ *
84
+ * @param uri - PostgreSQL connection URI
85
+ * @returns Object indicating whether the database already existed
86
+ * @private
87
+ */
28
88
  private static async create(
29
89
  uri: string,
30
90
  ): Promise<{ alreadyExisted: boolean }> {
@@ -47,6 +107,14 @@ export abstract class PostgresMigrator {
47
107
  }
48
108
  }
49
109
 
110
+ /**
111
+ * Drops a PostgreSQL database.
112
+ * Used for cleanup after tests are complete.
113
+ *
114
+ * @param uri - PostgreSQL connection URI
115
+ * @throws Error if database cannot be dropped
116
+ * @private
117
+ */
50
118
  private static async drop(uri: string): Promise<void> {
51
119
  const { database, db } = await setupClient(uri);
52
120
  try {
@@ -57,6 +125,28 @@ export abstract class PostgresMigrator {
57
125
  }
58
126
  }
59
127
 
128
+ /**
129
+ * Starts the migration process by creating the database and running migrations.
130
+ * Returns a cleanup function that will drop the database when called.
131
+ *
132
+ * @returns Async cleanup function that drops the created database
133
+ *
134
+ * @example
135
+ * ```typescript
136
+ * const migrator = new MyMigrator('postgresql://localhost:5432/test_db');
137
+ *
138
+ * // Start migrations and get cleanup function
139
+ * const cleanup = await migrator.start();
140
+ *
141
+ * try {
142
+ * // Run your tests here
143
+ * await runTests();
144
+ * } finally {
145
+ * // Always clean up
146
+ * await cleanup();
147
+ * }
148
+ * ```
149
+ */
60
150
  async start() {
61
151
  const { database, db } = await setupClient(this.uri);
62
152
  try {
@@ -4,12 +4,58 @@ import {
4
4
  VitestPostgresTransactionIsolator,
5
5
  } from './VitestTransactionIsolator';
6
6
 
7
+ /**
8
+ * Kysely-specific implementation of the Vitest transaction isolator.
9
+ * Provides automatic transaction rollback for test isolation using Kysely's transaction API.
10
+ * Each test runs within a database transaction that is rolled back after completion,
11
+ * ensuring a clean state between tests without the overhead of recreating data.
12
+ *
13
+ * @template Database - The database schema type
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * import { VitestKyselyTransactionIsolator } from '@geekmidas/testkit';
18
+ * import { db } from './database';
19
+ *
20
+ * // Create isolator instance
21
+ * const isolator = new VitestKyselyTransactionIsolator<Database>();
22
+ *
23
+ * // In your test setup
24
+ * beforeEach(async () => {
25
+ * await isolator.start(db);
26
+ * });
27
+ *
28
+ * afterEach(async () => {
29
+ * await isolator.rollback();
30
+ * });
31
+ *
32
+ * // Tests run in isolated transactions
33
+ * it('should create user', async () => {
34
+ * const user = await db.insertInto('users')
35
+ * .values({ name: 'Test User' })
36
+ * .returningAll()
37
+ * .executeTakeFirst();
38
+ *
39
+ * expect(user).toBeDefined();
40
+ * // This data will be rolled back after the test
41
+ * });
42
+ * ```
43
+ */
7
44
  export class VitestKyselyTransactionIsolator<
8
45
  Database,
9
46
  > extends VitestPostgresTransactionIsolator<
10
47
  Kysely<Database>,
11
48
  Transaction<Database>
12
49
  > {
50
+ /**
51
+ * Creates a Kysely transaction with the specified isolation level.
52
+ * Implements the abstract transact method from VitestPostgresTransactionIsolator.
53
+ *
54
+ * @param conn - The Kysely database connection
55
+ * @param level - The transaction isolation level
56
+ * @param fn - The function to execute within the transaction
57
+ * @returns Promise that resolves when the transaction completes
58
+ */
13
59
  async transact(
14
60
  conn: Kysely<Database>,
15
61
  level: IsolationLevel,
@@ -0,0 +1,73 @@
1
+ import type { Knex } from 'knex';
2
+ import {
3
+ type IsolationLevel,
4
+ VitestPostgresTransactionIsolator,
5
+ } from './VitestTransactionIsolator';
6
+
7
+ /**
8
+ * Objection.js-specific implementation of the Vitest transaction isolator.
9
+ * Provides automatic transaction rollback for test isolation using Objection.js and Knex transaction API.
10
+ * Each test runs within a database transaction that is rolled back after completion,
11
+ * ensuring a clean state between tests without the overhead of recreating data.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { VitestObjectionTransactionIsolator } from '@geekmidas/testkit';
16
+ * import { knex } from './database';
17
+ * import { User } from './models';
18
+ * import { test } from 'vitest';
19
+ *
20
+ * // Create isolator instance
21
+ * const isolator = new VitestObjectionTransactionIsolator(test);
22
+ *
23
+ * // Use with wrapped test API
24
+ * const isolatedTest = isolator.wrapVitestWithTransaction(knex);
25
+ *
26
+ * isolatedTest('should create user', async ({ trx }) => {
27
+ * const user = await User.query(trx)
28
+ * .insert({ name: 'Test User' });
29
+ *
30
+ * expect(user).toBeDefined();
31
+ * // This data will be rolled back after the test
32
+ * });
33
+ * ```
34
+ */
35
+ export class VitestObjectionTransactionIsolator extends VitestPostgresTransactionIsolator<
36
+ Knex,
37
+ Knex.Transaction
38
+ > {
39
+ /**
40
+ * Creates a Knex transaction with the specified isolation level.
41
+ * Implements the abstract transact method from VitestPostgresTransactionIsolator.
42
+ * This transaction can be used with Objection.js models via Model.query(trx).
43
+ *
44
+ * @param conn - The Knex database connection
45
+ * @param level - The transaction isolation level
46
+ * @param fn - The function to execute within the transaction
47
+ * @returns Promise that resolves when the transaction completes
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * await isolator.transact(knex, IsolationLevel.REPEATABLE_READ, async (trx) => {
52
+ * // Use transaction with Objection models
53
+ * await User.query(trx).insert({ name: 'Test' });
54
+ * await Post.query(trx).where('userId', user.id).delete();
55
+ * });
56
+ * ```
57
+ */
58
+ async transact(
59
+ conn: Knex,
60
+ level: IsolationLevel,
61
+ fn: (trx: Knex.Transaction) => Promise<void>,
62
+ ): Promise<void> {
63
+ const isolationLevel = level.toUpperCase() as Lowercase<IsolationLevel>;
64
+ await conn.transaction(
65
+ async (trx) => {
66
+ await fn(trx);
67
+ },
68
+ {
69
+ isolationLevel,
70
+ },
71
+ );
72
+ }
73
+ }
@@ -1,32 +1,129 @@
1
- import { test as base } from 'vitest';
1
+ import type { TestAPI } from 'vitest';
2
2
 
3
+ /**
4
+ * Type definition for test fixtures that provide transaction access.
5
+ * Used with Vitest's test.extend() API to inject transactions into tests.
6
+ *
7
+ * @template Transaction - The transaction type specific to the database driver
8
+ */
3
9
  export interface DatabaseFixtures<Transaction> {
10
+ /**
11
+ * The database transaction available to the test.
12
+ * All database operations should use this transaction to ensure proper rollback.
13
+ */
4
14
  trx: Transaction;
5
15
  }
6
16
 
17
+ /**
18
+ * PostgreSQL transaction isolation levels.
19
+ * Controls the visibility of concurrent transactions.
20
+ *
21
+ * @see https://www.postgresql.org/docs/current/transaction-iso.html
22
+ */
7
23
  export enum IsolationLevel {
24
+ /**
25
+ * Lowest isolation level. Allows dirty reads.
26
+ * Not recommended for testing.
27
+ */
8
28
  READ_UNCOMMITTED = 'READ UNCOMMITTED',
29
+ /**
30
+ * Default PostgreSQL isolation level.
31
+ * Prevents dirty reads but allows non-repeatable reads.
32
+ */
9
33
  READ_COMMITTED = 'READ COMMITTED',
34
+ /**
35
+ * Prevents dirty reads and non-repeatable reads.
36
+ * Recommended for most test scenarios.
37
+ */
10
38
  REPEATABLE_READ = 'REPEATABLE READ',
39
+ /**
40
+ * Highest isolation level. Prevents all phenomena.
41
+ * May cause performance overhead in tests.
42
+ */
11
43
  SERIALIZABLE = 'SERIALIZABLE',
12
44
  }
13
45
 
46
+ /**
47
+ * Abstract base class for implementing database transaction isolation in Vitest tests.
48
+ * Provides automatic transaction rollback after each test to maintain test isolation.
49
+ * Subclasses must implement the transact() method for their specific database driver.
50
+ *
51
+ * @template Connection - The database connection type
52
+ * @template Transaction - The transaction type
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * // Implement for your database driver
57
+ * class MyDatabaseIsolator extends VitestPostgresTransactionIsolator<MyDB, MyTx> {
58
+ * async transact(conn: MyDB, level: IsolationLevel, fn: (tx: MyTx) => Promise<void>) {
59
+ * await conn.transaction(level, fn);
60
+ * }
61
+ * }
62
+ *
63
+ * // Use in tests
64
+ * const isolator = new MyDatabaseIsolator(test);
65
+ * const isolatedTest = isolator.wrapVitestWithTransaction(db);
66
+ *
67
+ * isolatedTest('should create user', async ({ trx }) => {
68
+ * await trx.insert('users', { name: 'Test' });
69
+ * // Data is automatically rolled back after test
70
+ * });
71
+ * ```
72
+ */
14
73
  export abstract class VitestPostgresTransactionIsolator<
15
74
  Connection,
16
75
  Transaction,
17
76
  > {
77
+ /**
78
+ * Abstract method to create a transaction with the specified isolation level.
79
+ * Must be implemented by subclasses for specific database drivers.
80
+ *
81
+ * @param conn - The database connection
82
+ * @param isolationLevel - The transaction isolation level
83
+ * @param fn - The function to execute within the transaction
84
+ * @returns Promise that resolves when the transaction completes
85
+ */
18
86
  abstract transact(
19
87
  conn: Connection,
20
88
  isolationLevel: IsolationLevel,
21
89
  fn: (trx: Transaction) => Promise<void>,
22
90
  ): Promise<void>;
23
91
 
92
+ /**
93
+ * Creates a new VitestPostgresTransactionIsolator instance.
94
+ *
95
+ * @param api - The Vitest test API (usually the `test` export from vitest)
96
+ */
97
+ constructor(private readonly api: TestAPI) {}
98
+
99
+ /**
100
+ * Creates a wrapped version of Vitest's test API that provides transaction isolation.
101
+ * Each test will run within a database transaction that is automatically rolled back.
102
+ *
103
+ * @param conn - The database connection to use
104
+ * @param setup - Optional setup function to run within the transaction before each test
105
+ * @param level - The transaction isolation level (defaults to REPEATABLE_READ)
106
+ * @returns A wrapped test API with transaction support
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const isolatedTest = isolator.wrapVitestWithTransaction(db, async (trx) => {
111
+ * // Optional setup: create common test data
112
+ * await trx.insert('settings', { key: 'test', value: 'true' });
113
+ * });
114
+ *
115
+ * isolatedTest('test with transaction', async ({ trx }) => {
116
+ * const user = await trx.insert('users', { name: 'Test' });
117
+ * expect(user).toBeDefined();
118
+ * });
119
+ * ```
120
+ */
24
121
  wrapVitestWithTransaction(
25
122
  conn: Connection,
26
123
  setup?: (trx: Transaction) => Promise<void>,
27
124
  level: IsolationLevel = IsolationLevel.REPEATABLE_READ,
28
125
  ) {
29
- return base.extend<DatabaseFixtures<Transaction>>({
126
+ return this.api.extend<DatabaseFixtures<Transaction>>({
30
127
  // This fixture automatically provides a transaction to each test
31
128
  trx: async ({}, use) => {
32
129
  // Create a custom error class for rollback
@@ -1,13 +1,14 @@
1
- import type { ControlledTransaction, Kysely } from 'kysely';
2
1
  import pg from 'pg';
3
- import { describe, expect } from 'vitest';
2
+ import { describe, expect, it } from 'vitest';
4
3
  import { TEST_DATABASE_CONFIG } from '../../test/globalSetup';
5
4
  import { type TestDatabase, createTestTables } from '../../test/helpers';
6
5
  import { KyselyFactory } from '../KyselyFactory';
7
- import { createKyselyDb, wrapVitestKyselyTransaction } from '../helpers';
6
+ import { createKyselyDb } from '../helpers';
7
+ import { wrapVitestKyselyTransaction } from '../kysely';
8
8
 
9
9
  const db = createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
10
10
  const itWithTransaction = wrapVitestKyselyTransaction<TestDatabase>(
11
+ it,
11
12
  db,
12
13
  createTestTables,
13
14
  );
@@ -17,9 +18,6 @@ pg.types.setTypeParser(int8TypeId, (val) => {
17
18
  return parseInt(val, 10);
18
19
  });
19
20
  describe('KyselyFactory', () => {
20
- let db: Kysely<TestDatabase>;
21
- let trx: ControlledTransaction<TestDatabase, []>;
22
-
23
21
  describe('KyselyFactory.insert', () => {
24
22
  itWithTransaction(
25
23
  'should insert a record with defaults',
@@ -1,11 +1,16 @@
1
- import { beforeAll, describe, expect } from 'vitest';
1
+ import { it as base, beforeAll, describe, expect } from 'vitest';
2
2
  import { TEST_DATABASE_CONFIG } from '../../test/globalSetup';
3
3
  import { type TestDatabase, createTestTables } from '../../test/helpers';
4
4
  import { KyselyFactory } from '../KyselyFactory';
5
- import { createKyselyDb, wrapVitestKyselyTransaction } from '../helpers';
5
+ import { createKyselyDb } from '../helpers';
6
+ import { wrapVitestKyselyTransaction } from '../kysely';
6
7
 
7
8
  const db = createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
8
- const it = wrapVitestKyselyTransaction<TestDatabase>(db, createTestTables);
9
+ const it = wrapVitestKyselyTransaction<TestDatabase>(
10
+ base,
11
+ db,
12
+ createTestTables,
13
+ );
9
14
  describe('Testkit Integration Tests', () => {
10
15
  beforeAll(async () => {});
11
16
  describe('Complex Factory Scenarios', () => {
package/src/faker.ts CHANGED
@@ -3,15 +3,39 @@ import { faker as baseFaker } from '@faker-js/faker';
3
3
  // NOTE: This is a simple way to extend `faker` with additional methods
4
4
 
5
5
  /**
6
- * Atomic counter implementation for thread-safe sequence generation
6
+ * Atomic counter implementation for thread-safe sequence generation.
7
+ * Provides a clean abstraction for generating sequential numbers in tests.
8
+ * While JavaScript is single-threaded, this class makes the intent explicit.
9
+ *
10
+ * @example
11
+ * ```typescript
12
+ * const counter = new AtomicCounter(100);
13
+ * console.log(counter.increment()); // 101
14
+ * console.log(counter.increment()); // 102
15
+ * console.log(counter.get()); // 102
16
+ * counter.reset(200);
17
+ * console.log(counter.increment()); // 201
18
+ * ```
7
19
  */
8
20
  class AtomicCounter {
21
+ /**
22
+ * The current counter value.
23
+ * @private
24
+ */
9
25
  private value: number;
10
26
 
27
+ /**
28
+ * Creates a new atomic counter.
29
+ * @param initialValue - The starting value (default: 0)
30
+ */
11
31
  constructor(initialValue = 0) {
12
32
  this.value = initialValue;
13
33
  }
14
34
 
35
+ /**
36
+ * Increments the counter and returns the new value.
37
+ * @returns The incremented value
38
+ */
15
39
  increment(): number {
16
40
  // In Node.js, JavaScript is single-threaded within the event loop,
17
41
  // so this operation is already atomic. However, this class provides
@@ -19,17 +43,42 @@ class AtomicCounter {
19
43
  return ++this.value;
20
44
  }
21
45
 
46
+ /**
47
+ * Gets the current counter value without incrementing.
48
+ * @returns The current value
49
+ */
22
50
  get(): number {
23
51
  return this.value;
24
52
  }
25
53
 
54
+ /**
55
+ * Resets the counter to a specific value.
56
+ * @param value - The new value (default: 0)
57
+ */
26
58
  reset(value = 0): void {
27
59
  this.value = value;
28
60
  }
29
61
  }
30
62
 
31
63
  /**
32
- * Sets the `insertedAt` and `updatedAt` to a random date in the past.
64
+ * Generates random timestamp fields for database records.
65
+ * Creates a createdAt date in the past and an updatedAt date between creation and now.
66
+ * Milliseconds are set to 0 for cleaner database storage.
67
+ *
68
+ * @returns Object with createdAt and updatedAt Date fields
69
+ *
70
+ * @example
71
+ * ```typescript
72
+ * const { createdAt, updatedAt } = timestamps();
73
+ * console.log(createdAt); // 2023-05-15T10:30:00.000Z
74
+ * console.log(updatedAt); // 2023-11-20T14:45:00.000Z
75
+ *
76
+ * // Use in factory
77
+ * const user = {
78
+ * name: 'John Doe',
79
+ * ...timestamps()
80
+ * };
81
+ * ```
33
82
  */
34
83
  export function timestamps(): Timestamps {
35
84
  const createdAt = faker.date.past();
@@ -45,7 +94,18 @@ export function timestamps(): Timestamps {
45
94
  }
46
95
 
47
96
  /**
48
- * Returns a reverse domain name identifier.
97
+ * Generates a reverse domain name identifier.
98
+ * Useful for creating unique identifiers that follow domain naming conventions.
99
+ *
100
+ * @param suffix - Optional suffix to append to the identifier
101
+ * @returns A reverse domain name string (e.g., "com.example.feature123")
102
+ *
103
+ * @example
104
+ * ```typescript
105
+ * console.log(identifier()); // "com.example.widget1"
106
+ * console.log(identifier('user')); // "org.acme.user"
107
+ * console.log(identifier('api')); // "net.demo.api"
108
+ * ```
49
109
  */
50
110
  export function identifier(suffix?: string): string {
51
111
  return [
@@ -55,9 +115,33 @@ export function identifier(suffix?: string): string {
55
115
  ].join('.');
56
116
  }
57
117
 
58
- // Atomic sequences for thread-safe counter generation
118
+ /**
119
+ * Storage for named sequence counters.
120
+ * Each sequence maintains its own independent counter.
121
+ * @private
122
+ */
59
123
  const sequences = new Map<string, AtomicCounter>();
60
124
 
125
+ /**
126
+ * Generates sequential numbers for a named sequence.
127
+ * Useful for creating unique IDs or numbered test data.
128
+ * Each named sequence maintains its own counter.
129
+ *
130
+ * @param name - The sequence name (default: 'default')
131
+ * @returns The next number in the sequence
132
+ *
133
+ * @example
134
+ * ```typescript
135
+ * console.log(sequence()); // 1
136
+ * console.log(sequence()); // 2
137
+ * console.log(sequence('user')); // 1
138
+ * console.log(sequence('user')); // 2
139
+ * console.log(sequence()); // 3
140
+ *
141
+ * // Use in factories
142
+ * const email = `user${sequence('email')}@example.com`;
143
+ * ```
144
+ */
61
145
  export function sequence(name = 'default'): number {
62
146
  if (!sequences.has(name)) {
63
147
  sequences.set(name, new AtomicCounter());
@@ -68,7 +152,22 @@ export function sequence(name = 'default'): number {
68
152
  }
69
153
 
70
154
  /**
71
- * Resets a sequence counter to a specific value (default: 0)
155
+ * Resets a named sequence counter to a specific value.
156
+ * Useful for resetting sequences between test suites.
157
+ *
158
+ * @param name - The sequence name to reset (default: 'default')
159
+ * @param value - The new starting value (default: 0)
160
+ *
161
+ * @example
162
+ * ```typescript
163
+ * sequence('user'); // 1
164
+ * sequence('user'); // 2
165
+ * resetSequence('user');
166
+ * sequence('user'); // 1
167
+ *
168
+ * resetSequence('order', 1000);
169
+ * sequence('order'); // 1001
170
+ * ```
72
171
  */
73
172
  export function resetSequence(name = 'default', value = 0): void {
74
173
  if (sequences.has(name)) {
@@ -80,19 +179,61 @@ export function resetSequence(name = 'default', value = 0): void {
80
179
  }
81
180
 
82
181
  /**
83
- * Resets all sequence counters
182
+ * Resets all sequence counters.
183
+ * Useful for cleaning up between test suites to ensure predictable sequences.
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * // In test setup
188
+ * beforeEach(() => {
189
+ * resetAllSequences();
190
+ * });
191
+ *
192
+ * it('starts sequences from 1', () => {
193
+ * expect(sequence()).toBe(1);
194
+ * expect(sequence('user')).toBe(1);
195
+ * });
196
+ * ```
84
197
  */
85
198
  export function resetAllSequences(): void {
86
199
  sequences.clear();
87
200
  }
88
201
 
89
202
  /**
90
- * Returns a random price number.
203
+ * Generates a random price as a number.
204
+ * Converts faker's string price to a numeric value.
205
+ *
206
+ * @returns A random price number
207
+ *
208
+ * @example
209
+ * ```typescript
210
+ * const productPrice = price(); // 29.99
211
+ * const total = price() * quantity; // Numeric calculation
212
+ * ```
91
213
  */
92
214
  function price(): number {
93
215
  return +faker.commerce.price();
94
216
  }
95
217
 
218
+ /**
219
+ * Enhanced faker instance with additional utility methods for testing.
220
+ * Extends @faker-js/faker with custom methods for common test data generation patterns.
221
+ *
222
+ * @example
223
+ * ```typescript
224
+ * import { faker } from '@geekmidas/testkit';
225
+ *
226
+ * // Use standard faker methods
227
+ * const name = faker.person.fullName();
228
+ * const email = faker.internet.email();
229
+ *
230
+ * // Use custom extensions
231
+ * const { createdAt, updatedAt } = faker.timestamps();
232
+ * const id = faker.identifier('user');
233
+ * const orderNumber = faker.sequence('order');
234
+ * const productPrice = faker.price();
235
+ * ```
236
+ */
96
237
  export const faker = Object.freeze(
97
238
  Object.assign({}, baseFaker, {
98
239
  timestamps,
@@ -104,9 +245,19 @@ export const faker = Object.freeze(
104
245
  }),
105
246
  );
106
247
 
248
+ /**
249
+ * Type definition for timestamp fields.
250
+ * Used by the timestamps() function to generate date fields.
251
+ */
107
252
  export type Timestamps = {
253
+ /** The creation date */
108
254
  createdAt: Date;
255
+ /** The last update date */
109
256
  updatedAt: Date;
110
257
  };
111
258
 
259
+ /**
260
+ * Type definition for the enhanced faker factory.
261
+ * Includes all standard faker methods plus custom extensions.
262
+ */
112
263
  export type FakerFactory = typeof faker;