@fragno-dev/test 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -1,8 +1,4 @@
1
- import { Kysely } from "kysely";
2
- import { SQLocalKysely } from "sqlocal/kysely";
3
- import { KyselyAdapter } from "@fragno-dev/db/adapters/kysely";
4
1
  import type { AnySchema } from "@fragno-dev/db/schema";
5
- import type { DatabaseAdapter } from "@fragno-dev/db/adapters";
6
2
  import {
7
3
  createFragmentForTest,
8
4
  type FragmentForTest,
@@ -10,6 +6,14 @@ import {
10
6
  } from "@fragno-dev/core/test";
11
7
  import type { FragnoPublicConfig } from "@fragno-dev/core/api/fragment-instantiation";
12
8
  import type { FragmentDefinition } from "@fragno-dev/core/api/fragment-builder";
9
+ import {
10
+ createAdapter,
11
+ type SupportedAdapter,
12
+ type TestContext,
13
+ type KyselySqliteAdapter,
14
+ type KyselyPgliteAdapter,
15
+ type DrizzlePgliteAdapter,
16
+ } from "./adapters";
13
17
 
14
18
  // Re-export utilities from @fragno-dev/core/test
15
19
  export {
@@ -21,6 +25,15 @@ export {
21
25
  type InitRoutesOverrides,
22
26
  } from "@fragno-dev/core/test";
23
27
 
28
+ // Re-export adapter types
29
+ export type {
30
+ SupportedAdapter,
31
+ KyselySqliteAdapter,
32
+ KyselyPgliteAdapter,
33
+ DrizzlePgliteAdapter,
34
+ TestContext,
35
+ } from "./adapters";
36
+
24
37
  /**
25
38
  * Options for creating a database fragment for testing
26
39
  */
@@ -30,34 +43,48 @@ export interface CreateDatabaseFragmentForTestOptions<
30
43
  TServices,
31
44
  TAdditionalContext extends Record<string, unknown>,
32
45
  TOptions extends FragnoPublicConfig,
46
+ TAdapter extends SupportedAdapter,
33
47
  > extends Omit<
34
48
  CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>,
35
49
  "config"
36
50
  > {
37
- databasePath?: string;
51
+ adapter: TAdapter;
38
52
  migrateToVersion?: number;
39
53
  config?: TConfig;
40
54
  }
41
55
 
42
56
  /**
43
- * Extended fragment test instance with database adapter and Kysely instance
57
+ * Result of creating a database fragment for testing
58
+ * All properties are getters that return the current fragment instance
44
59
  */
45
- export interface DatabaseFragmentForTest<
60
+ export interface DatabaseFragmentTestResult<
46
61
  TConfig,
47
62
  TDeps,
48
63
  TServices,
49
64
  TAdditionalContext extends Record<string, unknown>,
50
65
  TOptions extends FragnoPublicConfig,
51
- > extends FragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions> {
52
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
53
- kysely: Kysely<any>;
54
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
- adapter: DatabaseAdapter<any>;
56
- /**
57
- * Resets the database by creating a fresh in-memory database instance and re-running migrations.
58
- * After calling this, you should re-initialize any routes to ensure they use the new database instance.
59
- */
60
- resetDatabase: () => Promise<void>;
66
+ TAdapter extends SupportedAdapter,
67
+ > {
68
+ readonly fragment: FragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions>;
69
+ readonly services: TServices;
70
+ readonly initRoutes: FragmentForTest<
71
+ TConfig,
72
+ TDeps,
73
+ TServices,
74
+ TAdditionalContext,
75
+ TOptions
76
+ >["initRoutes"];
77
+ readonly handler: FragmentForTest<
78
+ TConfig,
79
+ TDeps,
80
+ TServices,
81
+ TAdditionalContext,
82
+ TOptions
83
+ >["handler"];
84
+ readonly config: TConfig;
85
+ readonly deps: TDeps;
86
+ readonly additionalContext: TAdditionalContext;
87
+ test: TestContext<TAdapter>;
61
88
  }
62
89
 
63
90
  export async function createDatabaseFragmentForTest<
@@ -67,28 +94,32 @@ export async function createDatabaseFragmentForTest<
67
94
  const TAdditionalContext extends Record<string, unknown>,
68
95
  const TOptions extends FragnoPublicConfig,
69
96
  const TSchema extends AnySchema,
97
+ const TAdapter extends SupportedAdapter,
70
98
  >(
71
99
  fragmentBuilder: {
72
100
  definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
73
101
  $requiredOptions: TOptions;
74
102
  },
75
- options?: CreateDatabaseFragmentForTestOptions<
103
+ options: CreateDatabaseFragmentForTestOptions<
76
104
  TConfig,
77
105
  TDeps,
78
106
  TServices,
79
107
  TAdditionalContext,
80
- TOptions
108
+ TOptions,
109
+ TAdapter
81
110
  >,
82
- ): Promise<DatabaseFragmentForTest<TConfig, TDeps, TServices, TAdditionalContext, TOptions>> {
111
+ ): Promise<
112
+ DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>
113
+ > {
83
114
  const {
84
- databasePath = ":memory:",
115
+ adapter: adapterConfig,
85
116
  migrateToVersion,
86
117
  config,
87
118
  options: fragmentOptions,
88
119
  deps,
89
120
  services,
90
121
  additionalContext,
91
- } = options ?? {};
122
+ } = options;
92
123
 
93
124
  // Get schema and namespace from fragment definition's additionalContext
94
125
  // Safe cast: DatabaseFragmentBuilder adds databaseSchema and databaseNamespace to additionalContext
@@ -104,37 +135,13 @@ export async function createDatabaseFragmentForTest<
104
135
  );
105
136
  }
106
137
 
107
- // Helper to create a new database instance and run migrations
108
- const createDatabase = async () => {
109
- // Create SQLocalKysely instance
110
- const { dialect } = new SQLocalKysely(databasePath);
111
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
112
- const kysely = new Kysely<any>({
113
- dialect,
114
- });
115
-
116
- // Create KyselyAdapter
117
- const adapter = new KyselyAdapter({
118
- db: kysely,
119
- provider: "sqlite",
120
- });
121
-
122
- // Run migrations
123
- const migrator = adapter.createMigrationEngine(schema, namespace);
124
- const preparedMigration = migrateToVersion
125
- ? await migrator.prepareMigrationTo(migrateToVersion, {
126
- updateSettings: false,
127
- })
128
- : await migrator.prepareMigration({
129
- updateSettings: false,
130
- });
131
- await preparedMigration.execute();
132
-
133
- return { kysely, adapter };
134
- };
135
-
136
- // Create initial database
137
- let { kysely, adapter } = await createDatabase();
138
+ // Create adapter using the factory
139
+ const { testContext: originalTestContext, adapter } = await createAdapter(
140
+ adapterConfig,
141
+ schema,
142
+ namespace,
143
+ migrateToVersion,
144
+ );
138
145
 
139
146
  // Create fragment with database adapter in options
140
147
  // Safe cast: We're merging the user's options with the databaseAdapter, which is required by TOptions
@@ -155,32 +162,68 @@ export async function createDatabaseFragmentForTest<
155
162
  additionalContext,
156
163
  });
157
164
 
158
- // Reset database function - creates a fresh in-memory database and re-runs migrations
159
- const resetDatabase = async () => {
160
- // Destroy the old Kysely instance
161
- await kysely.destroy();
162
-
163
- // Create a new database instance
164
- const newDb = await createDatabase();
165
- kysely = newDb.kysely;
166
- adapter = newDb.adapter;
165
+ // Wrap resetDatabase to also recreate the fragment with the new adapter
166
+ const originalResetDatabase = originalTestContext.resetDatabase;
167
167
 
168
- // Recreate the fragment with the new adapter
169
- mergedOptions = {
170
- ...fragmentOptions,
171
- databaseAdapter: adapter,
172
- } as unknown as TOptions;
173
-
174
- fragment = createFragmentForTest(fragmentBuilder, {
175
- config: config as TConfig,
176
- options: mergedOptions,
177
- deps,
178
- services,
179
- additionalContext,
180
- });
181
- };
168
+ // Create test context with getters that always return current values
169
+ // We need to cast to any to avoid TypeScript errors when accessing kysely/drizzle properties
170
+ // that may not exist depending on the adapter type
171
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
172
+ const testContext: any = Object.create(originalTestContext, {
173
+ kysely: {
174
+ get() {
175
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
+ return (originalTestContext as any).kysely;
177
+ },
178
+ enumerable: true,
179
+ },
180
+ drizzle: {
181
+ get() {
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ return (originalTestContext as any).drizzle;
184
+ },
185
+ enumerable: true,
186
+ },
187
+ adapter: {
188
+ get() {
189
+ return originalTestContext.adapter;
190
+ },
191
+ enumerable: true,
192
+ },
193
+ resetDatabase: {
194
+ value: async () => {
195
+ // Call the original reset database function
196
+ await originalResetDatabase();
197
+
198
+ // Recreate the fragment with the new adapter (which has been updated by the factory's resetDatabase)
199
+ mergedOptions = {
200
+ ...fragmentOptions,
201
+ databaseAdapter: originalTestContext.adapter,
202
+ } as unknown as TOptions;
203
+
204
+ fragment = createFragmentForTest(fragmentBuilder, {
205
+ config: config as TConfig,
206
+ options: mergedOptions,
207
+ deps,
208
+ services,
209
+ additionalContext,
210
+ });
211
+ },
212
+ enumerable: true,
213
+ },
214
+ cleanup: {
215
+ value: async () => {
216
+ await originalTestContext.cleanup();
217
+ },
218
+ enumerable: true,
219
+ },
220
+ });
182
221
 
222
+ // Return an object with getters for fragment properties so they always reference the current fragment
183
223
  return {
224
+ get fragment() {
225
+ return fragment;
226
+ },
184
227
  get services() {
185
228
  return fragment.services;
186
229
  },
@@ -199,12 +242,6 @@ export async function createDatabaseFragmentForTest<
199
242
  get additionalContext() {
200
243
  return fragment.additionalContext;
201
244
  },
202
- get kysely() {
203
- return kysely;
204
- },
205
- get adapter() {
206
- return adapter;
207
- },
208
- resetDatabase,
245
+ test: testContext,
209
246
  };
210
247
  }