@geekmidas/testkit 0.1.1 → 0.3.0
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-z2m01hMj.mjs → Factory-BFVnMMCC.mjs} +2 -2
- package/dist/Factory-BFVnMMCC.mjs.map +1 -0
- package/dist/{Factory-DE3hE0WO.d.mts → Factory-BcGJjLc8.d.mts} +24 -10
- package/dist/{Factory-WMhTNZ9S.cjs → Factory-BhjUOBWN.cjs} +2 -2
- package/dist/Factory-BhjUOBWN.cjs.map +1 -0
- package/dist/{Factory-pNV7ZQ7-.d.cts → Factory-IdfEkOeT.d.cts} +24 -10
- package/dist/Factory.cjs +1 -1
- package/dist/Factory.d.cts +3 -3
- package/dist/Factory.d.mts +3 -3
- package/dist/Factory.mjs +1 -1
- package/dist/{KyselyFactory-DaaCykWP.cjs → KyselyFactory-BFqVIn_0.cjs} +21 -4
- package/dist/KyselyFactory-BFqVIn_0.cjs.map +1 -0
- package/dist/{KyselyFactory-CPZTUuMB.d.cts → KyselyFactory-Cj-EultY.d.mts} +18 -5
- package/dist/{KyselyFactory-CXY5gJk2.mjs → KyselyFactory-DMswpwji.mjs} +21 -4
- package/dist/KyselyFactory-DMswpwji.mjs.map +1 -0
- package/dist/{KyselyFactory-RAyvZ8Cj.d.mts → KyselyFactory-Dxhi_ZkA.d.cts} +18 -5
- package/dist/KyselyFactory.cjs +2 -2
- package/dist/KyselyFactory.d.cts +3 -3
- package/dist/KyselyFactory.d.mts +3 -3
- package/dist/KyselyFactory.mjs +2 -2
- package/dist/{ObjectionFactory-BlBicEia.d.mts → ObjectionFactory-BRAkAByP.d.cts} +18 -5
- package/dist/{ObjectionFactory-Eb04AOnv.cjs → ObjectionFactory-BeFBYcan.cjs} +21 -4
- package/dist/ObjectionFactory-BeFBYcan.cjs.map +1 -0
- package/dist/{ObjectionFactory-BwpN4gMX.d.cts → ObjectionFactory-DL4qkuF1.d.mts} +18 -5
- package/dist/{ObjectionFactory-zf2fLKrL.mjs → ObjectionFactory-QCJ7u0Ql.mjs} +21 -4
- package/dist/ObjectionFactory-QCJ7u0Ql.mjs.map +1 -0
- package/dist/ObjectionFactory.cjs +2 -2
- package/dist/ObjectionFactory.d.cts +3 -3
- package/dist/ObjectionFactory.d.mts +3 -3
- package/dist/ObjectionFactory.mjs +2 -2
- package/dist/{VitestKyselyTransactionIsolator-XDL3ngs_.mjs → VitestKyselyTransactionIsolator-CNURW8y6.mjs} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-XDL3ngs_.mjs.map → VitestKyselyTransactionIsolator-CNURW8y6.mjs.map} +1 -1
- package/dist/{VitestKyselyTransactionIsolator-DiskaURs.d.mts → VitestKyselyTransactionIsolator-D3EZZhjZ.d.cts} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-BqrZDeaT.d.cts → VitestKyselyTransactionIsolator-Dxlp1u0f.d.mts} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-DX_VPKS-.cjs → VitestKyselyTransactionIsolator-EvDLk5zg.cjs} +2 -2
- package/dist/{VitestKyselyTransactionIsolator-DX_VPKS-.cjs.map → VitestKyselyTransactionIsolator-EvDLk5zg.cjs.map} +1 -1
- package/dist/VitestKyselyTransactionIsolator.cjs +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
- package/dist/VitestKyselyTransactionIsolator.mjs +2 -2
- package/dist/{VitestObjectionTransactionIsolator-CD2ucJpH.d.cts → VitestObjectionTransactionIsolator-1TpsPqfG.d.cts} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-D_tlOtq8.cjs → VitestObjectionTransactionIsolator-CM5KTAFA.cjs} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-D_tlOtq8.cjs.map → VitestObjectionTransactionIsolator-CM5KTAFA.cjs.map} +1 -1
- package/dist/{VitestObjectionTransactionIsolator-DhQ8XGva.d.mts → VitestObjectionTransactionIsolator-i9jIgU8Q.d.mts} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-_EhJKu_O.mjs → VitestObjectionTransactionIsolator-jQFaCz0u.mjs} +2 -2
- package/dist/{VitestObjectionTransactionIsolator-_EhJKu_O.mjs.map → VitestObjectionTransactionIsolator-jQFaCz0u.mjs.map} +1 -1
- package/dist/VitestObjectionTransactionIsolator.cjs +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
- package/dist/VitestObjectionTransactionIsolator.mjs +2 -2
- package/dist/{VitestTransactionIsolator-DLdQlfZ5.d.mts → VitestTransactionIsolator-BvR19bYn.d.mts} +31 -14
- package/dist/{VitestTransactionIsolator-BKIrj3Uy.cjs → VitestTransactionIsolator-CMfJXZP8.cjs} +52 -36
- package/dist/VitestTransactionIsolator-CMfJXZP8.cjs.map +1 -0
- package/dist/{VitestTransactionIsolator-DfA80g2M.d.cts → VitestTransactionIsolator-CwQaxZLP.d.cts} +31 -14
- package/dist/{VitestTransactionIsolator-BIaMs4c2.mjs → VitestTransactionIsolator-DQ7tLqgV.mjs} +52 -36
- package/dist/VitestTransactionIsolator-DQ7tLqgV.mjs.map +1 -0
- package/dist/VitestTransactionIsolator.cjs +1 -1
- package/dist/VitestTransactionIsolator.d.cts +2 -2
- package/dist/VitestTransactionIsolator.d.mts +2 -2
- package/dist/VitestTransactionIsolator.mjs +1 -1
- package/dist/benchmark.cjs +49 -0
- package/dist/benchmark.cjs.map +1 -0
- package/dist/benchmark.d.cts +35 -0
- package/dist/benchmark.d.mts +35 -0
- package/dist/benchmark.mjs +45 -0
- package/dist/benchmark.mjs.map +1 -0
- package/dist/better-auth.d.cts +2 -2
- package/dist/better-auth.d.mts +2 -2
- package/dist/{directory-Q178x53k.d.mts → directory-BXavAeJZ.d.mts} +3 -3
- package/dist/{faker-BSH1EMtg.d.cts → faker-CbYiF-8_.d.cts} +3 -3
- package/dist/{faker-D9gz7KjY.d.mts → faker-DHh7xs4u.d.mts} +3 -3
- package/dist/faker.d.cts +1 -1
- package/dist/faker.d.mts +1 -1
- package/dist/kysely.cjs +25 -31
- package/dist/kysely.cjs.map +1 -1
- package/dist/kysely.d.cts +39 -34
- package/dist/kysely.d.mts +39 -34
- package/dist/kysely.mjs +25 -31
- package/dist/kysely.mjs.map +1 -1
- package/dist/objection.cjs +25 -49
- package/dist/objection.cjs.map +1 -1
- package/dist/objection.d.cts +39 -52
- package/dist/objection.d.mts +39 -52
- package/dist/objection.mjs +25 -49
- package/dist/objection.mjs.map +1 -1
- package/dist/os/directory.d.mts +1 -1
- package/dist/os/index.d.mts +1 -1
- package/package.json +6 -1
- package/src/Factory.ts +25 -11
- package/src/KyselyFactory.ts +21 -4
- package/src/ObjectionFactory.ts +21 -4
- package/src/VitestTransactionIsolator.ts +67 -16
- package/src/__tests__/Factory.spec.ts +16 -10
- package/src/__tests__/KyselyFactory.spec.ts +16 -11
- package/src/__tests__/ObjectionFactory.spec.ts +61 -53
- package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +6 -3
- package/src/__tests__/integration.spec.ts +24 -18
- package/src/benchmark.ts +48 -0
- package/src/kysely.ts +44 -33
- package/src/objection.ts +42 -51
- package/dist/Factory-WMhTNZ9S.cjs.map +0 -1
- package/dist/Factory-z2m01hMj.mjs.map +0 -1
- package/dist/KyselyFactory-CXY5gJk2.mjs.map +0 -1
- package/dist/KyselyFactory-DaaCykWP.cjs.map +0 -1
- package/dist/ObjectionFactory-Eb04AOnv.cjs.map +0 -1
- package/dist/ObjectionFactory-zf2fLKrL.mjs.map +0 -1
- package/dist/VitestTransactionIsolator-BIaMs4c2.mjs.map +0 -1
- package/dist/VitestTransactionIsolator-BKIrj3Uy.cjs.map +0 -1
package/src/Factory.ts
CHANGED
|
@@ -39,7 +39,7 @@ export abstract class Factory<
|
|
|
39
39
|
*
|
|
40
40
|
* @example
|
|
41
41
|
* ```typescript
|
|
42
|
-
* const userWithPostsSeed = Factory.createSeed(async (attrs, factory, db) => {
|
|
42
|
+
* const userWithPostsSeed = Factory.createSeed(async ({ attrs, factory, db }) => {
|
|
43
43
|
* const user = await factory.insert('user', attrs);
|
|
44
44
|
* return user;
|
|
45
45
|
* });
|
|
@@ -86,7 +86,7 @@ export abstract class Factory<
|
|
|
86
86
|
*/
|
|
87
87
|
abstract seed<K extends keyof Seeds>(
|
|
88
88
|
seedName: K,
|
|
89
|
-
attrs?:
|
|
89
|
+
attrs?: ExtractSeedAttrs<Seeds[K]>,
|
|
90
90
|
): ReturnType<Seeds[K]>;
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -133,15 +133,16 @@ export type MixedFactoryBuilder<
|
|
|
133
133
|
* @template Result - The type of object returned by the seed
|
|
134
134
|
* @template DB - The database connection type (Kysely, Knex, etc.)
|
|
135
135
|
*
|
|
136
|
-
* @param
|
|
137
|
-
* @param
|
|
138
|
-
* @param
|
|
136
|
+
* @param context - Object containing attrs, factory, and db
|
|
137
|
+
* @param context.attrs - Configuration attributes for the seed
|
|
138
|
+
* @param context.factory - The factory instance for creating records
|
|
139
|
+
* @param context.db - The database connection
|
|
139
140
|
* @returns A promise resolving to the seed result
|
|
140
141
|
*
|
|
141
142
|
* @example
|
|
142
143
|
* ```typescript
|
|
143
144
|
* const userWithPostsSeed: FactorySeed<{ postCount?: number }, Factory, User, DB> =
|
|
144
|
-
* async (attrs, factory, db) => {
|
|
145
|
+
* async ({ attrs, factory, db }) => {
|
|
145
146
|
* const user = await factory.insert('user', attrs);
|
|
146
147
|
* const postCount = attrs.postCount || 3;
|
|
147
148
|
*
|
|
@@ -153,8 +154,21 @@ export type MixedFactoryBuilder<
|
|
|
153
154
|
* };
|
|
154
155
|
* ```
|
|
155
156
|
*/
|
|
156
|
-
export type FactorySeed<
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
157
|
+
export type FactorySeed<
|
|
158
|
+
Attrs = any,
|
|
159
|
+
Factory = any,
|
|
160
|
+
Result = any,
|
|
161
|
+
DB = any,
|
|
162
|
+
> = (context: { attrs: Attrs; factory: Factory; db: DB }) => Promise<Result>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Helper type to extract the Attrs type from a FactorySeed function.
|
|
166
|
+
* Used internally by Factory implementations to correctly type the seed method parameters.
|
|
167
|
+
*/
|
|
168
|
+
export type ExtractSeedAttrs<T> = T extends (context: {
|
|
169
|
+
attrs: infer A;
|
|
170
|
+
factory: any;
|
|
171
|
+
db: any;
|
|
172
|
+
}) => any
|
|
173
|
+
? A
|
|
174
|
+
: never;
|
package/src/KyselyFactory.ts
CHANGED
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
Kysely,
|
|
5
5
|
Selectable,
|
|
6
6
|
} from 'kysely';
|
|
7
|
-
import { Factory, type FactorySeed } from './Factory.ts';
|
|
7
|
+
import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory.ts';
|
|
8
8
|
import { type FakerFactory, faker } from './faker.ts';
|
|
9
9
|
|
|
10
10
|
/**
|
|
@@ -55,8 +55,21 @@ export class KyselyFactory<
|
|
|
55
55
|
* Inherits from the base Factory class implementation.
|
|
56
56
|
*
|
|
57
57
|
* @template Seed - The seed function type
|
|
58
|
-
* @param seedFn - The seed function to wrap
|
|
58
|
+
* @param seedFn - The seed function to wrap (receives { attrs, factory, db } object)
|
|
59
59
|
* @returns The same seed function with proper typing
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* const seeds = {
|
|
64
|
+
* userWithPosts: KyselyFactory.createSeed(
|
|
65
|
+
* async ({ attrs, factory }) => {
|
|
66
|
+
* const user = await factory.insert('user', attrs);
|
|
67
|
+
* await factory.insertMany(3, 'post', { userId: user.id });
|
|
68
|
+
* return user;
|
|
69
|
+
* },
|
|
70
|
+
* ),
|
|
71
|
+
* };
|
|
72
|
+
* ```
|
|
60
73
|
*/
|
|
61
74
|
static createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
|
|
62
75
|
return Factory.createSeed(seedFn);
|
|
@@ -359,7 +372,7 @@ export class KyselyFactory<
|
|
|
359
372
|
*/
|
|
360
373
|
seed<K extends keyof Seeds>(
|
|
361
374
|
seedName: K,
|
|
362
|
-
attrs?:
|
|
375
|
+
attrs?: ExtractSeedAttrs<Seeds[K]>,
|
|
363
376
|
): ReturnType<Seeds[K]> {
|
|
364
377
|
if (!(seedName in this.seeds)) {
|
|
365
378
|
throw new Error(
|
|
@@ -369,6 +382,10 @@ export class KyselyFactory<
|
|
|
369
382
|
);
|
|
370
383
|
}
|
|
371
384
|
|
|
372
|
-
return this.seeds[seedName](
|
|
385
|
+
return this.seeds[seedName]({
|
|
386
|
+
attrs: attrs || {},
|
|
387
|
+
factory: this,
|
|
388
|
+
db: this.db,
|
|
389
|
+
});
|
|
373
390
|
}
|
|
374
391
|
}
|
package/src/ObjectionFactory.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import type { Model } from 'objection';
|
|
3
|
-
import { Factory, type FactorySeed } from './Factory.ts';
|
|
3
|
+
import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory.ts';
|
|
4
4
|
import { type FakerFactory, faker } from './faker.ts';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -48,8 +48,21 @@ export class ObjectionFactory<
|
|
|
48
48
|
* Inherits from the base Factory class implementation.
|
|
49
49
|
*
|
|
50
50
|
* @template Seed - The seed function type
|
|
51
|
-
* @param seedFn - The seed function to wrap
|
|
51
|
+
* @param seedFn - The seed function to wrap (receives { attrs, factory, db } object)
|
|
52
52
|
* @returns The same seed function with proper typing
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const seeds = {
|
|
57
|
+
* userWithPosts: ObjectionFactory.createSeed(
|
|
58
|
+
* async ({ attrs, factory }) => {
|
|
59
|
+
* const user = await factory.insert('user', attrs);
|
|
60
|
+
* await factory.insertMany(3, 'post', { userId: user.id });
|
|
61
|
+
* return user;
|
|
62
|
+
* },
|
|
63
|
+
* ),
|
|
64
|
+
* };
|
|
65
|
+
* ```
|
|
53
66
|
*/
|
|
54
67
|
static createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
|
|
55
68
|
return Factory.createSeed(seedFn);
|
|
@@ -381,7 +394,7 @@ export class ObjectionFactory<
|
|
|
381
394
|
*/
|
|
382
395
|
seed<K extends keyof Seeds>(
|
|
383
396
|
seedName: K,
|
|
384
|
-
attrs?:
|
|
397
|
+
attrs?: ExtractSeedAttrs<Seeds[K]>,
|
|
385
398
|
): ReturnType<Seeds[K]> {
|
|
386
399
|
if (!(seedName in this.seeds)) {
|
|
387
400
|
throw new Error(
|
|
@@ -391,6 +404,10 @@ export class ObjectionFactory<
|
|
|
391
404
|
);
|
|
392
405
|
}
|
|
393
406
|
|
|
394
|
-
return this.seeds[seedName](
|
|
407
|
+
return this.seeds[seedName]({
|
|
408
|
+
attrs: attrs || {},
|
|
409
|
+
factory: this,
|
|
410
|
+
db: this.db,
|
|
411
|
+
});
|
|
395
412
|
}
|
|
396
413
|
}
|
|
@@ -124,30 +124,59 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
124
124
|
* Creates a wrapped version of Vitest's test API that provides transaction isolation.
|
|
125
125
|
* Each test will run within a database transaction that is automatically rolled back.
|
|
126
126
|
*
|
|
127
|
-
* @param
|
|
128
|
-
* @param setup - Optional setup function to run within the transaction before each test
|
|
129
|
-
* @param level - The transaction isolation level (defaults to REPEATABLE_READ)
|
|
127
|
+
* @param options - Configuration options for transaction wrapping
|
|
130
128
|
* @returns A wrapped test API with transaction support
|
|
131
129
|
*
|
|
132
130
|
* @example
|
|
133
131
|
* ```typescript
|
|
134
|
-
* const isolatedTest = isolator.wrapVitestWithTransaction(
|
|
135
|
-
*
|
|
136
|
-
*
|
|
132
|
+
* const isolatedTest = isolator.wrapVitestWithTransaction({
|
|
133
|
+
* connection: db,
|
|
134
|
+
* setup: async (trx) => {
|
|
135
|
+
* await trx.insert('settings', { key: 'test', value: 'true' });
|
|
136
|
+
* },
|
|
137
|
+
* fixtures: {
|
|
138
|
+
* factory: (trx) => new Factory(trx),
|
|
139
|
+
* },
|
|
137
140
|
* });
|
|
138
141
|
*
|
|
139
|
-
* isolatedTest('test with transaction', async ({ trx }) => {
|
|
140
|
-
* const user = await
|
|
142
|
+
* isolatedTest('test with transaction', async ({ trx, factory }) => {
|
|
143
|
+
* const user = await factory.insert('user', { name: 'Test' });
|
|
141
144
|
* expect(user).toBeDefined();
|
|
142
145
|
* });
|
|
143
146
|
* ```
|
|
144
147
|
*/
|
|
145
|
-
wrapVitestWithTransaction(
|
|
146
|
-
|
|
147
|
-
setup?: (trx: Transaction) => Promise<void>,
|
|
148
|
-
level: IsolationLevel = IsolationLevel.REPEATABLE_READ,
|
|
148
|
+
wrapVitestWithTransaction<Extended extends Record<string, unknown> = {}>(
|
|
149
|
+
options: TransactionWrapperOptions<TConn, Transaction, Extended>,
|
|
149
150
|
) {
|
|
150
|
-
|
|
151
|
+
const {
|
|
152
|
+
connection,
|
|
153
|
+
setup,
|
|
154
|
+
isolationLevel = IsolationLevel.REPEATABLE_READ,
|
|
155
|
+
fixtures,
|
|
156
|
+
} = options;
|
|
157
|
+
|
|
158
|
+
// Build fixture definitions for additional fixtures that depend on trx
|
|
159
|
+
const additionalFixtures: Record<string, unknown> = {};
|
|
160
|
+
if (fixtures) {
|
|
161
|
+
for (const [key, creator] of Object.entries(fixtures)) {
|
|
162
|
+
additionalFixtures[key] = async (
|
|
163
|
+
{ trx }: { trx: Transaction },
|
|
164
|
+
use: (value: unknown) => Promise<void>,
|
|
165
|
+
) => {
|
|
166
|
+
const value = await (creator as (trx: Transaction) => unknown)(trx);
|
|
167
|
+
await use(value);
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
type CombinedFixtures = DatabaseFixtures<Transaction> & Extended;
|
|
173
|
+
|
|
174
|
+
// Cast to bypass Vitest's strict fixture typing which can't infer
|
|
175
|
+
// dynamically built fixture objects
|
|
176
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
177
|
+
const extendFn = this.api.extend as <T>(fixtures: any) => TestAPI<T>;
|
|
178
|
+
|
|
179
|
+
return extendFn<CombinedFixtures>({
|
|
151
180
|
// This fixture automatically provides a transaction to each test
|
|
152
181
|
trx: async ({}, use: (value: Transaction) => Promise<void>) => {
|
|
153
182
|
// Create a custom error class for rollback
|
|
@@ -159,9 +188,9 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
159
188
|
}
|
|
160
189
|
|
|
161
190
|
let testError: Error | undefined;
|
|
162
|
-
const conn = await
|
|
191
|
+
const conn = await connection();
|
|
163
192
|
try {
|
|
164
|
-
await this.transact(conn,
|
|
193
|
+
await this.transact(conn, isolationLevel, async (transaction) => {
|
|
165
194
|
try {
|
|
166
195
|
// Provide the transaction to the test
|
|
167
196
|
await setup?.(transaction);
|
|
@@ -188,6 +217,7 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
188
217
|
await this.destroy(conn);
|
|
189
218
|
}
|
|
190
219
|
},
|
|
220
|
+
...additionalFixtures,
|
|
191
221
|
});
|
|
192
222
|
}
|
|
193
223
|
}
|
|
@@ -195,6 +225,24 @@ export abstract class VitestPostgresTransactionIsolator<TConn, Transaction> {
|
|
|
195
225
|
export type DatabaseConnectionFn<Conn> = () => Conn | Promise<Conn>;
|
|
196
226
|
export type DatabaseConnection<Conn> = DatabaseConnectionFn<Conn>;
|
|
197
227
|
|
|
228
|
+
/**
|
|
229
|
+
* Options for wrapping Vitest tests with database transaction isolation.
|
|
230
|
+
*/
|
|
231
|
+
export interface TransactionWrapperOptions<
|
|
232
|
+
TConn,
|
|
233
|
+
Transaction,
|
|
234
|
+
Extended extends Record<string, unknown> = {},
|
|
235
|
+
> {
|
|
236
|
+
/** Function that creates or returns a database connection */
|
|
237
|
+
connection: DatabaseConnection<TConn>;
|
|
238
|
+
/** Optional setup function to run within the transaction before each test */
|
|
239
|
+
setup?: (trx: Transaction) => Promise<void>;
|
|
240
|
+
/** Transaction isolation level (defaults to REPEATABLE_READ) */
|
|
241
|
+
isolationLevel?: IsolationLevel;
|
|
242
|
+
/** Additional fixtures that depend on the transaction */
|
|
243
|
+
fixtures?: FixtureCreators<Transaction, Extended>;
|
|
244
|
+
}
|
|
245
|
+
|
|
198
246
|
/**
|
|
199
247
|
* Type for fixture creator functions that depend on the transaction.
|
|
200
248
|
* Each function receives the transaction and returns the fixture value.
|
|
@@ -253,7 +301,10 @@ export type TestWithExtendedFixtures<
|
|
|
253
301
|
* import { wrapVitestKyselyTransaction, extendWithFixtures } from '@geekmidas/testkit/kysely';
|
|
254
302
|
*
|
|
255
303
|
* // Create base wrapped test
|
|
256
|
-
* const baseTest = wrapVitestKyselyTransaction(test,
|
|
304
|
+
* const baseTest = wrapVitestKyselyTransaction(test, {
|
|
305
|
+
* connection: db,
|
|
306
|
+
* setup: createTestTables,
|
|
307
|
+
* });
|
|
257
308
|
*
|
|
258
309
|
* // Extend with fixtures
|
|
259
310
|
* const it = extendWithFixtures(baseTest, {
|
|
@@ -46,7 +46,11 @@ describe('Factory', () => {
|
|
|
46
46
|
|
|
47
47
|
describe('createSeed static method', () => {
|
|
48
48
|
it('should return the seed function unchanged', () => {
|
|
49
|
-
const seedFn = async (
|
|
49
|
+
const seedFn = async ({
|
|
50
|
+
attrs,
|
|
51
|
+
factory,
|
|
52
|
+
db,
|
|
53
|
+
}: { attrs: any; factory: any; db: any }) => {
|
|
50
54
|
return { id: 1, name: 'test' };
|
|
51
55
|
};
|
|
52
56
|
|
|
@@ -57,12 +61,14 @@ describe('Factory', () => {
|
|
|
57
61
|
});
|
|
58
62
|
|
|
59
63
|
it('should work with different seed function signatures', () => {
|
|
60
|
-
const simpleSeed = () =>
|
|
61
|
-
const complexSeed = async (
|
|
62
|
-
attrs
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
const simpleSeed = async () => ({ simple: true });
|
|
65
|
+
const complexSeed = async ({
|
|
66
|
+
attrs,
|
|
67
|
+
}: {
|
|
68
|
+
attrs: { name: string };
|
|
69
|
+
factory: any;
|
|
70
|
+
db: any;
|
|
71
|
+
}) => {
|
|
66
72
|
return { name: attrs.name, created: true };
|
|
67
73
|
};
|
|
68
74
|
|
|
@@ -124,8 +130,8 @@ describe('Factory', () => {
|
|
|
124
130
|
});
|
|
125
131
|
|
|
126
132
|
it('should properly type FactorySeed', () => {
|
|
127
|
-
// Test that FactorySeed requires async return
|
|
128
|
-
const seed: FactorySeed = async (attrs, factory, db) => ({
|
|
133
|
+
// Test that FactorySeed requires async return with object parameter
|
|
134
|
+
const seed: FactorySeed = async ({ attrs, factory, db }) => ({
|
|
129
135
|
seeded: true,
|
|
130
136
|
});
|
|
131
137
|
|
|
@@ -155,7 +161,7 @@ describe('Factory types', () => {
|
|
|
155
161
|
TestFactory,
|
|
156
162
|
{ created: number },
|
|
157
163
|
any
|
|
158
|
-
> = async (attrs
|
|
164
|
+
> = async ({ attrs }) => {
|
|
159
165
|
return { created: attrs.count };
|
|
160
166
|
};
|
|
161
167
|
|
|
@@ -7,11 +7,10 @@ import { createKyselyDb } from '../helpers';
|
|
|
7
7
|
import { wrapVitestKyselyTransaction } from '../kysely';
|
|
8
8
|
|
|
9
9
|
const db = () => createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
|
|
10
|
-
const itWithTransaction = wrapVitestKyselyTransaction<TestDatabase>(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
);
|
|
10
|
+
const itWithTransaction = wrapVitestKyselyTransaction<TestDatabase>(it, {
|
|
11
|
+
connection: db,
|
|
12
|
+
setup: createTestTables,
|
|
13
|
+
});
|
|
15
14
|
|
|
16
15
|
const int8TypeId = 20;
|
|
17
16
|
pg.types.setTypeParser(int8TypeId, (val) => {
|
|
@@ -280,7 +279,10 @@ describe('KyselyFactory', () => {
|
|
|
280
279
|
|
|
281
280
|
const seeds = {
|
|
282
281
|
createAdminUser: KyselyFactory.createSeed(
|
|
283
|
-
async (
|
|
282
|
+
async ({
|
|
283
|
+
attrs,
|
|
284
|
+
factory,
|
|
285
|
+
}: { attrs: { name?: string }; factory: any; db: any }) => {
|
|
284
286
|
return await factory.insert('user', {
|
|
285
287
|
name: attrs.name || 'Admin User',
|
|
286
288
|
email: 'admin@example.com',
|
|
@@ -320,11 +322,14 @@ describe('KyselyFactory', () => {
|
|
|
320
322
|
|
|
321
323
|
const seeds = {
|
|
322
324
|
createCustomUser: KyselyFactory.createSeed(
|
|
323
|
-
async (
|
|
324
|
-
attrs
|
|
325
|
-
factory
|
|
326
|
-
|
|
327
|
-
|
|
325
|
+
async ({
|
|
326
|
+
attrs,
|
|
327
|
+
factory,
|
|
328
|
+
}: {
|
|
329
|
+
attrs: { name: string; email: string };
|
|
330
|
+
factory: any;
|
|
331
|
+
db: any;
|
|
332
|
+
}) => {
|
|
328
333
|
return await factory.insert('user', attrs);
|
|
329
334
|
},
|
|
330
335
|
),
|
|
@@ -37,11 +37,10 @@ class Comment extends Model {
|
|
|
37
37
|
user_id!: string;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
const it = wrapVitestObjectionTransaction(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
);
|
|
40
|
+
const it = wrapVitestObjectionTransaction(test, {
|
|
41
|
+
connection: createKnexDb,
|
|
42
|
+
setup: createTestTablesKnex,
|
|
43
|
+
});
|
|
45
44
|
describe('ObjectionFactory', () => {
|
|
46
45
|
it('should create an ObjectionFactory instance', ({ trx }) => {
|
|
47
46
|
const builders = {};
|
|
@@ -217,20 +216,22 @@ describe('ObjectionFactory', () => {
|
|
|
217
216
|
|
|
218
217
|
const builders = { user: userBuilder };
|
|
219
218
|
|
|
220
|
-
const createAdminSeed = async (attrs: any, factory: any, db: Knex) => {
|
|
221
|
-
return await factory.insert('user', {
|
|
222
|
-
name: attrs.name || 'Admin User',
|
|
223
|
-
});
|
|
224
|
-
};
|
|
225
|
-
|
|
226
219
|
const seeds = {
|
|
227
|
-
createAdmin:
|
|
220
|
+
createAdmin: ObjectionFactory.createSeed(
|
|
221
|
+
async ({
|
|
222
|
+
attrs,
|
|
223
|
+
factory,
|
|
224
|
+
}: { attrs: { name?: string }; factory: any; db: Knex }) => {
|
|
225
|
+
return await factory.insert('user', {
|
|
226
|
+
name: attrs.name || 'Admin User',
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
),
|
|
228
230
|
};
|
|
229
231
|
|
|
230
232
|
const factory = new ObjectionFactory(builders, seeds, trx);
|
|
231
233
|
|
|
232
|
-
const
|
|
233
|
-
const result = await factory.seed('createAdmin', attrs);
|
|
234
|
+
const result = await factory.seed('createAdmin', { name: 'Super Admin' });
|
|
234
235
|
|
|
235
236
|
expect(result).toBeInstanceOf(User);
|
|
236
237
|
expect(result.name).toBe('Super Admin');
|
|
@@ -248,15 +249,15 @@ describe('ObjectionFactory', () => {
|
|
|
248
249
|
|
|
249
250
|
const builders = { user: userBuilder };
|
|
250
251
|
|
|
251
|
-
const createAdminSeed = async (attrs: any, factory: any, db: Knex) => {
|
|
252
|
-
return await factory.insert('user', {
|
|
253
|
-
name: 'Default Admin',
|
|
254
|
-
role: 'admin',
|
|
255
|
-
});
|
|
256
|
-
};
|
|
257
|
-
|
|
258
252
|
const seeds = {
|
|
259
|
-
createAdmin:
|
|
253
|
+
createAdmin: ObjectionFactory.createSeed(
|
|
254
|
+
async ({ factory }: { attrs: any; factory: any; db: Knex }) => {
|
|
255
|
+
return await factory.insert('user', {
|
|
256
|
+
name: 'Default Admin',
|
|
257
|
+
role: 'admin',
|
|
258
|
+
});
|
|
259
|
+
},
|
|
260
|
+
),
|
|
260
261
|
};
|
|
261
262
|
|
|
262
263
|
const factory = new ObjectionFactory(builders, seeds, trx);
|
|
@@ -284,25 +285,28 @@ describe('ObjectionFactory', () => {
|
|
|
284
285
|
|
|
285
286
|
const builders = { user: userBuilder };
|
|
286
287
|
|
|
287
|
-
|
|
288
|
-
attrs: any,
|
|
289
|
-
passedFactory: any,
|
|
290
|
-
passedDb: Knex,
|
|
291
|
-
) => {
|
|
292
|
-
// Verify that factory and db are passed correctly
|
|
293
|
-
expect(passedFactory).toBe(factory);
|
|
294
|
-
expect(passedDb).toBe(trx);
|
|
295
|
-
|
|
296
|
-
return await passedFactory.insert('user', {
|
|
297
|
-
name: `Complex ${attrs.data}`,
|
|
298
|
-
});
|
|
299
|
-
};
|
|
288
|
+
let factoryRef: any;
|
|
300
289
|
|
|
301
290
|
const seeds = {
|
|
302
|
-
complexSeed
|
|
291
|
+
complexSeed: ObjectionFactory.createSeed(
|
|
292
|
+
async ({
|
|
293
|
+
attrs,
|
|
294
|
+
factory: passedFactory,
|
|
295
|
+
db: passedDb,
|
|
296
|
+
}: { attrs: { data: string }; factory: any; db: Knex }) => {
|
|
297
|
+
// Verify that factory and db are passed correctly
|
|
298
|
+
expect(passedFactory).toBe(factoryRef);
|
|
299
|
+
expect(passedDb).toBe(trx);
|
|
300
|
+
|
|
301
|
+
return await passedFactory.insert('user', {
|
|
302
|
+
name: `Complex ${attrs.data}`,
|
|
303
|
+
});
|
|
304
|
+
},
|
|
305
|
+
),
|
|
303
306
|
};
|
|
304
307
|
|
|
305
308
|
const factory = new ObjectionFactory(builders, seeds, trx);
|
|
309
|
+
factoryRef = factory;
|
|
306
310
|
|
|
307
311
|
const result = await factory.seed('complexSeed', { data: 'test' });
|
|
308
312
|
|
|
@@ -311,7 +315,7 @@ describe('ObjectionFactory', () => {
|
|
|
311
315
|
});
|
|
312
316
|
|
|
313
317
|
it('should return the seed function unchanged', () => {
|
|
314
|
-
const seedFn = async (attrs: any
|
|
318
|
+
const seedFn = async ({ attrs }: { attrs: any; factory: any; db: any }) => {
|
|
315
319
|
return { id: 1, name: 'test' };
|
|
316
320
|
};
|
|
317
321
|
|
|
@@ -482,12 +486,12 @@ describe('ObjectionFactory', () => {
|
|
|
482
486
|
});
|
|
483
487
|
|
|
484
488
|
it('should handle seed function errors gracefully', async ({ trx }) => {
|
|
485
|
-
const failingSeed = async (attrs: any, factory: any, db: Knex) => {
|
|
486
|
-
throw new Error('Seed failed');
|
|
487
|
-
};
|
|
488
|
-
|
|
489
489
|
const seeds = {
|
|
490
|
-
failingSeed
|
|
490
|
+
failingSeed: ObjectionFactory.createSeed(
|
|
491
|
+
async ({}: { attrs: any; factory: any; db: Knex }) => {
|
|
492
|
+
throw new Error('Seed failed');
|
|
493
|
+
},
|
|
494
|
+
),
|
|
491
495
|
};
|
|
492
496
|
|
|
493
497
|
const factory = new ObjectionFactory({}, seeds, trx);
|
|
@@ -509,18 +513,22 @@ describe('ObjectionFactory', () => {
|
|
|
509
513
|
});
|
|
510
514
|
};
|
|
511
515
|
|
|
512
|
-
const adminSeed = async (
|
|
513
|
-
attrs: { isSuper?: boolean },
|
|
514
|
-
factory: any,
|
|
515
|
-
db: Knex,
|
|
516
|
-
) => {
|
|
517
|
-
return factory.insert('user', {
|
|
518
|
-
name: 'Admin',
|
|
519
|
-
});
|
|
520
|
-
};
|
|
521
|
-
|
|
522
516
|
const builders = { user: userBuilder };
|
|
523
|
-
const seeds = {
|
|
517
|
+
const seeds = {
|
|
518
|
+
admin: ObjectionFactory.createSeed(
|
|
519
|
+
async ({
|
|
520
|
+
factory,
|
|
521
|
+
}: {
|
|
522
|
+
attrs: { isSuper?: boolean };
|
|
523
|
+
factory: any;
|
|
524
|
+
db: Knex;
|
|
525
|
+
}) => {
|
|
526
|
+
return factory.insert('user', {
|
|
527
|
+
name: 'Admin',
|
|
528
|
+
});
|
|
529
|
+
},
|
|
530
|
+
),
|
|
531
|
+
};
|
|
524
532
|
|
|
525
533
|
// This should compile without type errors
|
|
526
534
|
const factory = new ObjectionFactory(builders, seeds, trx);
|
|
@@ -108,9 +108,12 @@ class Comment extends Model {
|
|
|
108
108
|
// Create database connection
|
|
109
109
|
|
|
110
110
|
// Create wrapped test with transaction isolation
|
|
111
|
-
const it = wrapVitestObjectionTransaction(base,
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
const it = wrapVitestObjectionTransaction(base, {
|
|
112
|
+
connection: createKnexDb,
|
|
113
|
+
setup: async (trx) => {
|
|
114
|
+
// Create tables in the transaction
|
|
115
|
+
await createTestTablesKnex(trx);
|
|
116
|
+
},
|
|
114
117
|
});
|
|
115
118
|
|
|
116
119
|
describe('VitestObjectionTransactionIsolator', () => {
|
|
@@ -6,11 +6,10 @@ import { createKyselyDb } from '../helpers';
|
|
|
6
6
|
import { extendWithFixtures, wrapVitestKyselyTransaction } from '../kysely';
|
|
7
7
|
|
|
8
8
|
const db = () => createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
|
|
9
|
-
const it = wrapVitestKyselyTransaction<TestDatabase>(
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
);
|
|
9
|
+
const it = wrapVitestKyselyTransaction<TestDatabase>(base, {
|
|
10
|
+
connection: db,
|
|
11
|
+
setup: createTestTables,
|
|
12
|
+
});
|
|
14
13
|
describe('Testkit Integration Tests', () => {
|
|
15
14
|
beforeAll(async () => {});
|
|
16
15
|
describe('Complex Factory Scenarios', () => {
|
|
@@ -193,10 +192,14 @@ describe('Testkit Integration Tests', () => {
|
|
|
193
192
|
// Create complex seeds
|
|
194
193
|
const seeds = {
|
|
195
194
|
blogWithAdminAndPosts: KyselyFactory.createSeed(
|
|
196
|
-
async (
|
|
197
|
-
attrs
|
|
198
|
-
factory
|
|
199
|
-
|
|
195
|
+
async ({
|
|
196
|
+
attrs,
|
|
197
|
+
factory,
|
|
198
|
+
}: {
|
|
199
|
+
attrs: { postCount?: number };
|
|
200
|
+
factory: KyselyFactory<TestDatabase, typeof builders, {}>;
|
|
201
|
+
db: any;
|
|
202
|
+
}) => {
|
|
200
203
|
// Create admin user
|
|
201
204
|
const admin = await factory.insert('user', {
|
|
202
205
|
name: 'Blog Admin',
|
|
@@ -230,10 +233,14 @@ describe('Testkit Integration Tests', () => {
|
|
|
230
233
|
),
|
|
231
234
|
|
|
232
235
|
usersWithPosts: KyselyFactory.createSeed(
|
|
233
|
-
async (
|
|
234
|
-
attrs
|
|
235
|
-
factory
|
|
236
|
-
|
|
236
|
+
async ({
|
|
237
|
+
attrs,
|
|
238
|
+
factory,
|
|
239
|
+
}: {
|
|
240
|
+
attrs: { userCount?: number; postsPerUser?: number };
|
|
241
|
+
factory: KyselyFactory<TestDatabase, typeof builders, {}>;
|
|
242
|
+
db: any;
|
|
243
|
+
}) => {
|
|
237
244
|
const userCount = attrs.userCount || 2;
|
|
238
245
|
const postsPerUser = attrs.postsPerUser || 2;
|
|
239
246
|
|
|
@@ -481,11 +488,10 @@ describe('extendWithFixtures', () => {
|
|
|
481
488
|
};
|
|
482
489
|
|
|
483
490
|
// Create base test with transaction
|
|
484
|
-
const baseTest = wrapVitestKyselyTransaction<TestDatabase>(
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
);
|
|
491
|
+
const baseTest = wrapVitestKyselyTransaction<TestDatabase>(base, {
|
|
492
|
+
connection: db,
|
|
493
|
+
setup: createTestTables,
|
|
494
|
+
});
|
|
489
495
|
|
|
490
496
|
// Extend with factory fixture
|
|
491
497
|
const itWithFactory = extendWithFixtures<
|