@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.
Files changed (209) hide show
  1. package/dist/Factory-BFVnMMCC.mjs.map +1 -1
  2. package/dist/Factory-BhjUOBWN.cjs.map +1 -1
  3. package/dist/{Factory-c16c27Y6.d.cts → Factory-Bx0AJXZB.d.cts} +3 -2
  4. package/dist/Factory-Bx0AJXZB.d.cts.map +1 -0
  5. package/dist/{Factory-BcGJjLc8.d.mts → Factory-SFupxRC2.d.mts} +2 -1
  6. package/dist/Factory-SFupxRC2.d.mts.map +1 -0
  7. package/dist/Factory.d.cts +2 -2
  8. package/dist/Factory.d.mts +1 -1
  9. package/dist/KyselyFactory-BFqVIn_0.cjs.map +1 -1
  10. package/dist/KyselyFactory-DMswpwji.mjs.map +1 -1
  11. package/dist/{KyselyFactory-uZ45h7YU.d.cts → KyselyFactory-KLeKH43i.d.cts} +4 -3
  12. package/dist/KyselyFactory-KLeKH43i.d.cts.map +1 -0
  13. package/dist/{KyselyFactory-Cj-EultY.d.mts → KyselyFactory-vAxYodck.d.mts} +3 -2
  14. package/dist/KyselyFactory-vAxYodck.d.mts.map +1 -0
  15. package/dist/KyselyFactory.d.cts +3 -3
  16. package/dist/KyselyFactory.d.mts +2 -2
  17. package/dist/{ObjectionFactory-DL4qkuF1.d.mts → ObjectionFactory-BWjB49-i.d.mts} +3 -2
  18. package/dist/ObjectionFactory-BWjB49-i.d.mts.map +1 -0
  19. package/dist/ObjectionFactory-BeFBYcan.cjs.map +1 -1
  20. package/dist/ObjectionFactory-QCJ7u0Ql.mjs.map +1 -1
  21. package/dist/{ObjectionFactory-CdhzKs4f.d.cts → ObjectionFactory-aMGvAKt9.d.cts} +4 -3
  22. package/dist/ObjectionFactory-aMGvAKt9.d.cts.map +1 -0
  23. package/dist/ObjectionFactory.d.cts +3 -3
  24. package/dist/ObjectionFactory.d.mts +2 -2
  25. package/dist/{PostgresKyselyMigrator-upT-hmrz.mjs → PostgresKyselyMigrator-6sE1KOni.mjs} +2 -2
  26. package/dist/PostgresKyselyMigrator-6sE1KOni.mjs.map +1 -0
  27. package/dist/{PostgresKyselyMigrator-CIx3AFSR.d.mts → PostgresKyselyMigrator-CBltSOq5.d.cts} +3 -2
  28. package/dist/PostgresKyselyMigrator-CBltSOq5.d.cts.map +1 -0
  29. package/dist/{PostgresKyselyMigrator-CfytARcA.cjs → PostgresKyselyMigrator-D6IbPq8t.cjs} +2 -2
  30. package/dist/PostgresKyselyMigrator-D6IbPq8t.cjs.map +1 -0
  31. package/dist/{PostgresKyselyMigrator-CQ3aUoy_.d.cts → PostgresKyselyMigrator-DrVWncqd.d.mts} +3 -2
  32. package/dist/PostgresKyselyMigrator-DrVWncqd.d.mts.map +1 -0
  33. package/dist/PostgresKyselyMigrator.cjs +2 -2
  34. package/dist/PostgresKyselyMigrator.d.cts +2 -2
  35. package/dist/PostgresKyselyMigrator.d.mts +2 -2
  36. package/dist/PostgresKyselyMigrator.mjs +2 -2
  37. package/dist/{PostgresMigrator-DbuJGAVy.mjs → PostgresMigrator-BjjenqSd.mjs} +2 -2
  38. package/dist/PostgresMigrator-BjjenqSd.mjs.map +1 -0
  39. package/dist/{PostgresMigrator-D5UkK1_K.d.cts → PostgresMigrator-Bres0U6E.d.cts} +2 -1
  40. package/dist/PostgresMigrator-Bres0U6E.d.cts.map +1 -0
  41. package/dist/{PostgresMigrator-DFcNdCvD.cjs → PostgresMigrator-D6dQn0x2.cjs} +2 -2
  42. package/dist/PostgresMigrator-D6dQn0x2.cjs.map +1 -0
  43. package/dist/{PostgresMigrator-DQaRxoaY.d.mts → PostgresMigrator-S-YYosAC.d.mts} +2 -1
  44. package/dist/PostgresMigrator-S-YYosAC.d.mts.map +1 -0
  45. package/dist/PostgresMigrator.cjs +1 -1
  46. package/dist/PostgresMigrator.d.cts +1 -1
  47. package/dist/PostgresMigrator.d.mts +1 -1
  48. package/dist/PostgresMigrator.mjs +1 -1
  49. package/dist/{PostgresObjectionMigrator-CZHHcCOv.d.cts → PostgresObjectionMigrator-CPfBAP7r.d.cts} +3 -2
  50. package/dist/PostgresObjectionMigrator-CPfBAP7r.d.cts.map +1 -0
  51. package/dist/{PostgresObjectionMigrator-BG6ymgnt.cjs → PostgresObjectionMigrator-DK8ODIHQ.cjs} +2 -2
  52. package/dist/PostgresObjectionMigrator-DK8ODIHQ.cjs.map +1 -0
  53. package/dist/{PostgresObjectionMigrator-D_hCcrQu.d.mts → PostgresObjectionMigrator-DVEqB5tp.d.mts} +3 -2
  54. package/dist/PostgresObjectionMigrator-DVEqB5tp.d.mts.map +1 -0
  55. package/dist/{PostgresObjectionMigrator-DPj2pOpX.mjs → PostgresObjectionMigrator-D_QxXbIN.mjs} +2 -2
  56. package/dist/PostgresObjectionMigrator-D_QxXbIN.mjs.map +1 -0
  57. package/dist/PostgresObjectionMigrator.cjs +2 -2
  58. package/dist/PostgresObjectionMigrator.d.cts +2 -2
  59. package/dist/PostgresObjectionMigrator.d.mts +2 -2
  60. package/dist/PostgresObjectionMigrator.mjs +2 -2
  61. package/dist/{VitestKyselyTransactionIsolator-D3EZZhjZ.d.cts → VitestKyselyTransactionIsolator-CduJlHoT.d.cts} +4 -3
  62. package/dist/VitestKyselyTransactionIsolator-CduJlHoT.d.cts.map +1 -0
  63. package/dist/{VitestKyselyTransactionIsolator-Dxlp1u0f.d.mts → VitestKyselyTransactionIsolator-Cswnnj0k.d.mts} +4 -3
  64. package/dist/VitestKyselyTransactionIsolator-Cswnnj0k.d.mts.map +1 -0
  65. package/dist/{VitestKyselyTransactionIsolator-EvDLk5zg.cjs → VitestKyselyTransactionIsolator-D7RRXOBa.cjs} +2 -2
  66. package/dist/VitestKyselyTransactionIsolator-D7RRXOBa.cjs.map +1 -0
  67. package/dist/{VitestKyselyTransactionIsolator-CNURW8y6.mjs → VitestKyselyTransactionIsolator-DceyIqr4.mjs} +2 -2
  68. package/dist/VitestKyselyTransactionIsolator-DceyIqr4.mjs.map +1 -0
  69. package/dist/VitestKyselyTransactionIsolator.cjs +1 -1
  70. package/dist/VitestKyselyTransactionIsolator.d.cts +2 -2
  71. package/dist/VitestKyselyTransactionIsolator.d.mts +2 -2
  72. package/dist/VitestKyselyTransactionIsolator.mjs +1 -1
  73. package/dist/{VitestObjectionTransactionIsolator-1TpsPqfG.d.cts → VitestObjectionTransactionIsolator-BXoR6xdG.d.cts} +4 -3
  74. package/dist/VitestObjectionTransactionIsolator-BXoR6xdG.d.cts.map +1 -0
  75. package/dist/{VitestObjectionTransactionIsolator-CM5KTAFA.cjs → VitestObjectionTransactionIsolator-CdLRrzNf.cjs} +2 -2
  76. package/dist/VitestObjectionTransactionIsolator-CdLRrzNf.cjs.map +1 -0
  77. package/dist/{VitestObjectionTransactionIsolator-jQFaCz0u.mjs → VitestObjectionTransactionIsolator-OF2osYY5.mjs} +2 -2
  78. package/dist/VitestObjectionTransactionIsolator-OF2osYY5.mjs.map +1 -0
  79. package/dist/{VitestObjectionTransactionIsolator-i9jIgU8Q.d.mts → VitestObjectionTransactionIsolator-x6hY5j4u.d.mts} +4 -3
  80. package/dist/VitestObjectionTransactionIsolator-x6hY5j4u.d.mts.map +1 -0
  81. package/dist/VitestObjectionTransactionIsolator.cjs +1 -1
  82. package/dist/VitestObjectionTransactionIsolator.d.cts +2 -2
  83. package/dist/VitestObjectionTransactionIsolator.d.mts +2 -2
  84. package/dist/VitestObjectionTransactionIsolator.mjs +1 -1
  85. package/dist/{VitestTransactionIsolator-BvR19bYn.d.mts → VitestTransactionIsolator-BNWJqh9f.d.mts} +3 -2
  86. package/dist/VitestTransactionIsolator-BNWJqh9f.d.mts.map +1 -0
  87. package/dist/VitestTransactionIsolator-CMfJXZP8.cjs.map +1 -1
  88. package/dist/{VitestTransactionIsolator-CwQaxZLP.d.cts → VitestTransactionIsolator-CSroc7Df.d.cts} +3 -2
  89. package/dist/VitestTransactionIsolator-CSroc7Df.d.cts.map +1 -0
  90. package/dist/VitestTransactionIsolator-DQ7tLqgV.mjs.map +1 -1
  91. package/dist/VitestTransactionIsolator.d.cts +1 -1
  92. package/dist/VitestTransactionIsolator.d.mts +1 -1
  93. package/dist/aws.cjs.map +1 -1
  94. package/dist/aws.d.cts +2 -0
  95. package/dist/aws.d.cts.map +1 -0
  96. package/dist/aws.d.mts +2 -0
  97. package/dist/aws.d.mts.map +1 -0
  98. package/dist/aws.mjs.map +1 -1
  99. package/dist/benchmark.cjs.map +1 -1
  100. package/dist/benchmark.d.cts +1 -0
  101. package/dist/benchmark.d.cts.map +1 -0
  102. package/dist/benchmark.d.mts +1 -0
  103. package/dist/benchmark.d.mts.map +1 -0
  104. package/dist/benchmark.mjs.map +1 -1
  105. package/dist/better-auth.cjs +29 -30
  106. package/dist/better-auth.cjs.map +1 -1
  107. package/dist/better-auth.d.cts +2 -2
  108. package/dist/better-auth.d.cts.map +1 -0
  109. package/dist/better-auth.d.mts.map +1 -0
  110. package/dist/better-auth.mjs +29 -30
  111. package/dist/better-auth.mjs.map +1 -1
  112. package/dist/directory-B-Ozljzk.mjs.map +1 -1
  113. package/dist/directory-BVC8g7cX.cjs.map +1 -1
  114. package/dist/{directory-BXavAeJZ.d.mts → directory-CVrfTq1I.d.mts} +2 -1
  115. package/dist/directory-CVrfTq1I.d.mts.map +1 -0
  116. package/dist/directory-Cys9g76X.d.cts +13 -0
  117. package/dist/directory-Cys9g76X.d.cts.map +1 -0
  118. package/dist/faker-B14IEMIN.cjs.map +1 -1
  119. package/dist/faker-BGKYFoCT.mjs.map +1 -1
  120. package/dist/{faker-DvxiCtxc.d.cts → faker-BSH1EMtg.d.cts} +3 -3
  121. package/dist/faker-BSH1EMtg.d.cts.map +1 -0
  122. package/dist/faker-DHh7xs4u.d.mts.map +1 -0
  123. package/dist/faker.d.cts +1 -1
  124. package/dist/helpers.cjs.map +1 -1
  125. package/dist/helpers.d.cts +1 -0
  126. package/dist/helpers.d.cts.map +1 -0
  127. package/dist/helpers.d.mts +1 -0
  128. package/dist/helpers.d.mts.map +1 -0
  129. package/dist/helpers.mjs.map +1 -1
  130. package/dist/kysely.cjs +3 -3
  131. package/dist/kysely.cjs.map +1 -1
  132. package/dist/kysely.d.cts +8 -7
  133. package/dist/kysely.d.cts.map +1 -0
  134. package/dist/kysely.d.mts +7 -6
  135. package/dist/kysely.d.mts.map +1 -0
  136. package/dist/kysely.mjs +3 -3
  137. package/dist/kysely.mjs.map +1 -1
  138. package/dist/logger.cjs.map +1 -1
  139. package/dist/logger.d.cts +1 -0
  140. package/dist/logger.d.cts.map +1 -0
  141. package/dist/logger.d.mts +1 -0
  142. package/dist/logger.d.mts.map +1 -0
  143. package/dist/logger.mjs.map +1 -1
  144. package/dist/objection.cjs +3 -3
  145. package/dist/objection.cjs.map +1 -1
  146. package/dist/objection.d.cts +8 -7
  147. package/dist/objection.d.cts.map +1 -0
  148. package/dist/objection.d.mts +7 -6
  149. package/dist/objection.d.mts.map +1 -0
  150. package/dist/objection.mjs +3 -3
  151. package/dist/objection.mjs.map +1 -1
  152. package/dist/os/directory.d.cts +1 -1
  153. package/dist/os/directory.d.mts +1 -1
  154. package/dist/os/index.d.cts +1 -1
  155. package/dist/os/index.d.mts +1 -1
  156. package/dist/timer.cjs.map +1 -1
  157. package/dist/timer.d.cts +2 -0
  158. package/dist/timer.d.cts.map +1 -0
  159. package/dist/timer.d.mts +2 -0
  160. package/dist/timer.d.mts.map +1 -0
  161. package/dist/timer.mjs.map +1 -1
  162. package/package.json +5 -5
  163. package/src/Factory.ts +72 -72
  164. package/src/KyselyFactory.ts +330 -330
  165. package/src/ObjectionFactory.ts +354 -355
  166. package/src/PostgresKyselyMigrator.ts +37 -37
  167. package/src/PostgresMigrator.ts +107 -107
  168. package/src/PostgresObjectionMigrator.ts +91 -91
  169. package/src/VitestKyselyTransactionIsolator.ts +27 -27
  170. package/src/VitestObjectionTransactionIsolator.ts +39 -39
  171. package/src/VitestTransactionIsolator.ts +196 -195
  172. package/src/__tests__/Factory.spec.ts +163 -155
  173. package/src/__tests__/KyselyFactory.spec.ts +443 -439
  174. package/src/__tests__/ObjectionFactory.spec.ts +563 -557
  175. package/src/__tests__/PostgresKyselyMigrator.spec.ts +641 -641
  176. package/src/__tests__/PostgresMigrator.spec.ts +341 -341
  177. package/src/__tests__/PostgresObjectionMigrator.spec.ts +578 -578
  178. package/src/__tests__/VitestObjectionTransactionIsolator.spec.ts +114 -114
  179. package/src/__tests__/benchmark.spec.ts +140 -0
  180. package/src/__tests__/better-auth.spec.ts +15 -15
  181. package/src/__tests__/faker.spec.ts +226 -137
  182. package/src/__tests__/integration.spec.ts +597 -597
  183. package/src/__tests__/utilities.spec.ts +211 -0
  184. package/src/aws.ts +104 -104
  185. package/src/benchmark.ts +12 -12
  186. package/src/better-auth.ts +286 -301
  187. package/src/faker.ts +153 -153
  188. package/src/helpers.ts +6 -6
  189. package/src/kysely.ts +33 -33
  190. package/src/logger.ts +10 -10
  191. package/src/objection.ts +31 -31
  192. package/src/os/directory.ts +11 -10
  193. package/src/timer.ts +1 -1
  194. package/test/globalSetup.ts +45 -45
  195. package/test/helpers.ts +189 -189
  196. package/test/migrations/1749664623372_user.ts +13 -13
  197. package/tsconfig.json +9 -0
  198. package/vitest.config.ts +4 -4
  199. package/dist/PostgresKyselyMigrator-CfytARcA.cjs.map +0 -1
  200. package/dist/PostgresKyselyMigrator-upT-hmrz.mjs.map +0 -1
  201. package/dist/PostgresMigrator-DFcNdCvD.cjs.map +0 -1
  202. package/dist/PostgresMigrator-DbuJGAVy.mjs.map +0 -1
  203. package/dist/PostgresObjectionMigrator-BG6ymgnt.cjs.map +0 -1
  204. package/dist/PostgresObjectionMigrator-DPj2pOpX.mjs.map +0 -1
  205. package/dist/VitestKyselyTransactionIsolator-CNURW8y6.mjs.map +0 -1
  206. package/dist/VitestKyselyTransactionIsolator-EvDLk5zg.cjs.map +0 -1
  207. package/dist/VitestObjectionTransactionIsolator-CM5KTAFA.cjs.map +0 -1
  208. package/dist/VitestObjectionTransactionIsolator-jQFaCz0u.mjs.map +0 -1
  209. package/dist/directory-Mi7tdOuD.d.cts +0 -12
@@ -1,14 +1,14 @@
1
1
  import { Kysely, type MigrationProvider, PostgresDialect, sql } from 'kysely';
2
2
  import { Client, Pool } from 'pg';
3
3
  import {
4
- afterAll,
5
- afterEach,
6
- beforeAll,
7
- beforeEach,
8
- describe,
9
- expect,
10
- it,
11
- vi,
4
+ afterAll,
5
+ afterEach,
6
+ beforeAll,
7
+ beforeEach,
8
+ describe,
9
+ expect,
10
+ it,
11
+ vi,
12
12
  } from 'vitest';
13
13
  import { createTestDatabase } from '../../test/helpers';
14
14
  import { PostgresKyselyMigrator } from '../PostgresKyselyMigrator';
@@ -16,415 +16,415 @@ import { PostgresMigrator } from '../PostgresMigrator';
16
16
 
17
17
  // Test database schema
18
18
  interface TestSchema {
19
- users: {
20
- id: number;
21
- name: string;
22
- email: string;
23
- created_at: Date;
24
- };
25
- posts: {
26
- id: number;
27
- title: string;
28
- content: string;
29
- user_id: number;
30
- created_at: Date;
31
- };
19
+ users: {
20
+ id: number;
21
+ name: string;
22
+ email: string;
23
+ created_at: Date;
24
+ };
25
+ posts: {
26
+ id: number;
27
+ title: string;
28
+ content: string;
29
+ user_id: number;
30
+ created_at: Date;
31
+ };
32
32
  }
33
33
 
34
34
  // Test migration provider
35
35
  class TestMigrationProvider implements MigrationProvider {
36
- private migrations: Record<string, any> = {};
37
- private shouldError = false;
38
-
39
- addMigration(
40
- name: string,
41
- migration: {
42
- up: (db: Kysely<any>) => Promise<void>;
43
- down?: (db: Kysely<any>) => Promise<void>;
44
- },
45
- ) {
46
- this.migrations[name] = migration;
47
- }
48
-
49
- setError(shouldError: boolean) {
50
- this.shouldError = shouldError;
51
- }
52
-
53
- async getMigrations() {
54
- if (this.shouldError) {
55
- throw new Error('Failed to load migrations');
56
- }
57
- return this.migrations;
58
- }
36
+ private migrations: Record<string, any> = {};
37
+ private shouldError = false;
38
+
39
+ addMigration(
40
+ name: string,
41
+ migration: {
42
+ up: (db: Kysely<any>) => Promise<void>;
43
+ down?: (db: Kysely<any>) => Promise<void>;
44
+ },
45
+ ) {
46
+ this.migrations[name] = migration;
47
+ }
48
+
49
+ setError(shouldError: boolean) {
50
+ this.shouldError = shouldError;
51
+ }
52
+
53
+ async getMigrations() {
54
+ if (this.shouldError) {
55
+ throw new Error('Failed to load migrations');
56
+ }
57
+ return this.migrations;
58
+ }
59
59
  }
60
60
 
61
61
  describe('PostgresKyselyMigrator', () => {
62
- let testDbName: string;
63
- let cleanupDb: () => Promise<void>;
64
- let consoleSpy: any;
65
- let consoleErrorSpy: any;
66
-
67
- beforeAll(async () => {
68
- // Create a unique test database for each test run
69
- testDbName = `test_kysely_migrator_${Date.now()}`;
70
- cleanupDb = await createTestDatabase(testDbName);
71
- });
72
-
73
- beforeEach(() => {
74
- consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
75
- consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
76
- });
77
-
78
- afterEach(() => {
79
- consoleSpy.mockRestore();
80
- consoleErrorSpy.mockRestore();
81
- });
82
-
83
- afterAll(async () => {
84
- await cleanupDb();
85
- });
86
-
87
- describe('constructor', () => {
88
- it('should create a PostgresKyselyMigrator instance', () => {
89
- const db = new Kysely<TestSchema>({
90
- dialect: new PostgresDialect({
91
- pool: new Pool({
92
- host: 'localhost',
93
- port: 5432,
94
- user: 'geekmidas',
95
- password: 'geekmidas',
96
- database: testDbName,
97
- }),
98
- }),
99
- });
100
-
101
- const provider = new TestMigrationProvider();
102
- const migrator = new PostgresKyselyMigrator({
103
- uri: `postgresql://geekmidas:geekmidas@localhost:5432/${testDbName}`,
104
- db,
105
- provider,
106
- });
107
-
108
- expect(migrator).toBeInstanceOf(PostgresKyselyMigrator);
109
- expect(migrator).toBeInstanceOf(PostgresMigrator);
110
- });
111
- });
112
-
113
- describe('migrate method', () => {
114
- it('should apply migrations successfully', async () => {
115
- const newDbName = `test_migrate_${Date.now()}`;
116
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${newDbName}`;
117
-
118
- const db = new Kysely<TestSchema>({
119
- dialect: new PostgresDialect({
120
- pool: new Pool({
121
- host: 'localhost',
122
- port: 5432,
123
- user: 'geekmidas',
124
- password: 'geekmidas',
125
- database: newDbName,
126
- }),
127
- }),
128
- });
129
-
130
- const provider = new TestMigrationProvider();
131
-
132
- // Add test migrations
133
- provider.addMigration('001_create_users', {
134
- up: async (db) => {
135
- await db.schema
136
- .createTable('users')
137
- .addColumn('id', 'serial', (col) => col.primaryKey())
138
- .addColumn('name', 'varchar', (col) => col.notNull())
139
- .addColumn('email', 'varchar', (col) => col.notNull().unique())
140
- .addColumn('created_at', 'timestamp', (col) =>
141
- col.defaultTo(sql`now()`).notNull(),
142
- )
143
- .execute();
144
- },
145
- down: async (db) => {
146
- await db.schema.dropTable('users').execute();
147
- },
148
- });
149
-
150
- provider.addMigration('002_create_posts', {
151
- up: async (db) => {
152
- await db.schema
153
- .createTable('posts')
154
- .addColumn('id', 'serial', (col) => col.primaryKey())
155
- .addColumn('title', 'varchar', (col) => col.notNull())
156
- .addColumn('content', 'text', (col) => col.notNull())
157
- .addColumn('user_id', 'integer', (col) =>
158
- col.notNull().references('users.id').onDelete('cascade'),
159
- )
160
- .addColumn('created_at', 'timestamp', (col) =>
161
- col.defaultTo(sql`now()`).notNull(),
162
- )
163
- .execute();
164
- },
165
- down: async (db) => {
166
- await db.schema.dropTable('posts').execute();
167
- },
168
- });
169
-
170
- const migrator = new PostgresKyselyMigrator({
171
- uri,
172
- db,
173
- provider,
174
- });
175
-
176
- // Start the migrator (creates database and runs migrations)
177
- const cleanup = await migrator.start();
178
-
179
- expect(consoleSpy).toHaveBeenCalledWith(
180
- expect.stringContaining('Applied 2 migrations successfully'),
181
- );
182
-
183
- // Verify tables were created
184
- const client = new Client({
185
- host: 'localhost',
186
- port: 5432,
187
- user: 'geekmidas',
188
- password: 'geekmidas',
189
- database: newDbName,
190
- });
191
-
192
- try {
193
- await client.connect();
194
- const tablesResult = await client.query(`
62
+ let testDbName: string;
63
+ let cleanupDb: () => Promise<void>;
64
+ let consoleSpy: any;
65
+ let consoleErrorSpy: any;
66
+
67
+ beforeAll(async () => {
68
+ // Create a unique test database for each test run
69
+ testDbName = `test_kysely_migrator_${Date.now()}`;
70
+ cleanupDb = await createTestDatabase(testDbName);
71
+ });
72
+
73
+ beforeEach(() => {
74
+ consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
75
+ consoleErrorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
76
+ });
77
+
78
+ afterEach(() => {
79
+ consoleSpy.mockRestore();
80
+ consoleErrorSpy.mockRestore();
81
+ });
82
+
83
+ afterAll(async () => {
84
+ await cleanupDb();
85
+ });
86
+
87
+ describe('constructor', () => {
88
+ it('should create a PostgresKyselyMigrator instance', () => {
89
+ const db = new Kysely<TestSchema>({
90
+ dialect: new PostgresDialect({
91
+ pool: new Pool({
92
+ host: 'localhost',
93
+ port: 5432,
94
+ user: 'geekmidas',
95
+ password: 'geekmidas',
96
+ database: testDbName,
97
+ }),
98
+ }),
99
+ });
100
+
101
+ const provider = new TestMigrationProvider();
102
+ const migrator = new PostgresKyselyMigrator({
103
+ uri: `postgresql://geekmidas:geekmidas@localhost:5432/${testDbName}`,
104
+ db,
105
+ provider,
106
+ });
107
+
108
+ expect(migrator).toBeInstanceOf(PostgresKyselyMigrator);
109
+ expect(migrator).toBeInstanceOf(PostgresMigrator);
110
+ });
111
+ });
112
+
113
+ describe('migrate method', () => {
114
+ it('should apply migrations successfully', async () => {
115
+ const newDbName = `test_migrate_${Date.now()}`;
116
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${newDbName}`;
117
+
118
+ const db = new Kysely<TestSchema>({
119
+ dialect: new PostgresDialect({
120
+ pool: new Pool({
121
+ host: 'localhost',
122
+ port: 5432,
123
+ user: 'geekmidas',
124
+ password: 'geekmidas',
125
+ database: newDbName,
126
+ }),
127
+ }),
128
+ });
129
+
130
+ const provider = new TestMigrationProvider();
131
+
132
+ // Add test migrations
133
+ provider.addMigration('001_create_users', {
134
+ up: async (db) => {
135
+ await db.schema
136
+ .createTable('users')
137
+ .addColumn('id', 'serial', (col) => col.primaryKey())
138
+ .addColumn('name', 'varchar', (col) => col.notNull())
139
+ .addColumn('email', 'varchar', (col) => col.notNull().unique())
140
+ .addColumn('created_at', 'timestamp', (col) =>
141
+ col.defaultTo(sql`now()`).notNull(),
142
+ )
143
+ .execute();
144
+ },
145
+ down: async (db) => {
146
+ await db.schema.dropTable('users').execute();
147
+ },
148
+ });
149
+
150
+ provider.addMigration('002_create_posts', {
151
+ up: async (db) => {
152
+ await db.schema
153
+ .createTable('posts')
154
+ .addColumn('id', 'serial', (col) => col.primaryKey())
155
+ .addColumn('title', 'varchar', (col) => col.notNull())
156
+ .addColumn('content', 'text', (col) => col.notNull())
157
+ .addColumn('user_id', 'integer', (col) =>
158
+ col.notNull().references('users.id').onDelete('cascade'),
159
+ )
160
+ .addColumn('created_at', 'timestamp', (col) =>
161
+ col.defaultTo(sql`now()`).notNull(),
162
+ )
163
+ .execute();
164
+ },
165
+ down: async (db) => {
166
+ await db.schema.dropTable('posts').execute();
167
+ },
168
+ });
169
+
170
+ const migrator = new PostgresKyselyMigrator({
171
+ uri,
172
+ db,
173
+ provider,
174
+ });
175
+
176
+ // Start the migrator (creates database and runs migrations)
177
+ const cleanup = await migrator.start();
178
+
179
+ expect(consoleSpy).toHaveBeenCalledWith(
180
+ expect.stringContaining('Applied 2 migrations successfully'),
181
+ );
182
+
183
+ // Verify tables were created
184
+ const client = new Client({
185
+ host: 'localhost',
186
+ port: 5432,
187
+ user: 'geekmidas',
188
+ password: 'geekmidas',
189
+ database: newDbName,
190
+ });
191
+
192
+ try {
193
+ await client.connect();
194
+ const tablesResult = await client.query(`
195
195
  SELECT table_name FROM information_schema.tables
196
196
  WHERE table_schema = 'public'
197
197
  AND table_name IN ('users', 'posts')
198
198
  ORDER BY table_name
199
199
  `);
200
200
 
201
- expect(tablesResult.rowCount).toBe(2);
202
- expect(tablesResult.rows).toEqual([
203
- { table_name: 'posts' },
204
- { table_name: 'users' },
205
- ]);
206
- } finally {
207
- await client.end();
208
- }
209
-
210
- // Cleanup
211
- await cleanup();
212
- });
213
-
214
- it('should handle migration errors', async () => {
215
- const errorDbName = `test_migrate_error_${Date.now()}`;
216
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${errorDbName}`;
217
-
218
- const db = new Kysely<TestSchema>({
219
- dialect: new PostgresDialect({
220
- pool: new Pool({
221
- host: 'localhost',
222
- port: 5432,
223
- user: 'geekmidas',
224
- password: 'geekmidas',
225
- database: errorDbName,
226
- }),
227
- }),
228
- });
229
-
230
- const provider = new TestMigrationProvider();
231
-
232
- // Add a migration that will fail
233
- provider.addMigration('001_failing_migration', {
234
- up: async () => {
235
- throw new Error('Migration failed intentionally');
236
- },
237
- });
238
-
239
- const migrator = new PostgresKyselyMigrator({
240
- uri,
241
- db,
242
- provider,
243
- });
244
-
245
- // Expect the start method to throw
246
- await expect(migrator.start()).rejects.toThrow(
247
- 'Migration failed intentionally',
248
- );
249
-
250
- expect(consoleErrorSpy).toHaveBeenCalledWith(
251
- expect.any(Error),
252
- 'Failed to apply migrations',
253
- );
254
-
255
- // Ensure db is closed before cleanup
256
- await db.destroy();
257
-
258
- // Cleanup the created database
259
- const cleanupClient = new Client({
260
- host: 'localhost',
261
- port: 5432,
262
- user: 'geekmidas',
263
- password: 'geekmidas',
264
- database: 'postgres',
265
- });
266
- try {
267
- await cleanupClient.connect();
268
- // Force disconnect any existing connections
269
- await cleanupClient.query(`
201
+ expect(tablesResult.rowCount).toBe(2);
202
+ expect(tablesResult.rows).toEqual([
203
+ { table_name: 'posts' },
204
+ { table_name: 'users' },
205
+ ]);
206
+ } finally {
207
+ await client.end();
208
+ }
209
+
210
+ // Cleanup
211
+ await cleanup();
212
+ });
213
+
214
+ it('should handle migration errors', async () => {
215
+ const errorDbName = `test_migrate_error_${Date.now()}`;
216
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${errorDbName}`;
217
+
218
+ const db = new Kysely<TestSchema>({
219
+ dialect: new PostgresDialect({
220
+ pool: new Pool({
221
+ host: 'localhost',
222
+ port: 5432,
223
+ user: 'geekmidas',
224
+ password: 'geekmidas',
225
+ database: errorDbName,
226
+ }),
227
+ }),
228
+ });
229
+
230
+ const provider = new TestMigrationProvider();
231
+
232
+ // Add a migration that will fail
233
+ provider.addMigration('001_failing_migration', {
234
+ up: async () => {
235
+ throw new Error('Migration failed intentionally');
236
+ },
237
+ });
238
+
239
+ const migrator = new PostgresKyselyMigrator({
240
+ uri,
241
+ db,
242
+ provider,
243
+ });
244
+
245
+ // Expect the start method to throw
246
+ await expect(migrator.start()).rejects.toThrow(
247
+ 'Migration failed intentionally',
248
+ );
249
+
250
+ expect(consoleErrorSpy).toHaveBeenCalledWith(
251
+ expect.any(Error),
252
+ 'Failed to apply migrations',
253
+ );
254
+
255
+ // Ensure db is closed before cleanup
256
+ await db.destroy();
257
+
258
+ // Cleanup the created database
259
+ const cleanupClient = new Client({
260
+ host: 'localhost',
261
+ port: 5432,
262
+ user: 'geekmidas',
263
+ password: 'geekmidas',
264
+ database: 'postgres',
265
+ });
266
+ try {
267
+ await cleanupClient.connect();
268
+ // Force disconnect any existing connections
269
+ await cleanupClient.query(`
270
270
  SELECT pg_terminate_backend(pg_stat_activity.pid)
271
271
  FROM pg_stat_activity
272
272
  WHERE pg_stat_activity.datname = '${errorDbName}'
273
273
  AND pid <> pg_backend_pid()
274
274
  `);
275
- await cleanupClient.query(`DROP DATABASE IF EXISTS "${errorDbName}"`);
276
- } finally {
277
- await cleanupClient.end();
278
- }
279
- });
280
-
281
- it('should destroy database connection after migrations', async () => {
282
- const destroyDbName = `test_destroy_${Date.now()}`;
283
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${destroyDbName}`;
284
-
285
- const db = new Kysely<TestSchema>({
286
- dialect: new PostgresDialect({
287
- pool: new Pool({
288
- host: 'localhost',
289
- port: 5432,
290
- user: 'geekmidas',
291
- password: 'geekmidas',
292
- database: destroyDbName,
293
- }),
294
- }),
295
- });
296
-
297
- const destroySpy = vi.spyOn(db, 'destroy');
298
- const provider = new TestMigrationProvider();
299
-
300
- provider.addMigration('001_simple', {
301
- up: async (db) => {
302
- await db.schema
303
- .createTable('test')
304
- .addColumn('id', 'serial', (col) => col.primaryKey())
305
- .execute();
306
- },
307
- });
308
-
309
- const migrator = new PostgresKyselyMigrator({
310
- uri,
311
- db,
312
- provider,
313
- });
314
-
315
- const cleanup = await migrator.start();
316
-
317
- // Verify destroy was called after migrations
318
- expect(destroySpy).toHaveBeenCalled();
319
-
320
- // Cleanup
321
- await cleanup();
322
- });
323
- });
324
-
325
- describe('integration with PostgresMigrator', () => {
326
- it('should work with complete workflow', async () => {
327
- const integrationDbName = `test_integration_${Date.now()}`;
328
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${integrationDbName}`;
329
-
330
- const db = new Kysely<TestSchema>({
331
- dialect: new PostgresDialect({
332
- pool: new Pool({
333
- host: 'localhost',
334
- port: 5432,
335
- user: 'geekmidas',
336
- password: 'geekmidas',
337
- database: integrationDbName,
338
- }),
339
- }),
340
- });
341
-
342
- const provider = new TestMigrationProvider();
343
-
344
- // Add comprehensive migrations
345
- provider.addMigration('001_initial_schema', {
346
- up: async (db) => {
347
- // Users table
348
- await db.schema
349
- .createTable('users')
350
- .addColumn('id', 'serial', (col) => col.primaryKey())
351
- .addColumn('name', 'varchar', (col) => col.notNull())
352
- .addColumn('email', 'varchar', (col) => col.notNull().unique())
353
- .addColumn('created_at', 'timestamp', (col) =>
354
- col.defaultTo(sql`now()`).notNull(),
355
- )
356
- .execute();
357
-
358
- // Posts table
359
- await db.schema
360
- .createTable('posts')
361
- .addColumn('id', 'serial', (col) => col.primaryKey())
362
- .addColumn('title', 'varchar', (col) => col.notNull())
363
- .addColumn('content', 'text', (col) => col.notNull())
364
- .addColumn('user_id', 'integer', (col) =>
365
- col.notNull().references('users.id').onDelete('cascade'),
366
- )
367
- .addColumn('created_at', 'timestamp', (col) =>
368
- col.defaultTo(sql`now()`).notNull(),
369
- )
370
- .execute();
371
-
372
- // Add index
373
- await db.schema
374
- .createIndex('idx_posts_user_id')
375
- .on('posts')
376
- .column('user_id')
377
- .execute();
378
- },
379
- });
380
-
381
- provider.addMigration('002_add_updated_at', {
382
- up: async (db) => {
383
- await db.schema
384
- .alterTable('users')
385
- .addColumn('updated_at', 'timestamp', (col) =>
386
- col.defaultTo(sql`now()`).notNull(),
387
- )
388
- .execute();
389
-
390
- await db.schema
391
- .alterTable('posts')
392
- .addColumn('updated_at', 'timestamp', (col) =>
393
- col.defaultTo(sql`now()`).notNull(),
394
- )
395
- .execute();
396
- },
397
- });
398
-
399
- const migrator = new PostgresKyselyMigrator({
400
- uri,
401
- db,
402
- provider,
403
- });
404
-
405
- const cleanup = await migrator.start();
406
-
407
- expect(consoleSpy).toHaveBeenCalledWith(
408
- `Migrating database: ${integrationDbName}`,
409
- );
410
- expect(consoleSpy).toHaveBeenCalledWith(
411
- 'Applied 2 migrations successfully',
412
- );
413
-
414
- // Verify final schema
415
- const verifyClient = new Client({
416
- host: 'localhost',
417
- port: 5432,
418
- user: 'geekmidas',
419
- password: 'geekmidas',
420
- database: integrationDbName,
421
- });
422
-
423
- try {
424
- await verifyClient.connect();
425
-
426
- // Check columns exist
427
- const columnsResult = await verifyClient.query(`
275
+ await cleanupClient.query(`DROP DATABASE IF EXISTS "${errorDbName}"`);
276
+ } finally {
277
+ await cleanupClient.end();
278
+ }
279
+ });
280
+
281
+ it('should destroy database connection after migrations', async () => {
282
+ const destroyDbName = `test_destroy_${Date.now()}`;
283
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${destroyDbName}`;
284
+
285
+ const db = new Kysely<TestSchema>({
286
+ dialect: new PostgresDialect({
287
+ pool: new Pool({
288
+ host: 'localhost',
289
+ port: 5432,
290
+ user: 'geekmidas',
291
+ password: 'geekmidas',
292
+ database: destroyDbName,
293
+ }),
294
+ }),
295
+ });
296
+
297
+ const destroySpy = vi.spyOn(db, 'destroy');
298
+ const provider = new TestMigrationProvider();
299
+
300
+ provider.addMigration('001_simple', {
301
+ up: async (db) => {
302
+ await db.schema
303
+ .createTable('test')
304
+ .addColumn('id', 'serial', (col) => col.primaryKey())
305
+ .execute();
306
+ },
307
+ });
308
+
309
+ const migrator = new PostgresKyselyMigrator({
310
+ uri,
311
+ db,
312
+ provider,
313
+ });
314
+
315
+ const cleanup = await migrator.start();
316
+
317
+ // Verify destroy was called after migrations
318
+ expect(destroySpy).toHaveBeenCalled();
319
+
320
+ // Cleanup
321
+ await cleanup();
322
+ });
323
+ });
324
+
325
+ describe('integration with PostgresMigrator', () => {
326
+ it('should work with complete workflow', async () => {
327
+ const integrationDbName = `test_integration_${Date.now()}`;
328
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${integrationDbName}`;
329
+
330
+ const db = new Kysely<TestSchema>({
331
+ dialect: new PostgresDialect({
332
+ pool: new Pool({
333
+ host: 'localhost',
334
+ port: 5432,
335
+ user: 'geekmidas',
336
+ password: 'geekmidas',
337
+ database: integrationDbName,
338
+ }),
339
+ }),
340
+ });
341
+
342
+ const provider = new TestMigrationProvider();
343
+
344
+ // Add comprehensive migrations
345
+ provider.addMigration('001_initial_schema', {
346
+ up: async (db) => {
347
+ // Users table
348
+ await db.schema
349
+ .createTable('users')
350
+ .addColumn('id', 'serial', (col) => col.primaryKey())
351
+ .addColumn('name', 'varchar', (col) => col.notNull())
352
+ .addColumn('email', 'varchar', (col) => col.notNull().unique())
353
+ .addColumn('created_at', 'timestamp', (col) =>
354
+ col.defaultTo(sql`now()`).notNull(),
355
+ )
356
+ .execute();
357
+
358
+ // Posts table
359
+ await db.schema
360
+ .createTable('posts')
361
+ .addColumn('id', 'serial', (col) => col.primaryKey())
362
+ .addColumn('title', 'varchar', (col) => col.notNull())
363
+ .addColumn('content', 'text', (col) => col.notNull())
364
+ .addColumn('user_id', 'integer', (col) =>
365
+ col.notNull().references('users.id').onDelete('cascade'),
366
+ )
367
+ .addColumn('created_at', 'timestamp', (col) =>
368
+ col.defaultTo(sql`now()`).notNull(),
369
+ )
370
+ .execute();
371
+
372
+ // Add index
373
+ await db.schema
374
+ .createIndex('idx_posts_user_id')
375
+ .on('posts')
376
+ .column('user_id')
377
+ .execute();
378
+ },
379
+ });
380
+
381
+ provider.addMigration('002_add_updated_at', {
382
+ up: async (db) => {
383
+ await db.schema
384
+ .alterTable('users')
385
+ .addColumn('updated_at', 'timestamp', (col) =>
386
+ col.defaultTo(sql`now()`).notNull(),
387
+ )
388
+ .execute();
389
+
390
+ await db.schema
391
+ .alterTable('posts')
392
+ .addColumn('updated_at', 'timestamp', (col) =>
393
+ col.defaultTo(sql`now()`).notNull(),
394
+ )
395
+ .execute();
396
+ },
397
+ });
398
+
399
+ const migrator = new PostgresKyselyMigrator({
400
+ uri,
401
+ db,
402
+ provider,
403
+ });
404
+
405
+ const cleanup = await migrator.start();
406
+
407
+ expect(consoleSpy).toHaveBeenCalledWith(
408
+ `Migrating database: ${integrationDbName}`,
409
+ );
410
+ expect(consoleSpy).toHaveBeenCalledWith(
411
+ 'Applied 2 migrations successfully',
412
+ );
413
+
414
+ // Verify final schema
415
+ const verifyClient = new Client({
416
+ host: 'localhost',
417
+ port: 5432,
418
+ user: 'geekmidas',
419
+ password: 'geekmidas',
420
+ database: integrationDbName,
421
+ });
422
+
423
+ try {
424
+ await verifyClient.connect();
425
+
426
+ // Check columns exist
427
+ const columnsResult = await verifyClient.query(`
428
428
  SELECT table_name, column_name
429
429
  FROM information_schema.columns
430
430
  WHERE table_schema = 'public'
@@ -433,258 +433,258 @@ describe('PostgresKyselyMigrator', () => {
433
433
  ORDER BY table_name
434
434
  `);
435
435
 
436
- expect(columnsResult.rowCount).toBe(2);
436
+ expect(columnsResult.rowCount).toBe(2);
437
437
 
438
- // Check index exists
439
- const indexResult = await verifyClient.query(`
438
+ // Check index exists
439
+ const indexResult = await verifyClient.query(`
440
440
  SELECT indexname FROM pg_indexes
441
441
  WHERE schemaname = 'public'
442
442
  AND indexname = 'idx_posts_user_id'
443
443
  `);
444
444
 
445
- expect(indexResult.rowCount).toBe(1);
446
- } finally {
447
- await verifyClient.end();
448
- }
449
-
450
- // Cleanup
451
- await cleanup();
452
- });
453
-
454
- it('should handle empty migrations', async () => {
455
- const emptyDbName = `test_empty_${Date.now()}`;
456
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${emptyDbName}`;
457
-
458
- const db = new Kysely<TestSchema>({
459
- dialect: new PostgresDialect({
460
- pool: new Pool({
461
- host: 'localhost',
462
- port: 5432,
463
- user: 'geekmidas',
464
- password: 'geekmidas',
465
- database: emptyDbName,
466
- }),
467
- }),
468
- });
469
-
470
- const provider = new TestMigrationProvider();
471
- // No migrations added
472
-
473
- const migrator = new PostgresKyselyMigrator({
474
- uri,
475
- db,
476
- provider,
477
- });
478
-
479
- const cleanup = await migrator.start();
480
-
481
- expect(consoleSpy).toHaveBeenCalledWith(
482
- 'Applied 0 migrations successfully',
483
- );
484
-
485
- await cleanup();
486
- });
487
-
488
- it('should work with FileMigrationProvider pattern', async () => {
489
- const fileProviderDbName = `test_file_provider_${Date.now()}`;
490
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${fileProviderDbName}`;
491
-
492
- const db = new Kysely<TestSchema>({
493
- dialect: new PostgresDialect({
494
- pool: new Pool({
495
- host: 'localhost',
496
- port: 5432,
497
- user: 'geekmidas',
498
- password: 'geekmidas',
499
- database: fileProviderDbName,
500
- }),
501
- }),
502
- });
503
-
504
- // Simulate a file-based migration provider
505
- const migrations = {
506
- '2024_01_01_000001_create_users': {
507
- up: async (db: Kysely<any>) => {
508
- await db.schema
509
- .createTable('users')
510
- .addColumn('id', 'serial', (col) => col.primaryKey())
511
- .addColumn('username', 'varchar', (col) => col.notNull().unique())
512
- .execute();
513
- },
514
- down: async (db: Kysely<any>) => {
515
- await db.schema.dropTable('users').execute();
516
- },
517
- },
518
- '2024_01_02_000001_create_sessions': {
519
- up: async (db: Kysely<any>) => {
520
- await db.schema
521
- .createTable('sessions')
522
- .addColumn('id', 'serial', (col) => col.primaryKey())
523
- .addColumn('user_id', 'integer', (col) =>
524
- col.notNull().references('users.id'),
525
- )
526
- .addColumn('token', 'varchar', (col) => col.notNull())
527
- .execute();
528
- },
529
- down: async (db: Kysely<any>) => {
530
- await db.schema.dropTable('sessions').execute();
531
- },
532
- },
533
- };
534
-
535
- const provider: MigrationProvider = {
536
- async getMigrations() {
537
- return migrations;
538
- },
539
- };
540
-
541
- const migrator = new PostgresKyselyMigrator({
542
- uri,
543
- db,
544
- provider,
545
- });
546
-
547
- const cleanup = await migrator.start();
548
-
549
- expect(consoleSpy).toHaveBeenCalledWith(
550
- 'Applied 2 migrations successfully',
551
- );
552
-
553
- // Verify both tables exist
554
- const verifyClient = new Client({
555
- host: 'localhost',
556
- port: 5432,
557
- user: 'geekmidas',
558
- password: 'geekmidas',
559
- database: fileProviderDbName,
560
- });
561
-
562
- try {
563
- await verifyClient.connect();
564
- const tablesResult = await verifyClient.query(`
445
+ expect(indexResult.rowCount).toBe(1);
446
+ } finally {
447
+ await verifyClient.end();
448
+ }
449
+
450
+ // Cleanup
451
+ await cleanup();
452
+ });
453
+
454
+ it('should handle empty migrations', async () => {
455
+ const emptyDbName = `test_empty_${Date.now()}`;
456
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${emptyDbName}`;
457
+
458
+ const db = new Kysely<TestSchema>({
459
+ dialect: new PostgresDialect({
460
+ pool: new Pool({
461
+ host: 'localhost',
462
+ port: 5432,
463
+ user: 'geekmidas',
464
+ password: 'geekmidas',
465
+ database: emptyDbName,
466
+ }),
467
+ }),
468
+ });
469
+
470
+ const provider = new TestMigrationProvider();
471
+ // No migrations added
472
+
473
+ const migrator = new PostgresKyselyMigrator({
474
+ uri,
475
+ db,
476
+ provider,
477
+ });
478
+
479
+ const cleanup = await migrator.start();
480
+
481
+ expect(consoleSpy).toHaveBeenCalledWith(
482
+ 'Applied 0 migrations successfully',
483
+ );
484
+
485
+ await cleanup();
486
+ });
487
+
488
+ it('should work with FileMigrationProvider pattern', async () => {
489
+ const fileProviderDbName = `test_file_provider_${Date.now()}`;
490
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${fileProviderDbName}`;
491
+
492
+ const db = new Kysely<TestSchema>({
493
+ dialect: new PostgresDialect({
494
+ pool: new Pool({
495
+ host: 'localhost',
496
+ port: 5432,
497
+ user: 'geekmidas',
498
+ password: 'geekmidas',
499
+ database: fileProviderDbName,
500
+ }),
501
+ }),
502
+ });
503
+
504
+ // Simulate a file-based migration provider
505
+ const migrations = {
506
+ '2024_01_01_000001_create_users': {
507
+ up: async (db: Kysely<any>) => {
508
+ await db.schema
509
+ .createTable('users')
510
+ .addColumn('id', 'serial', (col) => col.primaryKey())
511
+ .addColumn('username', 'varchar', (col) => col.notNull().unique())
512
+ .execute();
513
+ },
514
+ down: async (db: Kysely<any>) => {
515
+ await db.schema.dropTable('users').execute();
516
+ },
517
+ },
518
+ '2024_01_02_000001_create_sessions': {
519
+ up: async (db: Kysely<any>) => {
520
+ await db.schema
521
+ .createTable('sessions')
522
+ .addColumn('id', 'serial', (col) => col.primaryKey())
523
+ .addColumn('user_id', 'integer', (col) =>
524
+ col.notNull().references('users.id'),
525
+ )
526
+ .addColumn('token', 'varchar', (col) => col.notNull())
527
+ .execute();
528
+ },
529
+ down: async (db: Kysely<any>) => {
530
+ await db.schema.dropTable('sessions').execute();
531
+ },
532
+ },
533
+ };
534
+
535
+ const provider: MigrationProvider = {
536
+ async getMigrations() {
537
+ return migrations;
538
+ },
539
+ };
540
+
541
+ const migrator = new PostgresKyselyMigrator({
542
+ uri,
543
+ db,
544
+ provider,
545
+ });
546
+
547
+ const cleanup = await migrator.start();
548
+
549
+ expect(consoleSpy).toHaveBeenCalledWith(
550
+ 'Applied 2 migrations successfully',
551
+ );
552
+
553
+ // Verify both tables exist
554
+ const verifyClient = new Client({
555
+ host: 'localhost',
556
+ port: 5432,
557
+ user: 'geekmidas',
558
+ password: 'geekmidas',
559
+ database: fileProviderDbName,
560
+ });
561
+
562
+ try {
563
+ await verifyClient.connect();
564
+ const tablesResult = await verifyClient.query(`
565
565
  SELECT table_name FROM information_schema.tables
566
566
  WHERE table_schema = 'public'
567
567
  AND table_name IN ('users', 'sessions')
568
568
  ORDER BY table_name
569
569
  `);
570
570
 
571
- expect(tablesResult.rowCount).toBe(2);
572
- } finally {
573
- await verifyClient.end();
574
- }
575
-
576
- await cleanup();
577
- });
578
- });
579
-
580
- describe('error scenarios', () => {
581
- it('should handle provider errors', async () => {
582
- const providerErrorDbName = `test_provider_error_${Date.now()}`;
583
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${providerErrorDbName}`;
584
-
585
- const db = new Kysely<TestSchema>({
586
- dialect: new PostgresDialect({
587
- pool: new Pool({
588
- host: 'localhost',
589
- port: 5432,
590
- user: 'geekmidas',
591
- password: 'geekmidas',
592
- database: providerErrorDbName,
593
- }),
594
- }),
595
- });
596
-
597
- const provider = new TestMigrationProvider();
598
- provider.setError(true);
599
-
600
- const migrator = new PostgresKyselyMigrator({
601
- uri,
602
- db,
603
- provider,
604
- });
605
-
606
- await expect(migrator.start()).rejects.toThrow(
607
- 'Failed to load migrations',
608
- );
609
-
610
- // Ensure db is closed
611
- await db.destroy();
612
-
613
- // Cleanup
614
- const cleanupClient = new Client({
615
- host: 'localhost',
616
- port: 5432,
617
- user: 'geekmidas',
618
- password: 'geekmidas',
619
- database: 'postgres',
620
- });
621
- try {
622
- await cleanupClient.connect();
623
- await cleanupClient.query(
624
- `DROP DATABASE IF EXISTS "${providerErrorDbName}"`,
625
- );
626
- } finally {
627
- await cleanupClient.end();
628
- }
629
- }, 10000);
630
-
631
- it('should handle invalid SQL in migrations', async () => {
632
- const invalidSqlDbName = `test_invalid_sql_${Date.now()}`;
633
- const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${invalidSqlDbName}`;
634
-
635
- const db = new Kysely<TestSchema>({
636
- dialect: new PostgresDialect({
637
- pool: new Pool({
638
- host: 'localhost',
639
- port: 5432,
640
- user: 'geekmidas',
641
- password: 'geekmidas',
642
- database: invalidSqlDbName,
643
- }),
644
- }),
645
- });
646
-
647
- const provider = new TestMigrationProvider();
648
- provider.addMigration('001_invalid_sql', {
649
- up: async (db) => {
650
- // Try to reference non-existent table
651
- await db.schema
652
- .createTable('posts')
653
- .addColumn('id', 'serial', (col) => col.primaryKey())
654
- .addColumn('user_id', 'integer', (col) =>
655
- col.notNull().references('non_existent_table.id'),
656
- )
657
- .execute();
658
- },
659
- });
660
-
661
- const migrator = new PostgresKyselyMigrator({
662
- uri,
663
- db,
664
- provider,
665
- });
666
-
667
- await expect(migrator.start()).rejects.toThrow();
668
-
669
- // Ensure db is closed
670
- await db.destroy();
671
-
672
- // Cleanup
673
- const cleanupClient = new Client({
674
- host: 'localhost',
675
- port: 5432,
676
- user: 'geekmidas',
677
- password: 'geekmidas',
678
- database: 'postgres',
679
- });
680
- try {
681
- await cleanupClient.connect();
682
- await cleanupClient.query(
683
- `DROP DATABASE IF EXISTS "${invalidSqlDbName}"`,
684
- );
685
- } finally {
686
- await cleanupClient.end();
687
- }
688
- }, 10000);
689
- });
571
+ expect(tablesResult.rowCount).toBe(2);
572
+ } finally {
573
+ await verifyClient.end();
574
+ }
575
+
576
+ await cleanup();
577
+ });
578
+ });
579
+
580
+ describe('error scenarios', () => {
581
+ it('should handle provider errors', async () => {
582
+ const providerErrorDbName = `test_provider_error_${Date.now()}`;
583
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${providerErrorDbName}`;
584
+
585
+ const db = new Kysely<TestSchema>({
586
+ dialect: new PostgresDialect({
587
+ pool: new Pool({
588
+ host: 'localhost',
589
+ port: 5432,
590
+ user: 'geekmidas',
591
+ password: 'geekmidas',
592
+ database: providerErrorDbName,
593
+ }),
594
+ }),
595
+ });
596
+
597
+ const provider = new TestMigrationProvider();
598
+ provider.setError(true);
599
+
600
+ const migrator = new PostgresKyselyMigrator({
601
+ uri,
602
+ db,
603
+ provider,
604
+ });
605
+
606
+ await expect(migrator.start()).rejects.toThrow(
607
+ 'Failed to load migrations',
608
+ );
609
+
610
+ // Ensure db is closed
611
+ await db.destroy();
612
+
613
+ // Cleanup
614
+ const cleanupClient = new Client({
615
+ host: 'localhost',
616
+ port: 5432,
617
+ user: 'geekmidas',
618
+ password: 'geekmidas',
619
+ database: 'postgres',
620
+ });
621
+ try {
622
+ await cleanupClient.connect();
623
+ await cleanupClient.query(
624
+ `DROP DATABASE IF EXISTS "${providerErrorDbName}"`,
625
+ );
626
+ } finally {
627
+ await cleanupClient.end();
628
+ }
629
+ }, 10000);
630
+
631
+ it('should handle invalid SQL in migrations', async () => {
632
+ const invalidSqlDbName = `test_invalid_sql_${Date.now()}`;
633
+ const uri = `postgresql://geekmidas:geekmidas@localhost:5432/${invalidSqlDbName}`;
634
+
635
+ const db = new Kysely<TestSchema>({
636
+ dialect: new PostgresDialect({
637
+ pool: new Pool({
638
+ host: 'localhost',
639
+ port: 5432,
640
+ user: 'geekmidas',
641
+ password: 'geekmidas',
642
+ database: invalidSqlDbName,
643
+ }),
644
+ }),
645
+ });
646
+
647
+ const provider = new TestMigrationProvider();
648
+ provider.addMigration('001_invalid_sql', {
649
+ up: async (db) => {
650
+ // Try to reference non-existent table
651
+ await db.schema
652
+ .createTable('posts')
653
+ .addColumn('id', 'serial', (col) => col.primaryKey())
654
+ .addColumn('user_id', 'integer', (col) =>
655
+ col.notNull().references('non_existent_table.id'),
656
+ )
657
+ .execute();
658
+ },
659
+ });
660
+
661
+ const migrator = new PostgresKyselyMigrator({
662
+ uri,
663
+ db,
664
+ provider,
665
+ });
666
+
667
+ await expect(migrator.start()).rejects.toThrow();
668
+
669
+ // Ensure db is closed
670
+ await db.destroy();
671
+
672
+ // Cleanup
673
+ const cleanupClient = new Client({
674
+ host: 'localhost',
675
+ port: 5432,
676
+ user: 'geekmidas',
677
+ password: 'geekmidas',
678
+ database: 'postgres',
679
+ });
680
+ try {
681
+ await cleanupClient.connect();
682
+ await cleanupClient.query(
683
+ `DROP DATABASE IF EXISTS "${invalidSqlDbName}"`,
684
+ );
685
+ } finally {
686
+ await cleanupClient.end();
687
+ }
688
+ }, 10000);
689
+ });
690
690
  });