@geekmidas/testkit 0.4.0 → 1.0.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.
Files changed (209) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/dist/Factory-BFVnMMCC.mjs.map +1 -1
  3. package/dist/{Factory-c16c27Y6.d.cts → Factory-BOX312yd.d.cts} +3 -2
  4. package/dist/Factory-BOX312yd.d.cts.map +1 -0
  5. package/dist/Factory-BhjUOBWN.cjs.map +1 -1
  6. package/dist/{Factory-BcGJjLc8.d.mts → Factory-SFupxRC2.d.mts} +2 -1
  7. package/dist/Factory-SFupxRC2.d.mts.map +1 -0
  8. package/dist/Factory.d.cts +2 -2
  9. package/dist/Factory.d.mts +1 -1
  10. package/dist/KyselyFactory-BFqVIn_0.cjs.map +1 -1
  11. package/dist/KyselyFactory-DMswpwji.mjs.map +1 -1
  12. package/dist/{KyselyFactory-uZ45h7YU.d.cts → KyselyFactory-Dy5zzV4B.d.cts} +4 -3
  13. package/dist/KyselyFactory-Dy5zzV4B.d.cts.map +1 -0
  14. package/dist/{KyselyFactory-Cj-EultY.d.mts → KyselyFactory-vAxYodck.d.mts} +3 -2
  15. package/dist/KyselyFactory-vAxYodck.d.mts.map +1 -0
  16. package/dist/KyselyFactory.d.cts +3 -3
  17. package/dist/KyselyFactory.d.mts +2 -2
  18. package/dist/{ObjectionFactory-DL4qkuF1.d.mts → ObjectionFactory-BWjB49-i.d.mts} +3 -2
  19. package/dist/ObjectionFactory-BWjB49-i.d.mts.map +1 -0
  20. package/dist/ObjectionFactory-BeFBYcan.cjs.map +1 -1
  21. package/dist/{ObjectionFactory-CdhzKs4f.d.cts → ObjectionFactory-CD-WFuMJ.d.cts} +4 -3
  22. package/dist/ObjectionFactory-CD-WFuMJ.d.cts.map +1 -0
  23. package/dist/ObjectionFactory-QCJ7u0Ql.mjs.map +1 -1
  24. package/dist/ObjectionFactory.d.cts +3 -3
  25. package/dist/ObjectionFactory.d.mts +2 -2
  26. package/dist/{PostgresKyselyMigrator-upT-hmrz.mjs → PostgresKyselyMigrator-6sE1KOni.mjs} +2 -2
  27. package/dist/PostgresKyselyMigrator-6sE1KOni.mjs.map +1 -0
  28. package/dist/{PostgresKyselyMigrator-CIx3AFSR.d.mts → PostgresKyselyMigrator-CBltSOq5.d.cts} +3 -2
  29. package/dist/PostgresKyselyMigrator-CBltSOq5.d.cts.map +1 -0
  30. package/dist/{PostgresKyselyMigrator-CfytARcA.cjs → PostgresKyselyMigrator-D6IbPq8t.cjs} +2 -2
  31. package/dist/PostgresKyselyMigrator-D6IbPq8t.cjs.map +1 -0
  32. package/dist/{PostgresKyselyMigrator-CQ3aUoy_.d.cts → PostgresKyselyMigrator-DrVWncqd.d.mts} +3 -2
  33. package/dist/PostgresKyselyMigrator-DrVWncqd.d.mts.map +1 -0
  34. package/dist/PostgresKyselyMigrator.cjs +2 -2
  35. package/dist/PostgresKyselyMigrator.d.cts +2 -2
  36. package/dist/PostgresKyselyMigrator.d.mts +2 -2
  37. package/dist/PostgresKyselyMigrator.mjs +2 -2
  38. package/dist/{PostgresMigrator-DbuJGAVy.mjs → PostgresMigrator-BjjenqSd.mjs} +2 -2
  39. package/dist/PostgresMigrator-BjjenqSd.mjs.map +1 -0
  40. package/dist/{PostgresMigrator-D5UkK1_K.d.cts → PostgresMigrator-Bres0U6E.d.cts} +2 -1
  41. package/dist/PostgresMigrator-Bres0U6E.d.cts.map +1 -0
  42. package/dist/{PostgresMigrator-DFcNdCvD.cjs → PostgresMigrator-D6dQn0x2.cjs} +2 -2
  43. package/dist/PostgresMigrator-D6dQn0x2.cjs.map +1 -0
  44. package/dist/{PostgresMigrator-DQaRxoaY.d.mts → PostgresMigrator-S-YYosAC.d.mts} +2 -1
  45. package/dist/PostgresMigrator-S-YYosAC.d.mts.map +1 -0
  46. package/dist/PostgresMigrator.cjs +1 -1
  47. package/dist/PostgresMigrator.d.cts +1 -1
  48. package/dist/PostgresMigrator.d.mts +1 -1
  49. package/dist/PostgresMigrator.mjs +1 -1
  50. package/dist/{PostgresObjectionMigrator-CZHHcCOv.d.cts → PostgresObjectionMigrator-CPfBAP7r.d.cts} +3 -2
  51. package/dist/PostgresObjectionMigrator-CPfBAP7r.d.cts.map +1 -0
  52. package/dist/{PostgresObjectionMigrator-BG6ymgnt.cjs → PostgresObjectionMigrator-DK8ODIHQ.cjs} +2 -2
  53. package/dist/PostgresObjectionMigrator-DK8ODIHQ.cjs.map +1 -0
  54. package/dist/{PostgresObjectionMigrator-D_hCcrQu.d.mts → PostgresObjectionMigrator-DVEqB5tp.d.mts} +3 -2
  55. package/dist/PostgresObjectionMigrator-DVEqB5tp.d.mts.map +1 -0
  56. package/dist/{PostgresObjectionMigrator-DPj2pOpX.mjs → PostgresObjectionMigrator-D_QxXbIN.mjs} +2 -2
  57. package/dist/PostgresObjectionMigrator-D_QxXbIN.mjs.map +1 -0
  58. package/dist/PostgresObjectionMigrator.cjs +2 -2
  59. package/dist/PostgresObjectionMigrator.d.cts +2 -2
  60. package/dist/PostgresObjectionMigrator.d.mts +2 -2
  61. package/dist/PostgresObjectionMigrator.mjs +2 -2
  62. package/dist/{VitestKyselyTransactionIsolator-D3EZZhjZ.d.cts → VitestKyselyTransactionIsolator-CduJlHoT.d.cts} +4 -3
  63. package/dist/VitestKyselyTransactionIsolator-CduJlHoT.d.cts.map +1 -0
  64. package/dist/{VitestKyselyTransactionIsolator-Dxlp1u0f.d.mts → VitestKyselyTransactionIsolator-Cswnnj0k.d.mts} +4 -3
  65. package/dist/VitestKyselyTransactionIsolator-Cswnnj0k.d.mts.map +1 -0
  66. package/dist/{VitestKyselyTransactionIsolator-EvDLk5zg.cjs → VitestKyselyTransactionIsolator-D7RRXOBa.cjs} +2 -2
  67. package/dist/VitestKyselyTransactionIsolator-D7RRXOBa.cjs.map +1 -0
  68. package/dist/{VitestKyselyTransactionIsolator-CNURW8y6.mjs → VitestKyselyTransactionIsolator-DceyIqr4.mjs} +2 -2
  69. package/dist/VitestKyselyTransactionIsolator-DceyIqr4.mjs.map +1 -0
  70. package/dist/VitestKyselyTransactionIsolator.cjs +1 -1
  71. package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
  72. package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
  73. package/dist/VitestKyselyTransactionIsolator.mjs +1 -1
  74. package/dist/{VitestObjectionTransactionIsolator-1TpsPqfG.d.cts → VitestObjectionTransactionIsolator-BXoR6xdG.d.cts} +4 -3
  75. package/dist/VitestObjectionTransactionIsolator-BXoR6xdG.d.cts.map +1 -0
  76. package/dist/{VitestObjectionTransactionIsolator-CM5KTAFA.cjs → VitestObjectionTransactionIsolator-CdLRrzNf.cjs} +2 -2
  77. package/dist/VitestObjectionTransactionIsolator-CdLRrzNf.cjs.map +1 -0
  78. package/dist/{VitestObjectionTransactionIsolator-jQFaCz0u.mjs → VitestObjectionTransactionIsolator-OF2osYY5.mjs} +2 -2
  79. package/dist/VitestObjectionTransactionIsolator-OF2osYY5.mjs.map +1 -0
  80. package/dist/{VitestObjectionTransactionIsolator-i9jIgU8Q.d.mts → VitestObjectionTransactionIsolator-x6hY5j4u.d.mts} +4 -3
  81. package/dist/VitestObjectionTransactionIsolator-x6hY5j4u.d.mts.map +1 -0
  82. package/dist/VitestObjectionTransactionIsolator.cjs +1 -1
  83. package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
  84. package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
  85. package/dist/VitestObjectionTransactionIsolator.mjs +1 -1
  86. package/dist/{VitestTransactionIsolator-BvR19bYn.d.mts → VitestTransactionIsolator-BNWJqh9f.d.mts} +3 -2
  87. package/dist/VitestTransactionIsolator-BNWJqh9f.d.mts.map +1 -0
  88. package/dist/VitestTransactionIsolator-CMfJXZP8.cjs.map +1 -1
  89. package/dist/{VitestTransactionIsolator-CwQaxZLP.d.cts → VitestTransactionIsolator-CSroc7Df.d.cts} +3 -2
  90. package/dist/VitestTransactionIsolator-CSroc7Df.d.cts.map +1 -0
  91. package/dist/VitestTransactionIsolator-DQ7tLqgV.mjs.map +1 -1
  92. package/dist/VitestTransactionIsolator.d.cts +1 -1
  93. package/dist/VitestTransactionIsolator.d.mts +1 -1
  94. package/dist/aws.cjs.map +1 -1
  95. package/dist/aws.d.cts +2 -0
  96. package/dist/aws.d.cts.map +1 -0
  97. package/dist/aws.d.mts +2 -0
  98. package/dist/aws.d.mts.map +1 -0
  99. package/dist/aws.mjs.map +1 -1
  100. package/dist/benchmark.cjs.map +1 -1
  101. package/dist/benchmark.d.cts +1 -0
  102. package/dist/benchmark.d.cts.map +1 -0
  103. package/dist/benchmark.d.mts +1 -0
  104. package/dist/benchmark.d.mts.map +1 -0
  105. package/dist/benchmark.mjs.map +1 -1
  106. package/dist/better-auth.cjs +29 -30
  107. package/dist/better-auth.cjs.map +1 -1
  108. package/dist/better-auth.d.cts +2 -2
  109. package/dist/better-auth.d.cts.map +1 -0
  110. package/dist/better-auth.d.mts.map +1 -0
  111. package/dist/better-auth.mjs +29 -30
  112. package/dist/better-auth.mjs.map +1 -1
  113. package/dist/directory-B-Ozljzk.mjs.map +1 -1
  114. package/dist/directory-BVC8g7cX.cjs.map +1 -1
  115. package/dist/{directory-BXavAeJZ.d.mts → directory-CVrfTq1I.d.mts} +2 -1
  116. package/dist/directory-CVrfTq1I.d.mts.map +1 -0
  117. package/dist/{directory-Mi7tdOuD.d.cts → directory-DAnMWi50.d.cts} +2 -1
  118. package/dist/directory-DAnMWi50.d.cts.map +1 -0
  119. package/dist/faker-B14IEMIN.cjs.map +1 -1
  120. package/dist/faker-BGKYFoCT.mjs.map +1 -1
  121. package/dist/{faker-DvxiCtxc.d.cts → faker-Cg76aFNO.d.cts} +3 -3
  122. package/dist/faker-Cg76aFNO.d.cts.map +1 -0
  123. package/dist/faker-DHh7xs4u.d.mts.map +1 -0
  124. package/dist/faker.d.cts +1 -1
  125. package/dist/helpers.cjs.map +1 -1
  126. package/dist/helpers.d.cts +1 -0
  127. package/dist/helpers.d.cts.map +1 -0
  128. package/dist/helpers.d.mts +1 -0
  129. package/dist/helpers.d.mts.map +1 -0
  130. package/dist/helpers.mjs.map +1 -1
  131. package/dist/kysely.cjs +3 -3
  132. package/dist/kysely.cjs.map +1 -1
  133. package/dist/kysely.d.cts +8 -7
  134. package/dist/kysely.d.cts.map +1 -0
  135. package/dist/kysely.d.mts +7 -6
  136. package/dist/kysely.d.mts.map +1 -0
  137. package/dist/kysely.mjs +3 -3
  138. package/dist/kysely.mjs.map +1 -1
  139. package/dist/logger.cjs.map +1 -1
  140. package/dist/logger.d.cts +1 -0
  141. package/dist/logger.d.cts.map +1 -0
  142. package/dist/logger.d.mts +1 -0
  143. package/dist/logger.d.mts.map +1 -0
  144. package/dist/logger.mjs.map +1 -1
  145. package/dist/objection.cjs +3 -3
  146. package/dist/objection.cjs.map +1 -1
  147. package/dist/objection.d.cts +8 -7
  148. package/dist/objection.d.cts.map +1 -0
  149. package/dist/objection.d.mts +7 -6
  150. package/dist/objection.d.mts.map +1 -0
  151. package/dist/objection.mjs +3 -3
  152. package/dist/objection.mjs.map +1 -1
  153. package/dist/os/directory.d.cts +1 -1
  154. package/dist/os/directory.d.mts +1 -1
  155. package/dist/os/index.d.cts +1 -1
  156. package/dist/os/index.d.mts +1 -1
  157. package/dist/timer.cjs.map +1 -1
  158. package/dist/timer.d.cts +2 -0
  159. package/dist/timer.d.cts.map +1 -0
  160. package/dist/timer.d.mts +2 -0
  161. package/dist/timer.d.mts.map +1 -0
  162. package/dist/timer.mjs.map +1 -1
  163. package/package.json +5 -5
  164. package/src/Factory.ts +72 -72
  165. package/src/KyselyFactory.ts +330 -330
  166. package/src/ObjectionFactory.ts +354 -355
  167. package/src/PostgresKyselyMigrator.ts +37 -37
  168. package/src/PostgresMigrator.ts +107 -107
  169. package/src/PostgresObjectionMigrator.ts +91 -91
  170. package/src/VitestKyselyTransactionIsolator.ts +27 -27
  171. package/src/VitestObjectionTransactionIsolator.ts +39 -39
  172. package/src/VitestTransactionIsolator.ts +196 -195
  173. package/src/__tests__/Factory.spec.ts +163 -155
  174. package/src/__tests__/KyselyFactory.spec.ts +443 -439
  175. package/src/__tests__/ObjectionFactory.spec.ts +563 -557
  176. package/src/__tests__/PostgresKyselyMigrator.spec.ts +641 -641
  177. package/src/__tests__/PostgresMigrator.spec.ts +341 -341
  178. package/src/__tests__/PostgresObjectionMigrator.spec.ts +578 -578
  179. package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +114 -114
  180. package/src/__tests__/benchmark.spec.ts +140 -0
  181. package/src/__tests__/better-auth.spec.ts +15 -15
  182. package/src/__tests__/faker.spec.ts +226 -137
  183. package/src/__tests__/integration.spec.ts +597 -597
  184. package/src/__tests__/utilities.spec.ts +211 -0
  185. package/src/aws.ts +104 -104
  186. package/src/benchmark.ts +12 -12
  187. package/src/better-auth.ts +286 -301
  188. package/src/faker.ts +153 -153
  189. package/src/helpers.ts +6 -6
  190. package/src/kysely.ts +33 -33
  191. package/src/logger.ts +10 -10
  192. package/src/objection.ts +31 -31
  193. package/src/os/directory.ts +11 -10
  194. package/src/timer.ts +1 -1
  195. package/test/globalSetup.ts +45 -45
  196. package/test/helpers.ts +189 -189
  197. package/test/migrations/1749664623372_user.ts +13 -13
  198. package/tsconfig.json +9 -0
  199. package/vitest.config.ts +4 -4
  200. package/dist/PostgresKyselyMigrator-CfytARcA.cjs.map +0 -1
  201. package/dist/PostgresKyselyMigrator-upT-hmrz.mjs.map +0 -1
  202. package/dist/PostgresMigrator-DFcNdCvD.cjs.map +0 -1
  203. package/dist/PostgresMigrator-DbuJGAVy.mjs.map +0 -1
  204. package/dist/PostgresObjectionMigrator-BG6ymgnt.cjs.map +0 -1
  205. package/dist/PostgresObjectionMigrator-DPj2pOpX.mjs.map +0 -1
  206. package/dist/VitestKyselyTransactionIsolator-CNURW8y6.mjs.map +0 -1
  207. package/dist/VitestKyselyTransactionIsolator-EvDLk5zg.cjs.map +0 -1
  208. package/dist/VitestObjectionTransactionIsolator-CM5KTAFA.cjs.map +0 -1
  209. package/dist/VitestObjectionTransactionIsolator-jQFaCz0u.mjs.map +0 -1
@@ -1,610 +1,610 @@
1
1
  import { it as base, beforeAll, describe, expect } from 'vitest';
2
2
  import { TEST_DATABASE_CONFIG } from '../../test/globalSetup';
3
- import { type TestDatabase, createTestTables } from '../../test/helpers';
4
- import { KyselyFactory } from '../KyselyFactory';
3
+ import { createTestTables, type TestDatabase } from '../../test/helpers';
5
4
  import { createKyselyDb } from '../helpers';
5
+ import { KyselyFactory } from '../KyselyFactory';
6
6
  import { extendWithFixtures, wrapVitestKyselyTransaction } from '../kysely';
7
7
 
8
8
  const db = () => createKyselyDb<TestDatabase>(TEST_DATABASE_CONFIG);
9
9
  const it = wrapVitestKyselyTransaction<TestDatabase>(base, {
10
- connection: db,
11
- setup: createTestTables,
10
+ connection: db,
11
+ setup: createTestTables,
12
12
  });
13
13
  describe('Testkit Integration Tests', () => {
14
- beforeAll(async () => {});
15
- describe('Complex Factory Scenarios', () => {
16
- it('should handle complex multi-table data creation', async ({ trx }) => {
17
- // Create builders for all entities
18
- const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
19
- 'users',
20
- async () => ({
21
- name: 'John Doe',
22
- email: `user${Date.now()}-${Math.random()}@example.com`,
23
- role: 'user' as const,
24
- createdAt: new Date(),
25
- updatedAt: new Date(),
26
- }),
27
- );
28
-
29
- const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
30
- 'posts',
31
- async ({ attrs, factory }) => {
32
- // Create a user if no userId provided
33
- if (!attrs.userId) {
34
- const user = await factory.insert('user');
35
- return {
36
- title: 'Default Post Title',
37
- content: 'Default post content...',
38
- userId: user.id,
39
- published: false,
40
- createdAt: new Date(),
41
- updatedAt: new Date(),
42
- };
43
- }
44
- return {
45
- title: 'Default Post Title',
46
- content: 'Default post content...',
47
- published: false,
48
- createdAt: new Date(),
49
- updatedAt: new Date(),
50
- };
51
- },
52
- );
53
-
54
- const commentBuilder = KyselyFactory.createBuilder<
55
- TestDatabase,
56
- 'comments'
57
- >('comments', async ({ attrs, factory }) => {
58
- let postId = attrs.postId;
59
- let userId = attrs.userId;
60
-
61
- // Create post if not provided
62
- if (!postId) {
63
- const post = await factory.insert('post');
64
- postId = post.id;
65
- }
66
-
67
- // Create user if not provided
68
- if (!userId) {
69
- const user = await factory.insert('user');
70
- userId = user.id;
71
- }
72
-
73
- return {
74
- content: 'Default comment content',
75
- postId,
76
- userId,
77
- createdAt: new Date(),
78
- };
79
- });
80
-
81
- const builders = {
82
- user: userBuilder,
83
- post: postBuilder,
84
- comment: commentBuilder,
85
- };
86
-
87
- const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
88
- builders,
89
- {},
90
- trx,
91
- );
92
-
93
- // Create a complete blog structure
94
- const author = await factory.insert('user', {
95
- name: 'Jane Author',
96
- email: 'jane@author.com',
97
- role: 'admin',
98
- });
99
-
100
- const posts = await factory.insertMany(3, 'post', (idx) => ({
101
- title: `Post ${idx + 1}`,
102
- content: `Content for post ${idx + 1}`,
103
- userId: author.id,
104
- published: idx < 2, // First two posts are published
105
- }));
106
-
107
- // Create comments on the first post
108
- const comments = await factory.insertMany(5, 'comment', (idx) => ({
109
- content: `Comment ${idx + 1} on first post`,
110
- postId: posts[0].id,
111
- }));
112
-
113
- // Verify the data structure
114
- expect(author.name).toBe('Jane Author');
115
- expect(author.role).toBe('admin');
116
-
117
- expect(posts).toHaveLength(3);
118
- expect(posts[0].title).toBe('Post 1');
119
- expect(posts[0].published).toBe(true);
120
- expect(posts[2].published).toBe(false);
121
-
122
- expect(comments).toHaveLength(5);
123
- comments.forEach((comment, idx) => {
124
- expect(comment.content).toBe(`Comment ${idx + 1} on first post`);
125
- expect(comment.postId).toBe(posts[0].id);
126
- });
127
-
128
- // Verify relationships in database
129
- const authorPosts = await trx
130
- .selectFrom('posts')
131
- .selectAll()
132
- .where('userId', '=', author.id)
133
- .execute();
134
-
135
- expect(authorPosts).toHaveLength(3);
136
-
137
- const firstPostComments = await trx
138
- .selectFrom('comments')
139
- .selectAll()
140
- .where('postId', '=', posts[0].id)
141
- .execute();
142
-
143
- expect(firstPostComments).toHaveLength(5);
144
- });
145
-
146
- it('should handle seeds for complex scenarios', async ({ trx }) => {
147
- const c = await trx
148
- .selectFrom('users')
149
- .select(trx.fn.count('id').as('count'))
150
- .executeTakeFirst();
151
-
152
- const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
153
- 'users',
154
- async () => ({
155
- name: 'Default User',
156
- email: `user${Date.now()}-${Math.random()}@example.com`,
157
- role: 'user' as const,
158
- createdAt: new Date(),
159
- updatedAt: new Date(),
160
- }),
161
- );
162
-
163
- const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
164
- 'posts',
165
- async ({ attrs, factory }) => {
166
- if (!attrs.userId) {
167
- const user = await factory.insert('user');
168
- return {
169
- title: 'Default Post',
170
- content: 'Default content',
171
- userId: user.id,
172
- published: false,
173
- createdAt: new Date(),
174
- updatedAt: new Date(),
175
- };
176
- }
177
- return {
178
- title: 'Default Post',
179
- content: 'Default content',
180
- published: false,
181
- createdAt: new Date(),
182
- updatedAt: new Date(),
183
- };
184
- },
185
- );
186
-
187
- const builders = {
188
- user: userBuilder,
189
- post: postBuilder,
190
- };
191
-
192
- // Create complex seeds
193
- const seeds = {
194
- blogWithAdminAndPosts: KyselyFactory.createSeed(
195
- async ({
196
- attrs,
197
- factory,
198
- }: {
199
- attrs: { postCount?: number };
200
- factory: KyselyFactory<TestDatabase, typeof builders, {}>;
201
- db: any;
202
- }) => {
203
- // Create admin user
204
- const admin = await factory.insert('user', {
205
- name: 'Blog Admin',
206
- email: 'admin@blog.com',
207
- role: 'admin',
208
- });
209
-
210
- // Create multiple posts
211
- const postCount = attrs.postCount || 3;
212
- const posts = await factory.insertMany(
213
- postCount,
214
- 'post',
215
- (idx) => ({
216
- title: `Admin Post ${idx + 1}`,
217
- content: `Content for admin post ${idx + 1}`,
218
- userId: admin.id,
219
- published: true,
220
- }),
221
- );
222
-
223
- return {
224
- admin,
225
- posts,
226
- summary: {
227
- adminId: admin.id,
228
- postIds: posts.map((p) => p.id),
229
- totalPosts: posts.length,
230
- },
231
- };
232
- },
233
- ),
234
-
235
- usersWithPosts: KyselyFactory.createSeed(
236
- async ({
237
- attrs,
238
- factory,
239
- }: {
240
- attrs: { userCount?: number; postsPerUser?: number };
241
- factory: KyselyFactory<TestDatabase, typeof builders, {}>;
242
- db: any;
243
- }) => {
244
- const userCount = attrs.userCount || 2;
245
- const postsPerUser = attrs.postsPerUser || 2;
246
-
247
- const results: Array<{
248
- user: Awaited<ReturnType<typeof builders.user>>;
249
- posts: Awaited<ReturnType<typeof builders.post>>[];
250
- }> = [];
251
-
252
- for (let i = 0; i < userCount; i++) {
253
- const user = await factory.insert('user', {
254
- name: `User ${i + 1}`,
255
- email: `user${i + 1}@example.com`,
256
- });
257
-
258
- const posts = await factory.insertMany(
259
- postsPerUser,
260
- 'post',
261
- (postIdx) => ({
262
- title: `User ${i + 1} Post ${postIdx + 1}`,
263
- content: `Content from user ${i + 1}, post ${postIdx + 1}`,
264
- userId: user.id,
265
- published: postIdx === 0, // Only first post is published
266
- }),
267
- );
268
-
269
- results.push({ user, posts });
270
- }
271
-
272
- return results;
273
- },
274
- ),
275
- };
276
-
277
- const factory = new KyselyFactory<
278
- TestDatabase,
279
- typeof builders,
280
- typeof seeds
281
- >(builders, seeds, trx);
282
-
283
- // Test first seed
284
- const blogData = await factory.seed('blogWithAdminAndPosts', {
285
- postCount: 5,
286
- });
287
-
288
- expect(blogData.admin.name).toBe('Blog Admin');
289
- expect(blogData.admin.role).toBe('admin');
290
- expect(blogData.posts).toHaveLength(5);
291
- expect(blogData.summary.totalPosts).toBe(5);
292
-
293
- // Test second seed
294
- const userData = await factory.seed('usersWithPosts', {
295
- userCount: 3,
296
- postsPerUser: 4,
297
- });
298
-
299
- expect(userData).toHaveLength(3);
300
-
301
- // Verify total counts in database
302
- const totalUsers = await trx
303
- .selectFrom('users')
304
- .select(trx.fn.count('id').as('count'))
305
- .executeTakeFirst();
306
-
307
- const totalPosts = await trx
308
- .selectFrom('posts')
309
- .select(trx.fn.count('id').as('count'))
310
- .executeTakeFirst();
311
-
312
- // 1 admin + 3 users = 4 total users
313
- expect(Number(totalUsers?.count)).toBe(4);
314
- // 5 admin posts + (3 users * 4 posts) = 17 total posts
315
- expect(Number(totalPosts?.count)).toBe(17);
316
- });
317
-
318
- it('should handle transaction isolation properly', async ({ trx }) => {
319
- const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
320
- 'users',
321
- async ({ faker }) => ({
322
- name: 'Test User',
323
- email: faker.internet.email(),
324
- role: 'user' as const,
325
- createdAt: new Date(),
326
- updatedAt: new Date(),
327
- }),
328
- );
329
-
330
- const builders = { user: userBuilder };
331
-
332
- const factory1 = new KyselyFactory<TestDatabase, typeof builders, {}>(
333
- builders,
334
- {},
335
- trx,
336
- );
337
-
338
- // Create user in transaction
339
- const user = await factory1.insert('user', {
340
- name: 'Transaction User',
341
- email: 'transaction@test.com',
342
- });
343
-
344
- // Verify user exists in transaction
345
- const userInTrx = await trx
346
- .selectFrom('users')
347
- .selectAll()
348
- .where('id', '=', user.id)
349
- .executeTakeFirst();
350
-
351
- expect(userInTrx).toBeDefined();
352
- expect(userInTrx?.name).toBe('Transaction User');
353
- });
354
- });
355
-
356
- describe('Performance and Edge Cases', () => {
357
- it('should handle creating many records efficiently', async ({ trx }) => {
358
- const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
359
- 'users',
360
- async ({ faker }) => ({
361
- name: `User ${Math.random()}`,
362
- email: faker.internet.email().toLowerCase(),
363
- role: 'user' as const,
364
- createdAt: new Date(),
365
- updatedAt: new Date(),
366
- }),
367
- );
368
-
369
- const builders = { user: userBuilder };
370
- const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
371
- builders,
372
- {},
373
- trx,
374
- );
375
-
376
- const startTime = Date.now();
377
-
378
- // Create 100 users
379
- const users = await factory.insertMany(100, 'user');
380
-
381
- const endTime = Date.now();
382
- const duration = endTime - startTime;
383
-
384
- expect(users).toHaveLength(100);
385
- expect(duration).toBeLessThan(5000); // Should complete in under 5 seconds
386
-
387
- // Verify all users are unique
388
- const emails = users.map((u) => u.email);
389
- const uniqueEmails = new Set(emails);
390
- expect(uniqueEmails.size).toBe(100);
391
- });
392
-
393
- it('should handle complex attribute generation', async ({ trx }) => {
394
- const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
395
- 'users',
396
- async ({ attrs, faker }) => {
397
- return {
398
- name: `Generated User ${attrs.id}`,
399
- email: faker.internet.email().toLowerCase(),
400
- role: 'user',
401
- };
402
- },
403
- );
404
-
405
- const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
406
- 'posts',
407
- async ({ attrs, factory }) => {
408
- let userId = attrs.userId;
409
- if (!userId) {
410
- const user = await factory.insert('user');
411
- userId = user.id;
412
- }
413
-
414
- return {
415
- title: `Auto-generated Post`,
416
- content: `This is auto-generated content for post. Lorem ipsum dolor sit amet.`,
417
- published: true,
418
- userId,
419
- createdAt: new Date(),
420
- updatedAt: new Date(),
421
- };
422
- },
423
- );
424
-
425
- const builders = {
426
- user: userBuilder,
427
- post: postBuilder,
428
- };
429
-
430
- const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
431
- builders,
432
- {},
433
- trx,
434
- );
435
-
436
- // Create posts which will auto-create users
437
- const posts = await factory.insertMany(10, 'post', (i) => ({
438
- published: i % 2 === 0,
439
- }));
440
-
441
- expect(posts).toHaveLength(10);
442
-
443
- // Check email normalization
444
- const users = await trx.selectFrom('users').selectAll().execute();
445
-
446
- users.forEach((user) => {
447
- expect(user.email).toBe(user.email.toLowerCase());
448
- expect(user.name).not.toMatch(/^\s|\s$/); // No leading/trailing spaces
449
- });
450
-
451
- // Check published pattern
452
- const publishedPosts = posts.filter((p) => p.published);
453
- const unpublishedPosts = posts.filter((p) => !p.published);
454
-
455
- expect(publishedPosts).toHaveLength(5); // Every other post
456
- expect(unpublishedPosts).toHaveLength(5);
457
- });
458
- });
14
+ beforeAll(async () => {});
15
+ describe('Complex Factory Scenarios', () => {
16
+ it('should handle complex multi-table data creation', async ({ trx }) => {
17
+ // Create builders for all entities
18
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
19
+ 'users',
20
+ async () => ({
21
+ name: 'John Doe',
22
+ email: `user${Date.now()}-${Math.random()}@example.com`,
23
+ role: 'user' as const,
24
+ createdAt: new Date(),
25
+ updatedAt: new Date(),
26
+ }),
27
+ );
28
+
29
+ const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
30
+ 'posts',
31
+ async ({ attrs, factory }) => {
32
+ // Create a user if no userId provided
33
+ if (!attrs.userId) {
34
+ const user = await factory.insert('user');
35
+ return {
36
+ title: 'Default Post Title',
37
+ content: 'Default post content...',
38
+ userId: user.id,
39
+ published: false,
40
+ createdAt: new Date(),
41
+ updatedAt: new Date(),
42
+ };
43
+ }
44
+ return {
45
+ title: 'Default Post Title',
46
+ content: 'Default post content...',
47
+ published: false,
48
+ createdAt: new Date(),
49
+ updatedAt: new Date(),
50
+ };
51
+ },
52
+ );
53
+
54
+ const commentBuilder = KyselyFactory.createBuilder<
55
+ TestDatabase,
56
+ 'comments'
57
+ >('comments', async ({ attrs, factory }) => {
58
+ let postId = attrs.postId;
59
+ let userId = attrs.userId;
60
+
61
+ // Create post if not provided
62
+ if (!postId) {
63
+ const post = await factory.insert('post');
64
+ postId = post.id;
65
+ }
66
+
67
+ // Create user if not provided
68
+ if (!userId) {
69
+ const user = await factory.insert('user');
70
+ userId = user.id;
71
+ }
72
+
73
+ return {
74
+ content: 'Default comment content',
75
+ postId,
76
+ userId,
77
+ createdAt: new Date(),
78
+ };
79
+ });
80
+
81
+ const builders = {
82
+ user: userBuilder,
83
+ post: postBuilder,
84
+ comment: commentBuilder,
85
+ };
86
+
87
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
88
+ builders,
89
+ {},
90
+ trx,
91
+ );
92
+
93
+ // Create a complete blog structure
94
+ const author = await factory.insert('user', {
95
+ name: 'Jane Author',
96
+ email: 'jane@author.com',
97
+ role: 'admin',
98
+ });
99
+
100
+ const posts = await factory.insertMany(3, 'post', (idx) => ({
101
+ title: `Post ${idx + 1}`,
102
+ content: `Content for post ${idx + 1}`,
103
+ userId: author.id,
104
+ published: idx < 2, // First two posts are published
105
+ }));
106
+
107
+ // Create comments on the first post
108
+ const comments = await factory.insertMany(5, 'comment', (idx) => ({
109
+ content: `Comment ${idx + 1} on first post`,
110
+ postId: posts[0].id,
111
+ }));
112
+
113
+ // Verify the data structure
114
+ expect(author.name).toBe('Jane Author');
115
+ expect(author.role).toBe('admin');
116
+
117
+ expect(posts).toHaveLength(3);
118
+ expect(posts[0].title).toBe('Post 1');
119
+ expect(posts[0].published).toBe(true);
120
+ expect(posts[2].published).toBe(false);
121
+
122
+ expect(comments).toHaveLength(5);
123
+ comments.forEach((comment, idx) => {
124
+ expect(comment.content).toBe(`Comment ${idx + 1} on first post`);
125
+ expect(comment.postId).toBe(posts[0].id);
126
+ });
127
+
128
+ // Verify relationships in database
129
+ const authorPosts = await trx
130
+ .selectFrom('posts')
131
+ .selectAll()
132
+ .where('userId', '=', author.id)
133
+ .execute();
134
+
135
+ expect(authorPosts).toHaveLength(3);
136
+
137
+ const firstPostComments = await trx
138
+ .selectFrom('comments')
139
+ .selectAll()
140
+ .where('postId', '=', posts[0].id)
141
+ .execute();
142
+
143
+ expect(firstPostComments).toHaveLength(5);
144
+ });
145
+
146
+ it('should handle seeds for complex scenarios', async ({ trx }) => {
147
+ const _c = await trx
148
+ .selectFrom('users')
149
+ .select(trx.fn.count('id').as('count'))
150
+ .executeTakeFirst();
151
+
152
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
153
+ 'users',
154
+ async () => ({
155
+ name: 'Default User',
156
+ email: `user${Date.now()}-${Math.random()}@example.com`,
157
+ role: 'user' as const,
158
+ createdAt: new Date(),
159
+ updatedAt: new Date(),
160
+ }),
161
+ );
162
+
163
+ const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
164
+ 'posts',
165
+ async ({ attrs, factory }) => {
166
+ if (!attrs.userId) {
167
+ const user = await factory.insert('user');
168
+ return {
169
+ title: 'Default Post',
170
+ content: 'Default content',
171
+ userId: user.id,
172
+ published: false,
173
+ createdAt: new Date(),
174
+ updatedAt: new Date(),
175
+ };
176
+ }
177
+ return {
178
+ title: 'Default Post',
179
+ content: 'Default content',
180
+ published: false,
181
+ createdAt: new Date(),
182
+ updatedAt: new Date(),
183
+ };
184
+ },
185
+ );
186
+
187
+ const builders = {
188
+ user: userBuilder,
189
+ post: postBuilder,
190
+ };
191
+
192
+ // Create complex seeds
193
+ const seeds = {
194
+ blogWithAdminAndPosts: KyselyFactory.createSeed(
195
+ async ({
196
+ attrs,
197
+ factory,
198
+ }: {
199
+ attrs: { postCount?: number };
200
+ factory: KyselyFactory<TestDatabase, typeof builders, {}>;
201
+ db: any;
202
+ }) => {
203
+ // Create admin user
204
+ const admin = await factory.insert('user', {
205
+ name: 'Blog Admin',
206
+ email: 'admin@blog.com',
207
+ role: 'admin',
208
+ });
209
+
210
+ // Create multiple posts
211
+ const postCount = attrs.postCount || 3;
212
+ const posts = await factory.insertMany(
213
+ postCount,
214
+ 'post',
215
+ (idx) => ({
216
+ title: `Admin Post ${idx + 1}`,
217
+ content: `Content for admin post ${idx + 1}`,
218
+ userId: admin.id,
219
+ published: true,
220
+ }),
221
+ );
222
+
223
+ return {
224
+ admin,
225
+ posts,
226
+ summary: {
227
+ adminId: admin.id,
228
+ postIds: posts.map((p) => p.id),
229
+ totalPosts: posts.length,
230
+ },
231
+ };
232
+ },
233
+ ),
234
+
235
+ usersWithPosts: KyselyFactory.createSeed(
236
+ async ({
237
+ attrs,
238
+ factory,
239
+ }: {
240
+ attrs: { userCount?: number; postsPerUser?: number };
241
+ factory: KyselyFactory<TestDatabase, typeof builders, {}>;
242
+ db: any;
243
+ }) => {
244
+ const userCount = attrs.userCount || 2;
245
+ const postsPerUser = attrs.postsPerUser || 2;
246
+
247
+ const results: Array<{
248
+ user: Awaited<ReturnType<typeof builders.user>>;
249
+ posts: Awaited<ReturnType<typeof builders.post>>[];
250
+ }> = [];
251
+
252
+ for (let i = 0; i < userCount; i++) {
253
+ const user = await factory.insert('user', {
254
+ name: `User ${i + 1}`,
255
+ email: `user${i + 1}@example.com`,
256
+ });
257
+
258
+ const posts = await factory.insertMany(
259
+ postsPerUser,
260
+ 'post',
261
+ (postIdx) => ({
262
+ title: `User ${i + 1} Post ${postIdx + 1}`,
263
+ content: `Content from user ${i + 1}, post ${postIdx + 1}`,
264
+ userId: user.id,
265
+ published: postIdx === 0, // Only first post is published
266
+ }),
267
+ );
268
+
269
+ results.push({ user, posts });
270
+ }
271
+
272
+ return results;
273
+ },
274
+ ),
275
+ };
276
+
277
+ const factory = new KyselyFactory<
278
+ TestDatabase,
279
+ typeof builders,
280
+ typeof seeds
281
+ >(builders, seeds, trx);
282
+
283
+ // Test first seed
284
+ const blogData = await factory.seed('blogWithAdminAndPosts', {
285
+ postCount: 5,
286
+ });
287
+
288
+ expect(blogData.admin.name).toBe('Blog Admin');
289
+ expect(blogData.admin.role).toBe('admin');
290
+ expect(blogData.posts).toHaveLength(5);
291
+ expect(blogData.summary.totalPosts).toBe(5);
292
+
293
+ // Test second seed
294
+ const userData = await factory.seed('usersWithPosts', {
295
+ userCount: 3,
296
+ postsPerUser: 4,
297
+ });
298
+
299
+ expect(userData).toHaveLength(3);
300
+
301
+ // Verify total counts in database
302
+ const totalUsers = await trx
303
+ .selectFrom('users')
304
+ .select(trx.fn.count('id').as('count'))
305
+ .executeTakeFirst();
306
+
307
+ const totalPosts = await trx
308
+ .selectFrom('posts')
309
+ .select(trx.fn.count('id').as('count'))
310
+ .executeTakeFirst();
311
+
312
+ // 1 admin + 3 users = 4 total users
313
+ expect(Number(totalUsers?.count)).toBe(4);
314
+ // 5 admin posts + (3 users * 4 posts) = 17 total posts
315
+ expect(Number(totalPosts?.count)).toBe(17);
316
+ });
317
+
318
+ it('should handle transaction isolation properly', async ({ trx }) => {
319
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
320
+ 'users',
321
+ async ({ faker }) => ({
322
+ name: 'Test User',
323
+ email: faker.internet.email(),
324
+ role: 'user' as const,
325
+ createdAt: new Date(),
326
+ updatedAt: new Date(),
327
+ }),
328
+ );
329
+
330
+ const builders = { user: userBuilder };
331
+
332
+ const factory1 = new KyselyFactory<TestDatabase, typeof builders, {}>(
333
+ builders,
334
+ {},
335
+ trx,
336
+ );
337
+
338
+ // Create user in transaction
339
+ const user = await factory1.insert('user', {
340
+ name: 'Transaction User',
341
+ email: 'transaction@test.com',
342
+ });
343
+
344
+ // Verify user exists in transaction
345
+ const userInTrx = await trx
346
+ .selectFrom('users')
347
+ .selectAll()
348
+ .where('id', '=', user.id)
349
+ .executeTakeFirst();
350
+
351
+ expect(userInTrx).toBeDefined();
352
+ expect(userInTrx?.name).toBe('Transaction User');
353
+ });
354
+ });
355
+
356
+ describe('Performance and Edge Cases', () => {
357
+ it('should handle creating many records efficiently', async ({ trx }) => {
358
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
359
+ 'users',
360
+ async ({ faker }) => ({
361
+ name: `User ${Math.random()}`,
362
+ email: faker.internet.email().toLowerCase(),
363
+ role: 'user' as const,
364
+ createdAt: new Date(),
365
+ updatedAt: new Date(),
366
+ }),
367
+ );
368
+
369
+ const builders = { user: userBuilder };
370
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
371
+ builders,
372
+ {},
373
+ trx,
374
+ );
375
+
376
+ const startTime = Date.now();
377
+
378
+ // Create 100 users
379
+ const users = await factory.insertMany(100, 'user');
380
+
381
+ const endTime = Date.now();
382
+ const duration = endTime - startTime;
383
+
384
+ expect(users).toHaveLength(100);
385
+ expect(duration).toBeLessThan(5000); // Should complete in under 5 seconds
386
+
387
+ // Verify all users are unique
388
+ const emails = users.map((u) => u.email);
389
+ const uniqueEmails = new Set(emails);
390
+ expect(uniqueEmails.size).toBe(100);
391
+ });
392
+
393
+ it('should handle complex attribute generation', async ({ trx }) => {
394
+ const userBuilder = KyselyFactory.createBuilder<TestDatabase, 'users'>(
395
+ 'users',
396
+ async ({ attrs, faker }) => {
397
+ return {
398
+ name: `Generated User ${attrs.id}`,
399
+ email: faker.internet.email().toLowerCase(),
400
+ role: 'user',
401
+ };
402
+ },
403
+ );
404
+
405
+ const postBuilder = KyselyFactory.createBuilder<TestDatabase, 'posts'>(
406
+ 'posts',
407
+ async ({ attrs, factory }) => {
408
+ let userId = attrs.userId;
409
+ if (!userId) {
410
+ const user = await factory.insert('user');
411
+ userId = user.id;
412
+ }
413
+
414
+ return {
415
+ title: `Auto-generated Post`,
416
+ content: `This is auto-generated content for post. Lorem ipsum dolor sit amet.`,
417
+ published: true,
418
+ userId,
419
+ createdAt: new Date(),
420
+ updatedAt: new Date(),
421
+ };
422
+ },
423
+ );
424
+
425
+ const builders = {
426
+ user: userBuilder,
427
+ post: postBuilder,
428
+ };
429
+
430
+ const factory = new KyselyFactory<TestDatabase, typeof builders, {}>(
431
+ builders,
432
+ {},
433
+ trx,
434
+ );
435
+
436
+ // Create posts which will auto-create users
437
+ const posts = await factory.insertMany(10, 'post', (i) => ({
438
+ published: i % 2 === 0,
439
+ }));
440
+
441
+ expect(posts).toHaveLength(10);
442
+
443
+ // Check email normalization
444
+ const users = await trx.selectFrom('users').selectAll().execute();
445
+
446
+ users.forEach((user) => {
447
+ expect(user.email).toBe(user.email.toLowerCase());
448
+ expect(user.name).not.toMatch(/^\s|\s$/); // No leading/trailing spaces
449
+ });
450
+
451
+ // Check published pattern
452
+ const publishedPosts = posts.filter((p) => p.published);
453
+ const unpublishedPosts = posts.filter((p) => !p.published);
454
+
455
+ expect(publishedPosts).toHaveLength(5); // Every other post
456
+ expect(unpublishedPosts).toHaveLength(5);
457
+ });
458
+ });
459
459
  });
460
460
 
461
461
  describe('extendWithFixtures', () => {
462
- // Create builders for use in extended fixtures
463
- const builders = {
464
- user: KyselyFactory.createBuilder<TestDatabase, 'users'>(
465
- 'users',
466
- ({ faker }) => ({
467
- name: faker.person.fullName(),
468
- email: faker.internet.email().toLowerCase(),
469
- role: 'user' as const,
470
- createdAt: new Date(),
471
- updatedAt: new Date(),
472
- }),
473
- ),
474
- post: KyselyFactory.createBuilder<TestDatabase, 'posts'>(
475
- 'posts',
476
- async ({ attrs, factory, faker }) => {
477
- const userId = attrs.userId ?? (await factory.insert('user')).id;
478
- return {
479
- title: faker.lorem.sentence(),
480
- content: faker.lorem.paragraphs(),
481
- userId,
482
- published: false,
483
- createdAt: new Date(),
484
- updatedAt: new Date(),
485
- };
486
- },
487
- ),
488
- };
489
-
490
- // Create base test with transaction
491
- const baseTest = wrapVitestKyselyTransaction<TestDatabase>(base, {
492
- connection: db,
493
- setup: createTestTables,
494
- });
495
-
496
- // Extend with factory fixture
497
- const itWithFactory = extendWithFixtures<
498
- TestDatabase,
499
- { factory: KyselyFactory<TestDatabase, typeof builders, {}> }
500
- >(baseTest, {
501
- factory: (trx) => new KyselyFactory(builders, {}, trx),
502
- });
503
-
504
- itWithFactory(
505
- 'should provide factory fixture alongside trx',
506
- async ({ trx, factory }) => {
507
- // Both trx and factory should be available
508
- expect(trx).toBeDefined();
509
- expect(factory).toBeDefined();
510
- expect(factory).toBeInstanceOf(KyselyFactory);
511
-
512
- // Factory should work with the transaction
513
- const user = await factory.insert('user', { name: 'Test User' });
514
- expect(user.id).toBeDefined();
515
- expect(user.name).toBe('Test User');
516
-
517
- // Verify user exists in transaction
518
- const found = await trx
519
- .selectFrom('users')
520
- .where('id', '=', user.id)
521
- .selectAll()
522
- .executeTakeFirst();
523
-
524
- expect(found).toBeDefined();
525
- expect(found?.name).toBe('Test User');
526
- },
527
- );
528
-
529
- itWithFactory(
530
- 'should allow factory to create related records',
531
- async ({ factory }) => {
532
- // Create user first
533
- const user = await factory.insert('user', {
534
- name: 'Author',
535
- email: 'author@example.com',
536
- });
537
-
538
- // Create posts for the user
539
- const posts = await factory.insertMany(3, 'post', (idx: number) => ({
540
- title: `Post ${idx + 1}`,
541
- userId: user.id,
542
- published: idx === 0,
543
- }));
544
-
545
- expect(posts).toHaveLength(3);
546
- expect(posts[0].userId).toBe(user.id);
547
- expect(posts[0].published).toBe(true);
548
- expect(posts[1].published).toBe(false);
549
- },
550
- );
551
-
552
- // Test with multiple fixtures
553
- const itWithMultipleFixtures = extendWithFixtures<
554
- TestDatabase,
555
- {
556
- factory: KyselyFactory<TestDatabase, typeof builders, {}>;
557
- userCount: number;
558
- }
559
- >(baseTest, {
560
- factory: (trx) => new KyselyFactory(builders, {}, trx),
561
- userCount: () => 42, // Simple fixture that doesn't use trx
562
- });
563
-
564
- itWithMultipleFixtures(
565
- 'should support multiple fixtures',
566
- async ({ trx, factory, userCount }) => {
567
- expect(trx).toBeDefined();
568
- expect(factory).toBeInstanceOf(KyselyFactory);
569
- expect(userCount).toBe(42);
570
-
571
- // Use the factory
572
- const user = await factory.insert('user');
573
- expect(user.id).toBeDefined();
574
- },
575
- );
576
-
577
- // Test async fixture creators
578
- const itWithAsyncFixture = extendWithFixtures<
579
- TestDatabase,
580
- { initialUser: Awaited<ReturnType<typeof builders.user>> }
581
- >(baseTest, {
582
- initialUser: async (trx) => {
583
- // Create a user directly in the fixture
584
- const factory = new KyselyFactory(builders, {}, trx);
585
- return factory.insert('user', {
586
- name: 'Initial User',
587
- email: 'initial@example.com',
588
- });
589
- },
590
- });
591
-
592
- itWithAsyncFixture(
593
- 'should support async fixture creators',
594
- async ({ trx, initialUser }) => {
595
- expect(initialUser).toBeDefined();
596
- expect(initialUser.name).toBe('Initial User');
597
- expect(initialUser.email).toBe('initial@example.com');
598
-
599
- // Verify user exists in database
600
- const found = await trx
601
- .selectFrom('users')
602
- .where('id', '=', initialUser.id)
603
- .selectAll()
604
- .executeTakeFirst();
605
-
606
- expect(found).toBeDefined();
607
- expect(found?.id).toBe(initialUser.id);
608
- },
609
- );
462
+ // Create builders for use in extended fixtures
463
+ const builders = {
464
+ user: KyselyFactory.createBuilder<TestDatabase, 'users'>(
465
+ 'users',
466
+ ({ faker }) => ({
467
+ name: faker.person.fullName(),
468
+ email: faker.internet.email().toLowerCase(),
469
+ role: 'user' as const,
470
+ createdAt: new Date(),
471
+ updatedAt: new Date(),
472
+ }),
473
+ ),
474
+ post: KyselyFactory.createBuilder<TestDatabase, 'posts'>(
475
+ 'posts',
476
+ async ({ attrs, factory, faker }) => {
477
+ const userId = attrs.userId ?? (await factory.insert('user')).id;
478
+ return {
479
+ title: faker.lorem.sentence(),
480
+ content: faker.lorem.paragraphs(),
481
+ userId,
482
+ published: false,
483
+ createdAt: new Date(),
484
+ updatedAt: new Date(),
485
+ };
486
+ },
487
+ ),
488
+ };
489
+
490
+ // Create base test with transaction
491
+ const baseTest = wrapVitestKyselyTransaction<TestDatabase>(base, {
492
+ connection: db,
493
+ setup: createTestTables,
494
+ });
495
+
496
+ // Extend with factory fixture
497
+ const itWithFactory = extendWithFixtures<
498
+ TestDatabase,
499
+ { factory: KyselyFactory<TestDatabase, typeof builders, {}> }
500
+ >(baseTest, {
501
+ factory: (trx) => new KyselyFactory(builders, {}, trx),
502
+ });
503
+
504
+ itWithFactory(
505
+ 'should provide factory fixture alongside trx',
506
+ async ({ trx, factory }) => {
507
+ // Both trx and factory should be available
508
+ expect(trx).toBeDefined();
509
+ expect(factory).toBeDefined();
510
+ expect(factory).toBeInstanceOf(KyselyFactory);
511
+
512
+ // Factory should work with the transaction
513
+ const user = await factory.insert('user', { name: 'Test User' });
514
+ expect(user.id).toBeDefined();
515
+ expect(user.name).toBe('Test User');
516
+
517
+ // Verify user exists in transaction
518
+ const found = await trx
519
+ .selectFrom('users')
520
+ .where('id', '=', user.id)
521
+ .selectAll()
522
+ .executeTakeFirst();
523
+
524
+ expect(found).toBeDefined();
525
+ expect(found?.name).toBe('Test User');
526
+ },
527
+ );
528
+
529
+ itWithFactory(
530
+ 'should allow factory to create related records',
531
+ async ({ factory }) => {
532
+ // Create user first
533
+ const user = await factory.insert('user', {
534
+ name: 'Author',
535
+ email: 'author@example.com',
536
+ });
537
+
538
+ // Create posts for the user
539
+ const posts = await factory.insertMany(3, 'post', (idx: number) => ({
540
+ title: `Post ${idx + 1}`,
541
+ userId: user.id,
542
+ published: idx === 0,
543
+ }));
544
+
545
+ expect(posts).toHaveLength(3);
546
+ expect(posts[0].userId).toBe(user.id);
547
+ expect(posts[0].published).toBe(true);
548
+ expect(posts[1].published).toBe(false);
549
+ },
550
+ );
551
+
552
+ // Test with multiple fixtures
553
+ const itWithMultipleFixtures = extendWithFixtures<
554
+ TestDatabase,
555
+ {
556
+ factory: KyselyFactory<TestDatabase, typeof builders, {}>;
557
+ userCount: number;
558
+ }
559
+ >(baseTest, {
560
+ factory: (trx) => new KyselyFactory(builders, {}, trx),
561
+ userCount: () => 42, // Simple fixture that doesn't use trx
562
+ });
563
+
564
+ itWithMultipleFixtures(
565
+ 'should support multiple fixtures',
566
+ async ({ trx, factory, userCount }) => {
567
+ expect(trx).toBeDefined();
568
+ expect(factory).toBeInstanceOf(KyselyFactory);
569
+ expect(userCount).toBe(42);
570
+
571
+ // Use the factory
572
+ const user = await factory.insert('user');
573
+ expect(user.id).toBeDefined();
574
+ },
575
+ );
576
+
577
+ // Test async fixture creators
578
+ const itWithAsyncFixture = extendWithFixtures<
579
+ TestDatabase,
580
+ { initialUser: Awaited<ReturnType<typeof builders.user>> }
581
+ >(baseTest, {
582
+ initialUser: async (trx) => {
583
+ // Create a user directly in the fixture
584
+ const factory = new KyselyFactory(builders, {}, trx);
585
+ return factory.insert('user', {
586
+ name: 'Initial User',
587
+ email: 'initial@example.com',
588
+ });
589
+ },
590
+ });
591
+
592
+ itWithAsyncFixture(
593
+ 'should support async fixture creators',
594
+ async ({ trx, initialUser }) => {
595
+ expect(initialUser).toBeDefined();
596
+ expect(initialUser.name).toBe('Initial User');
597
+ expect(initialUser.email).toBe('initial@example.com');
598
+
599
+ // Verify user exists in database
600
+ const found = await trx
601
+ .selectFrom('users')
602
+ .where('id', '=', initialUser.id)
603
+ .selectAll()
604
+ .executeTakeFirst();
605
+
606
+ expect(found).toBeDefined();
607
+ expect(found?.id).toBe(initialUser.id);
608
+ },
609
+ );
610
610
  });