@geekmidas/testkit 0.4.0 → 0.6.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-BFVnMMCC.mjs.map +1 -1
- package/dist/Factory-BhjUOBWN.cjs.map +1 -1
- package/dist/{Factory-c16c27Y6.d.cts → Factory-Bx0AJXZB.d.cts} +3 -2
- package/dist/Factory-Bx0AJXZB.d.cts.map +1 -0
- package/dist/{Factory-BcGJjLc8.d.mts → Factory-SFupxRC2.d.mts} +2 -1
- package/dist/Factory-SFupxRC2.d.mts.map +1 -0
- package/dist/Factory.d.cts +2 -2
- package/dist/Factory.d.mts +1 -1
- package/dist/KyselyFactory-BFqVIn_0.cjs.map +1 -1
- package/dist/KyselyFactory-DMswpwji.mjs.map +1 -1
- package/dist/{KyselyFactory-uZ45h7YU.d.cts → KyselyFactory-KLeKH43i.d.cts} +4 -3
- package/dist/KyselyFactory-KLeKH43i.d.cts.map +1 -0
- package/dist/{KyselyFactory-Cj-EultY.d.mts → KyselyFactory-vAxYodck.d.mts} +3 -2
- package/dist/KyselyFactory-vAxYodck.d.mts.map +1 -0
- package/dist/KyselyFactory.d.cts +3 -3
- package/dist/KyselyFactory.d.mts +2 -2
- package/dist/{ObjectionFactory-DL4qkuF1.d.mts → ObjectionFactory-BWjB49-i.d.mts} +3 -2
- package/dist/ObjectionFactory-BWjB49-i.d.mts.map +1 -0
- package/dist/ObjectionFactory-BeFBYcan.cjs.map +1 -1
- package/dist/ObjectionFactory-QCJ7u0Ql.mjs.map +1 -1
- package/dist/{ObjectionFactory-CdhzKs4f.d.cts → ObjectionFactory-aMGvAKt9.d.cts} +4 -3
- package/dist/ObjectionFactory-aMGvAKt9.d.cts.map +1 -0
- package/dist/ObjectionFactory.d.cts +3 -3
- package/dist/ObjectionFactory.d.mts +2 -2
- package/dist/{PostgresKyselyMigrator-upT-hmrz.mjs → PostgresKyselyMigrator-6sE1KOni.mjs} +2 -2
- package/dist/PostgresKyselyMigrator-6sE1KOni.mjs.map +1 -0
- package/dist/{PostgresKyselyMigrator-CIx3AFSR.d.mts → PostgresKyselyMigrator-CBltSOq5.d.cts} +3 -2
- package/dist/PostgresKyselyMigrator-CBltSOq5.d.cts.map +1 -0
- package/dist/{PostgresKyselyMigrator-CfytARcA.cjs → PostgresKyselyMigrator-D6IbPq8t.cjs} +2 -2
- package/dist/PostgresKyselyMigrator-D6IbPq8t.cjs.map +1 -0
- package/dist/{PostgresKyselyMigrator-CQ3aUoy_.d.cts → PostgresKyselyMigrator-DrVWncqd.d.mts} +3 -2
- package/dist/PostgresKyselyMigrator-DrVWncqd.d.mts.map +1 -0
- package/dist/PostgresKyselyMigrator.cjs +2 -2
- package/dist/PostgresKyselyMigrator.d.cts +2 -2
- package/dist/PostgresKyselyMigrator.d.mts +2 -2
- package/dist/PostgresKyselyMigrator.mjs +2 -2
- package/dist/{PostgresMigrator-DbuJGAVy.mjs → PostgresMigrator-BjjenqSd.mjs} +2 -2
- package/dist/PostgresMigrator-BjjenqSd.mjs.map +1 -0
- package/dist/{PostgresMigrator-D5UkK1_K.d.cts → PostgresMigrator-Bres0U6E.d.cts} +2 -1
- package/dist/PostgresMigrator-Bres0U6E.d.cts.map +1 -0
- package/dist/{PostgresMigrator-DFcNdCvD.cjs → PostgresMigrator-D6dQn0x2.cjs} +2 -2
- package/dist/PostgresMigrator-D6dQn0x2.cjs.map +1 -0
- package/dist/{PostgresMigrator-DQaRxoaY.d.mts → PostgresMigrator-S-YYosAC.d.mts} +2 -1
- package/dist/PostgresMigrator-S-YYosAC.d.mts.map +1 -0
- package/dist/PostgresMigrator.cjs +1 -1
- package/dist/PostgresMigrator.d.cts +1 -1
- package/dist/PostgresMigrator.d.mts +1 -1
- package/dist/PostgresMigrator.mjs +1 -1
- package/dist/{PostgresObjectionMigrator-CZHHcCOv.d.cts → PostgresObjectionMigrator-CPfBAP7r.d.cts} +3 -2
- package/dist/PostgresObjectionMigrator-CPfBAP7r.d.cts.map +1 -0
- package/dist/{PostgresObjectionMigrator-BG6ymgnt.cjs → PostgresObjectionMigrator-DK8ODIHQ.cjs} +2 -2
- package/dist/PostgresObjectionMigrator-DK8ODIHQ.cjs.map +1 -0
- package/dist/{PostgresObjectionMigrator-D_hCcrQu.d.mts → PostgresObjectionMigrator-DVEqB5tp.d.mts} +3 -2
- package/dist/PostgresObjectionMigrator-DVEqB5tp.d.mts.map +1 -0
- package/dist/{PostgresObjectionMigrator-DPj2pOpX.mjs → PostgresObjectionMigrator-D_QxXbIN.mjs} +2 -2
- package/dist/PostgresObjectionMigrator-D_QxXbIN.mjs.map +1 -0
- package/dist/PostgresObjectionMigrator.cjs +2 -2
- package/dist/PostgresObjectionMigrator.d.cts +2 -2
- package/dist/PostgresObjectionMigrator.d.mts +2 -2
- package/dist/PostgresObjectionMigrator.mjs +2 -2
- package/dist/{VitestKyselyTransactionIsolator-D3EZZhjZ.d.cts → VitestKyselyTransactionIsolator-CduJlHoT.d.cts} +4 -3
- package/dist/VitestKyselyTransactionIsolator-CduJlHoT.d.cts.map +1 -0
- package/dist/{VitestKyselyTransactionIsolator-Dxlp1u0f.d.mts → VitestKyselyTransactionIsolator-Cswnnj0k.d.mts} +4 -3
- package/dist/VitestKyselyTransactionIsolator-Cswnnj0k.d.mts.map +1 -0
- package/dist/{VitestKyselyTransactionIsolator-EvDLk5zg.cjs → VitestKyselyTransactionIsolator-D7RRXOBa.cjs} +2 -2
- package/dist/VitestKyselyTransactionIsolator-D7RRXOBa.cjs.map +1 -0
- package/dist/{VitestKyselyTransactionIsolator-CNURW8y6.mjs → VitestKyselyTransactionIsolator-DceyIqr4.mjs} +2 -2
- package/dist/VitestKyselyTransactionIsolator-DceyIqr4.mjs.map +1 -0
- package/dist/VitestKyselyTransactionIsolator.cjs +1 -1
- package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
- package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
- package/dist/VitestKyselyTransactionIsolator.mjs +1 -1
- package/dist/{VitestObjectionTransactionIsolator-1TpsPqfG.d.cts → VitestObjectionTransactionIsolator-BXoR6xdG.d.cts} +4 -3
- package/dist/VitestObjectionTransactionIsolator-BXoR6xdG.d.cts.map +1 -0
- package/dist/{VitestObjectionTransactionIsolator-CM5KTAFA.cjs → VitestObjectionTransactionIsolator-CdLRrzNf.cjs} +2 -2
- package/dist/VitestObjectionTransactionIsolator-CdLRrzNf.cjs.map +1 -0
- package/dist/{VitestObjectionTransactionIsolator-jQFaCz0u.mjs → VitestObjectionTransactionIsolator-OF2osYY5.mjs} +2 -2
- package/dist/VitestObjectionTransactionIsolator-OF2osYY5.mjs.map +1 -0
- package/dist/{VitestObjectionTransactionIsolator-i9jIgU8Q.d.mts → VitestObjectionTransactionIsolator-x6hY5j4u.d.mts} +4 -3
- package/dist/VitestObjectionTransactionIsolator-x6hY5j4u.d.mts.map +1 -0
- package/dist/VitestObjectionTransactionIsolator.cjs +1 -1
- package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
- package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
- package/dist/VitestObjectionTransactionIsolator.mjs +1 -1
- package/dist/{VitestTransactionIsolator-BvR19bYn.d.mts → VitestTransactionIsolator-BNWJqh9f.d.mts} +3 -2
- package/dist/VitestTransactionIsolator-BNWJqh9f.d.mts.map +1 -0
- package/dist/VitestTransactionIsolator-CMfJXZP8.cjs.map +1 -1
- package/dist/{VitestTransactionIsolator-CwQaxZLP.d.cts → VitestTransactionIsolator-CSroc7Df.d.cts} +3 -2
- package/dist/VitestTransactionIsolator-CSroc7Df.d.cts.map +1 -0
- package/dist/VitestTransactionIsolator-DQ7tLqgV.mjs.map +1 -1
- package/dist/VitestTransactionIsolator.d.cts +1 -1
- package/dist/VitestTransactionIsolator.d.mts +1 -1
- package/dist/aws.cjs.map +1 -1
- package/dist/aws.d.cts +2 -0
- package/dist/aws.d.cts.map +1 -0
- package/dist/aws.d.mts +2 -0
- package/dist/aws.d.mts.map +1 -0
- package/dist/aws.mjs.map +1 -1
- package/dist/benchmark.cjs.map +1 -1
- package/dist/benchmark.d.cts +1 -0
- package/dist/benchmark.d.cts.map +1 -0
- package/dist/benchmark.d.mts +1 -0
- package/dist/benchmark.d.mts.map +1 -0
- package/dist/benchmark.mjs.map +1 -1
- package/dist/better-auth.cjs +29 -30
- package/dist/better-auth.cjs.map +1 -1
- package/dist/better-auth.d.cts +2 -2
- package/dist/better-auth.d.cts.map +1 -0
- package/dist/better-auth.d.mts.map +1 -0
- package/dist/better-auth.mjs +29 -30
- package/dist/better-auth.mjs.map +1 -1
- package/dist/directory-B-Ozljzk.mjs.map +1 -1
- package/dist/directory-BVC8g7cX.cjs.map +1 -1
- package/dist/{directory-BXavAeJZ.d.mts → directory-CVrfTq1I.d.mts} +2 -1
- package/dist/directory-CVrfTq1I.d.mts.map +1 -0
- package/dist/directory-Cys9g76X.d.cts +13 -0
- package/dist/directory-Cys9g76X.d.cts.map +1 -0
- package/dist/faker-B14IEMIN.cjs.map +1 -1
- package/dist/faker-BGKYFoCT.mjs.map +1 -1
- package/dist/{faker-DvxiCtxc.d.cts → faker-BSH1EMtg.d.cts} +3 -3
- package/dist/faker-BSH1EMtg.d.cts.map +1 -0
- package/dist/faker-DHh7xs4u.d.mts.map +1 -0
- package/dist/faker.d.cts +1 -1
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +1 -0
- package/dist/helpers.d.cts.map +1 -0
- package/dist/helpers.d.mts +1 -0
- package/dist/helpers.d.mts.map +1 -0
- package/dist/helpers.mjs.map +1 -1
- package/dist/kysely.cjs +3 -3
- package/dist/kysely.cjs.map +1 -1
- package/dist/kysely.d.cts +8 -7
- package/dist/kysely.d.cts.map +1 -0
- package/dist/kysely.d.mts +7 -6
- package/dist/kysely.d.mts.map +1 -0
- package/dist/kysely.mjs +3 -3
- package/dist/kysely.mjs.map +1 -1
- package/dist/logger.cjs.map +1 -1
- package/dist/logger.d.cts +1 -0
- package/dist/logger.d.cts.map +1 -0
- package/dist/logger.d.mts +1 -0
- package/dist/logger.d.mts.map +1 -0
- package/dist/logger.mjs.map +1 -1
- package/dist/objection.cjs +3 -3
- package/dist/objection.cjs.map +1 -1
- package/dist/objection.d.cts +8 -7
- package/dist/objection.d.cts.map +1 -0
- package/dist/objection.d.mts +7 -6
- package/dist/objection.d.mts.map +1 -0
- package/dist/objection.mjs +3 -3
- package/dist/objection.mjs.map +1 -1
- package/dist/os/directory.d.cts +1 -1
- package/dist/os/directory.d.mts +1 -1
- package/dist/os/index.d.cts +1 -1
- package/dist/os/index.d.mts +1 -1
- package/dist/timer.cjs.map +1 -1
- package/dist/timer.d.cts +2 -0
- package/dist/timer.d.cts.map +1 -0
- package/dist/timer.d.mts +2 -0
- package/dist/timer.d.mts.map +1 -0
- package/dist/timer.mjs.map +1 -1
- package/package.json +5 -5
- package/src/Factory.ts +72 -72
- package/src/KyselyFactory.ts +330 -330
- package/src/ObjectionFactory.ts +354 -355
- package/src/PostgresKyselyMigrator.ts +37 -37
- package/src/PostgresMigrator.ts +107 -107
- package/src/PostgresObjectionMigrator.ts +91 -91
- package/src/VitestKyselyTransactionIsolator.ts +27 -27
- package/src/VitestObjectionTransactionIsolator.ts +39 -39
- package/src/VitestTransactionIsolator.ts +196 -195
- package/src/__tests__/Factory.spec.ts +163 -155
- package/src/__tests__/KyselyFactory.spec.ts +443 -439
- package/src/__tests__/ObjectionFactory.spec.ts +563 -557
- package/src/__tests__/PostgresKyselyMigrator.spec.ts +641 -641
- package/src/__tests__/PostgresMigrator.spec.ts +341 -341
- package/src/__tests__/PostgresObjectionMigrator.spec.ts +578 -578
- package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +114 -114
- package/src/__tests__/benchmark.spec.ts +140 -0
- package/src/__tests__/better-auth.spec.ts +15 -15
- package/src/__tests__/faker.spec.ts +226 -137
- package/src/__tests__/integration.spec.ts +597 -597
- package/src/__tests__/utilities.spec.ts +211 -0
- package/src/aws.ts +104 -104
- package/src/benchmark.ts +12 -12
- package/src/better-auth.ts +286 -301
- package/src/faker.ts +153 -153
- package/src/helpers.ts +6 -6
- package/src/kysely.ts +33 -33
- package/src/logger.ts +10 -10
- package/src/objection.ts +31 -31
- package/src/os/directory.ts +11 -10
- package/src/timer.ts +1 -1
- package/test/globalSetup.ts +45 -45
- package/test/helpers.ts +189 -189
- package/test/migrations/1749664623372_user.ts +13 -13
- package/tsconfig.json +9 -0
- package/vitest.config.ts +4 -4
- package/dist/PostgresKyselyMigrator-CfytARcA.cjs.map +0 -1
- package/dist/PostgresKyselyMigrator-upT-hmrz.mjs.map +0 -1
- package/dist/PostgresMigrator-DFcNdCvD.cjs.map +0 -1
- package/dist/PostgresMigrator-DbuJGAVy.mjs.map +0 -1
- package/dist/PostgresObjectionMigrator-BG6ymgnt.cjs.map +0 -1
- package/dist/PostgresObjectionMigrator-DPj2pOpX.mjs.map +0 -1
- package/dist/VitestKyselyTransactionIsolator-CNURW8y6.mjs.map +0 -1
- package/dist/VitestKyselyTransactionIsolator-EvDLk5zg.cjs.map +0 -1
- package/dist/VitestObjectionTransactionIsolator-CM5KTAFA.cjs.map +0 -1
- package/dist/VitestObjectionTransactionIsolator-jQFaCz0u.mjs.map +0 -1
- package/dist/directory-Mi7tdOuD.d.cts +0 -12
package/src/ObjectionFactory.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Knex } from 'knex';
|
|
2
2
|
import type { Model } from 'objection';
|
|
3
|
-
import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory
|
|
4
|
-
import { type FakerFactory, faker } from './faker
|
|
3
|
+
import { type ExtractSeedAttrs, Factory, type FactorySeed } from './Factory';
|
|
4
|
+
import { type FakerFactory, faker } from './faker';
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Factory implementation for Objection.js ORM, providing test data creation utilities.
|
|
@@ -40,374 +40,373 @@ import { type FakerFactory, faker } from './faker.ts';
|
|
|
40
40
|
* ```
|
|
41
41
|
*/
|
|
42
42
|
export class ObjectionFactory<
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
Builders extends Record<string, any>,
|
|
44
|
+
Seeds extends Record<string, any>,
|
|
45
45
|
> extends Factory<Builders, Seeds> {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Creates a typed seed function with proper type inference.
|
|
48
|
+
* Inherits from the base Factory class implementation.
|
|
49
|
+
*
|
|
50
|
+
* @template Seed - The seed function type
|
|
51
|
+
* @param seedFn - The seed function to wrap (receives { attrs, factory, db } object)
|
|
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
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
static override createSeed<Seed extends FactorySeed>(seedFn: Seed): Seed {
|
|
68
|
+
return Factory.createSeed(seedFn);
|
|
69
|
+
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Creates a typed builder function for Objection.js models.
|
|
73
|
+
* This is a utility method that helps create builders with proper type inference.
|
|
74
|
+
*
|
|
75
|
+
* @template TModel - The Objection.js Model class type
|
|
76
|
+
* @template Attrs - The attributes type for the builder (defaults to Partial of model)
|
|
77
|
+
* @template Factory - The factory instance type
|
|
78
|
+
* @template Result - The result type (defaults to the model instance)
|
|
79
|
+
*
|
|
80
|
+
* @param ModelClass - The Objection.js Model class
|
|
81
|
+
* @param defaults - Optional function to provide default values (receives destructured context)
|
|
82
|
+
* @param autoInsert - Whether to automatically insert the record (default: true)
|
|
83
|
+
* @returns A builder function that creates and optionally inserts records
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* // Create a simple builder with defaults - destructure only what you need
|
|
88
|
+
* const userBuilder = ObjectionFactory.createBuilder(User,
|
|
89
|
+
* ({ attrs, faker }) => ({
|
|
90
|
+
* id: faker.string.uuid(),
|
|
91
|
+
* name: faker.person.fullName(),
|
|
92
|
+
* email: faker.internet.email(),
|
|
93
|
+
* createdAt: new Date(),
|
|
94
|
+
* ...attrs
|
|
95
|
+
* })
|
|
96
|
+
* );
|
|
97
|
+
*
|
|
98
|
+
* // Only need faker? Just destructure that
|
|
99
|
+
* const leaveTypeBuilder = ObjectionFactory.createBuilder(LeaveType,
|
|
100
|
+
* ({ faker }) => ({
|
|
101
|
+
* name: faker.helpers.arrayElement(['Annual', 'Sick', 'Maternity']),
|
|
102
|
+
* code: faker.string.alpha({ length: 3, casing: 'upper' }),
|
|
103
|
+
* })
|
|
104
|
+
* );
|
|
105
|
+
*
|
|
106
|
+
* // Create a builder that doesn't auto-insert (useful for nested inserts)
|
|
107
|
+
* const addressBuilder = ObjectionFactory.createBuilder(Address,
|
|
108
|
+
* ({ attrs }) => ({
|
|
109
|
+
* street: '123 Main St',
|
|
110
|
+
* city: 'Anytown',
|
|
111
|
+
* ...attrs
|
|
112
|
+
* }),
|
|
113
|
+
* false // Don't auto-insert
|
|
114
|
+
* );
|
|
115
|
+
*
|
|
116
|
+
* // Use with relations
|
|
117
|
+
* const postBuilder = ObjectionFactory.createBuilder(Post,
|
|
118
|
+
* async ({ attrs, factory, faker }) => ({
|
|
119
|
+
* title: faker.lorem.sentence(),
|
|
120
|
+
* content: faker.lorem.paragraphs(),
|
|
121
|
+
* authorId: attrs.authorId || (await factory.insert('user')).id,
|
|
122
|
+
* ...attrs
|
|
123
|
+
* })
|
|
124
|
+
* );
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
static createBuilder<
|
|
128
|
+
TModel extends typeof Model,
|
|
129
|
+
Attrs extends Partial<InstanceType<TModel>> = Partial<InstanceType<TModel>>,
|
|
130
|
+
Factory = any,
|
|
131
|
+
Result = InstanceType<TModel>,
|
|
132
|
+
>(
|
|
133
|
+
ModelClass: TModel,
|
|
134
|
+
defaults?: (context: {
|
|
135
|
+
attrs: Attrs;
|
|
136
|
+
factory: Factory;
|
|
137
|
+
db: Knex;
|
|
138
|
+
faker: FakerFactory;
|
|
139
|
+
}) =>
|
|
140
|
+
| Partial<InstanceType<TModel>>
|
|
141
|
+
| Promise<Partial<InstanceType<TModel>>>,
|
|
142
|
+
autoInsert?: boolean,
|
|
143
|
+
): (
|
|
144
|
+
attrs: Attrs,
|
|
145
|
+
factory: Factory,
|
|
146
|
+
db: Knex,
|
|
147
|
+
faker: FakerFactory,
|
|
148
|
+
) => Promise<Result> {
|
|
149
|
+
return async (
|
|
150
|
+
attrs: Attrs,
|
|
151
|
+
factory: Factory,
|
|
152
|
+
db: Knex,
|
|
153
|
+
fakerInstance: FakerFactory,
|
|
154
|
+
) => {
|
|
155
|
+
// Start with attributes
|
|
156
|
+
let data: Partial<InstanceType<TModel>> = { ...attrs };
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
// Apply defaults
|
|
159
|
+
if (defaults) {
|
|
160
|
+
const defaultValues = await defaults({
|
|
161
|
+
attrs,
|
|
162
|
+
factory,
|
|
163
|
+
db,
|
|
164
|
+
faker: fakerInstance,
|
|
165
|
+
});
|
|
166
|
+
data = { ...defaultValues, ...data };
|
|
167
|
+
}
|
|
168
168
|
|
|
169
|
-
|
|
170
|
-
|
|
169
|
+
// Create model instance
|
|
170
|
+
const model = ModelClass.fromJson(data) as InstanceType<TModel>;
|
|
171
171
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
172
|
+
// Handle insertion based on autoInsert flag
|
|
173
|
+
if (autoInsert !== false) {
|
|
174
|
+
// Auto insert is enabled by default
|
|
175
|
+
// Extract only defined values for insertion
|
|
176
|
+
const insertData = Object.entries(model).reduce((acc, [key, value]) => {
|
|
177
|
+
if (value !== undefined && key !== 'id') {
|
|
178
|
+
acc[key] = value;
|
|
179
|
+
}
|
|
180
|
+
return acc;
|
|
181
|
+
}, {} as any);
|
|
182
182
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
183
|
+
// Use static query method to insert data directly
|
|
184
|
+
const result = await ModelClass.query(db).insert(insertData);
|
|
185
|
+
return result as Result;
|
|
186
|
+
} else {
|
|
187
|
+
// Return model for factory to handle insertion
|
|
188
|
+
return model as Result;
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
193
192
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
193
|
+
/**
|
|
194
|
+
* Creates a new ObjectionFactory instance.
|
|
195
|
+
*
|
|
196
|
+
* @param builders - Record of builder functions for creating individual entities
|
|
197
|
+
* @param seeds - Record of seed functions for creating complex test scenarios
|
|
198
|
+
* @param db - Knex database connection instance
|
|
199
|
+
*/
|
|
200
|
+
constructor(
|
|
201
|
+
private builders: Builders,
|
|
202
|
+
private seeds: Seeds,
|
|
203
|
+
private db: Knex,
|
|
204
|
+
) {
|
|
205
|
+
super();
|
|
206
|
+
}
|
|
208
207
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
208
|
+
/**
|
|
209
|
+
* Inserts a single record into the database using the specified builder.
|
|
210
|
+
* Uses Objection.js's insertGraph method to handle nested relations.
|
|
211
|
+
*
|
|
212
|
+
* @template K - The builder name (must be a key of Builders)
|
|
213
|
+
* @param builderName - The name of the builder to use
|
|
214
|
+
* @param attrs - Optional attributes to override builder defaults
|
|
215
|
+
* @returns A promise resolving to the inserted record with all relations
|
|
216
|
+
* @throws Error if the specified builder doesn't exist
|
|
217
|
+
*
|
|
218
|
+
* @example
|
|
219
|
+
* ```typescript
|
|
220
|
+
* // Insert with defaults
|
|
221
|
+
* const user = await factory.insert('user');
|
|
222
|
+
*
|
|
223
|
+
* // Insert with overrides
|
|
224
|
+
* const adminUser = await factory.insert('user', {
|
|
225
|
+
* email: 'admin@example.com',
|
|
226
|
+
* role: 'admin'
|
|
227
|
+
* });
|
|
228
|
+
*
|
|
229
|
+
* // Insert with nested relations
|
|
230
|
+
* const userWithProfile = await factory.insert('user', {
|
|
231
|
+
* name: 'John Doe',
|
|
232
|
+
* profile: {
|
|
233
|
+
* bio: 'Software Developer',
|
|
234
|
+
* avatar: 'avatar.jpg'
|
|
235
|
+
* }
|
|
236
|
+
* });
|
|
237
|
+
* ```
|
|
238
|
+
*/
|
|
239
|
+
async insert<K extends keyof Builders>(
|
|
240
|
+
builderName: K,
|
|
241
|
+
attrs?: Parameters<Builders[K]>[0],
|
|
242
|
+
): Promise<Awaited<ReturnType<Builders[K]>>> {
|
|
243
|
+
if (!(builderName in this.builders)) {
|
|
244
|
+
throw new Error(
|
|
245
|
+
`Factory "${
|
|
246
|
+
builderName as string
|
|
247
|
+
}" does not exist. Make sure it is correct and registered in src/test/setup.ts`,
|
|
248
|
+
);
|
|
249
|
+
}
|
|
251
250
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
251
|
+
const result = await this.builders[builderName](
|
|
252
|
+
attrs || {},
|
|
253
|
+
this,
|
|
254
|
+
this.db,
|
|
255
|
+
faker,
|
|
256
|
+
);
|
|
258
257
|
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
258
|
+
// If the builder returns a model instance, insert it
|
|
259
|
+
if (result && typeof result.$query === 'function') {
|
|
260
|
+
// Extract data from model, excluding undefined values and id
|
|
261
|
+
const insertData = Object.entries(result).reduce((acc, [key, value]) => {
|
|
262
|
+
if (value !== undefined && key !== 'id') {
|
|
263
|
+
acc[key] = value;
|
|
264
|
+
}
|
|
265
|
+
return acc;
|
|
266
|
+
}, {} as any);
|
|
268
267
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
268
|
+
// Use the model's constructor to get the query builder
|
|
269
|
+
return await result.constructor.query(this.db).insert(insertData);
|
|
270
|
+
}
|
|
272
271
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
272
|
+
// Otherwise, assume the builder handled insertion itself
|
|
273
|
+
return result;
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Inserts multiple records into the database using the specified builder.
|
|
277
|
+
* Supports both static attributes and dynamic attribute generation via a function.
|
|
278
|
+
*
|
|
279
|
+
* @param count - The number of records to insert
|
|
280
|
+
* @param builderName - The name of the builder to use
|
|
281
|
+
* @param attrs - Static attributes or a function that generates attributes for each record
|
|
282
|
+
* @returns A promise resolving to an array of inserted records
|
|
283
|
+
* @throws Error if the specified builder doesn't exist
|
|
284
|
+
*
|
|
285
|
+
* @example
|
|
286
|
+
* ```typescript
|
|
287
|
+
* // Insert multiple with same attributes
|
|
288
|
+
* const users = await factory.insertMany(5, 'user', { role: 'member' });
|
|
289
|
+
*
|
|
290
|
+
* // Insert multiple with dynamic attributes
|
|
291
|
+
* const posts = await factory.insertMany(10, 'post', (idx) => ({
|
|
292
|
+
* title: `Post ${idx + 1}`,
|
|
293
|
+
* content: `Content for post ${idx + 1}`,
|
|
294
|
+
* publishedAt: new Date()
|
|
295
|
+
* }));
|
|
296
|
+
*
|
|
297
|
+
* // Create users with sequential emails
|
|
298
|
+
* const admins = await factory.insertMany(3, 'user', (idx) => ({
|
|
299
|
+
* email: `admin${idx + 1}@example.com`,
|
|
300
|
+
* role: 'admin'
|
|
301
|
+
* }));
|
|
302
|
+
* ```
|
|
303
|
+
*/
|
|
304
|
+
// Method overloads for better type inference
|
|
305
|
+
async insertMany<K extends keyof Builders>(
|
|
306
|
+
count: number,
|
|
307
|
+
builderName: K,
|
|
308
|
+
attrs?: Parameters<Builders[K]>[0],
|
|
309
|
+
): Promise<Awaited<ReturnType<Builders[K]>>[]>;
|
|
310
|
+
async insertMany<K extends keyof Builders>(
|
|
311
|
+
count: number,
|
|
312
|
+
builderName: K,
|
|
313
|
+
attrs: (idx: number, faker: FakerFactory) => Parameters<Builders[K]>[0],
|
|
314
|
+
): Promise<Awaited<ReturnType<Builders[K]>>[]>;
|
|
315
|
+
async insertMany<K extends keyof Builders>(
|
|
316
|
+
count: number,
|
|
317
|
+
builderName: K,
|
|
318
|
+
attrs?: any,
|
|
319
|
+
): Promise<Awaited<ReturnType<Builders[K]>>[]> {
|
|
320
|
+
if (!(builderName in this.builders)) {
|
|
321
|
+
throw new Error(
|
|
322
|
+
`Builder "${
|
|
323
|
+
builderName as string
|
|
324
|
+
}" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
|
|
325
|
+
);
|
|
326
|
+
}
|
|
328
327
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
328
|
+
const records: any[] = [];
|
|
329
|
+
for (let i = 0; i < count; i++) {
|
|
330
|
+
const newAttrs =
|
|
331
|
+
typeof attrs === 'function' ? await (attrs as any)(i, faker) : attrs;
|
|
333
332
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
333
|
+
records.push(
|
|
334
|
+
this.builders[builderName](newAttrs, this, this.db, faker).then(
|
|
335
|
+
(record: any) => {
|
|
336
|
+
// If the builder returns a model instance, insert it
|
|
337
|
+
if (record && typeof record.$query === 'function') {
|
|
338
|
+
// Extract data from model, excluding undefined values and id
|
|
339
|
+
const insertData = Object.entries(record).reduce(
|
|
340
|
+
(acc, [key, value]) => {
|
|
341
|
+
if (value !== undefined && key !== 'id') {
|
|
342
|
+
acc[key] = value;
|
|
343
|
+
}
|
|
344
|
+
return acc;
|
|
345
|
+
},
|
|
346
|
+
{} as any,
|
|
347
|
+
);
|
|
349
348
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
349
|
+
// Use the model's constructor to get the query builder
|
|
350
|
+
return record.constructor.query(this.db).insert(insertData);
|
|
351
|
+
}
|
|
352
|
+
// Otherwise, assume the builder handled insertion itself
|
|
353
|
+
return record;
|
|
354
|
+
},
|
|
355
|
+
),
|
|
356
|
+
);
|
|
357
|
+
}
|
|
359
358
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
359
|
+
return Promise.all(records);
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Executes a seed function to create complex test scenarios with multiple related records.
|
|
363
|
+
* Seeds are useful for setting up complete test environments with realistic data relationships.
|
|
364
|
+
*
|
|
365
|
+
* @template K - The seed name (must be a key of Seeds)
|
|
366
|
+
* @param seedName - The name of the seed to execute
|
|
367
|
+
* @param attrs - Optional configuration attributes for the seed
|
|
368
|
+
* @returns The result of the seed function (typically the primary record created)
|
|
369
|
+
* @throws Error if the specified seed doesn't exist
|
|
370
|
+
*
|
|
371
|
+
* @example
|
|
372
|
+
* ```typescript
|
|
373
|
+
* // Execute a simple seed
|
|
374
|
+
* const user = await factory.seed('userWithProfile');
|
|
375
|
+
*
|
|
376
|
+
* // Execute a seed with configuration
|
|
377
|
+
* const author = await factory.seed('authorWithBooks', {
|
|
378
|
+
* bookCount: 5,
|
|
379
|
+
* includeReviews: true
|
|
380
|
+
* });
|
|
381
|
+
*
|
|
382
|
+
* // Use seed result in tests with Objection.js relations
|
|
383
|
+
* const company = await factory.seed('companyWithDepartments', {
|
|
384
|
+
* departmentCount: 3,
|
|
385
|
+
* employeesPerDepartment: 10
|
|
386
|
+
* });
|
|
387
|
+
*
|
|
388
|
+
* // Access eager loaded relations
|
|
389
|
+
* const companyWithRelations = await Company.query()
|
|
390
|
+
* .findById(company.id)
|
|
391
|
+
* .withGraphFetched('[departments.employees]');
|
|
392
|
+
* ```
|
|
393
|
+
*/
|
|
394
|
+
seed<K extends keyof Seeds>(
|
|
395
|
+
seedName: K,
|
|
396
|
+
attrs?: ExtractSeedAttrs<Seeds[K]>,
|
|
397
|
+
): ReturnType<Seeds[K]> {
|
|
398
|
+
if (!(seedName in this.seeds)) {
|
|
399
|
+
throw new Error(
|
|
400
|
+
`Seed "${
|
|
401
|
+
seedName as string
|
|
402
|
+
}" is not registered in this factory. Make sure it is correct and registered in src/test/setup.ts`,
|
|
403
|
+
);
|
|
404
|
+
}
|
|
406
405
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
406
|
+
return this.seeds[seedName]({
|
|
407
|
+
attrs: attrs || {},
|
|
408
|
+
factory: this,
|
|
409
|
+
db: this.db,
|
|
410
|
+
});
|
|
411
|
+
}
|
|
413
412
|
}
|