@fragno-dev/test 0.0.0-canary-20251030115355

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 ADDED
@@ -0,0 +1,254 @@
1
+ import type { AnySchema } from "@fragno-dev/db/schema";
2
+ import {
3
+ createFragmentForTest,
4
+ type FragmentForTest,
5
+ type CreateFragmentForTestOptions,
6
+ } from "@fragno-dev/core/test";
7
+ import type { FragnoPublicConfig } from "@fragno-dev/core/api/fragment-instantiation";
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";
17
+
18
+ // Re-export utilities from @fragno-dev/core/test
19
+ export {
20
+ createFragmentForTest,
21
+ type TestResponse,
22
+ type CreateFragmentForTestOptions,
23
+ type RouteHandlerInputOptions,
24
+ type FragmentForTest,
25
+ type InitRoutesOverrides,
26
+ } from "@fragno-dev/core/test";
27
+
28
+ // Re-export adapter types
29
+ export type {
30
+ SupportedAdapter,
31
+ KyselySqliteAdapter,
32
+ KyselyPgliteAdapter,
33
+ DrizzlePgliteAdapter,
34
+ TestContext,
35
+ } from "./adapters";
36
+
37
+ /**
38
+ * Options for creating a database fragment for testing
39
+ */
40
+ export interface CreateDatabaseFragmentForTestOptions<
41
+ TConfig,
42
+ TDeps,
43
+ TServices,
44
+ TAdditionalContext extends Record<string, unknown>,
45
+ TOptions extends FragnoPublicConfig,
46
+ TAdapter extends SupportedAdapter,
47
+ > extends Omit<
48
+ CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>,
49
+ "config"
50
+ > {
51
+ adapter: TAdapter;
52
+ migrateToVersion?: number;
53
+ config?: TConfig;
54
+ }
55
+
56
+ /**
57
+ * Result of creating a database fragment for testing
58
+ * All properties are getters that return the current fragment instance
59
+ */
60
+ export interface DatabaseFragmentTestResult<
61
+ TConfig,
62
+ TDeps,
63
+ TServices,
64
+ TAdditionalContext extends Record<string, unknown>,
65
+ TOptions extends FragnoPublicConfig,
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>;
88
+ }
89
+
90
+ export async function createDatabaseFragmentForTest<
91
+ const TConfig,
92
+ const TDeps,
93
+ const TServices extends Record<string, unknown>,
94
+ const TAdditionalContext extends Record<string, unknown>,
95
+ const TOptions extends FragnoPublicConfig,
96
+ const TSchema extends AnySchema,
97
+ const TAdapter extends SupportedAdapter,
98
+ >(
99
+ fragmentBuilder: {
100
+ definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
101
+ $requiredOptions: TOptions;
102
+ },
103
+ options: CreateDatabaseFragmentForTestOptions<
104
+ TConfig,
105
+ TDeps,
106
+ TServices,
107
+ TAdditionalContext,
108
+ TOptions,
109
+ TAdapter
110
+ >,
111
+ ): Promise<
112
+ DatabaseFragmentTestResult<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TAdapter>
113
+ > {
114
+ const {
115
+ adapter: adapterConfig,
116
+ migrateToVersion,
117
+ config,
118
+ options: fragmentOptions,
119
+ deps,
120
+ services,
121
+ additionalContext,
122
+ } = options;
123
+
124
+ // Get schema and namespace from fragment definition's additionalContext
125
+ // Safe cast: DatabaseFragmentBuilder adds databaseSchema and databaseNamespace to additionalContext
126
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
127
+ const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext as any;
128
+ const schema = fragmentAdditionalContext?.databaseSchema as TSchema | undefined;
129
+ const namespace = (fragmentAdditionalContext?.databaseNamespace as string | undefined) ?? "";
130
+
131
+ if (!schema) {
132
+ throw new Error(
133
+ `Fragment '${fragmentBuilder.definition.name}' does not have a database schema. ` +
134
+ `Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`,
135
+ );
136
+ }
137
+
138
+ // Create adapter using the factory
139
+ const { testContext: originalTestContext, adapter } = await createAdapter(
140
+ adapterConfig,
141
+ schema,
142
+ namespace,
143
+ migrateToVersion,
144
+ );
145
+
146
+ // Create fragment with database adapter in options
147
+ // Safe cast: We're merging the user's options with the databaseAdapter, which is required by TOptions
148
+ // The user's TOptions is constrained to FragnoPublicConfig (or a subtype), which we extend with databaseAdapter
149
+ let mergedOptions = {
150
+ ...fragmentOptions,
151
+ databaseAdapter: adapter,
152
+ } as unknown as TOptions;
153
+
154
+ // Safe cast: If config is not provided, we pass undefined as TConfig.
155
+ // The base createFragmentForTest expects config: TConfig, but if TConfig allows undefined
156
+ // or if the fragment doesn't use config in its dependencies function, this will work correctly.
157
+ let fragment = createFragmentForTest(fragmentBuilder, {
158
+ config: config as TConfig,
159
+ options: mergedOptions,
160
+ deps,
161
+ services,
162
+ additionalContext,
163
+ });
164
+
165
+ // Wrap resetDatabase to also recreate the fragment with the new adapter
166
+ const originalResetDatabase = originalTestContext.resetDatabase;
167
+
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
+ db: {
174
+ get() {
175
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
176
+ return (originalTestContext as any).db;
177
+ },
178
+ enumerable: true,
179
+ },
180
+ kysely: {
181
+ get() {
182
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
183
+ return (originalTestContext as any).kysely;
184
+ },
185
+ enumerable: true,
186
+ },
187
+ drizzle: {
188
+ get() {
189
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
190
+ return (originalTestContext as any).drizzle;
191
+ },
192
+ enumerable: true,
193
+ },
194
+ adapter: {
195
+ get() {
196
+ return originalTestContext.adapter;
197
+ },
198
+ enumerable: true,
199
+ },
200
+ resetDatabase: {
201
+ value: async () => {
202
+ // Call the original reset database function
203
+ await originalResetDatabase();
204
+
205
+ // Recreate the fragment with the new adapter (which has been updated by the factory's resetDatabase)
206
+ mergedOptions = {
207
+ ...fragmentOptions,
208
+ databaseAdapter: originalTestContext.adapter,
209
+ } as unknown as TOptions;
210
+
211
+ fragment = createFragmentForTest(fragmentBuilder, {
212
+ config: config as TConfig,
213
+ options: mergedOptions,
214
+ deps,
215
+ services,
216
+ additionalContext,
217
+ });
218
+ },
219
+ enumerable: true,
220
+ },
221
+ cleanup: {
222
+ value: async () => {
223
+ await originalTestContext.cleanup();
224
+ },
225
+ enumerable: true,
226
+ },
227
+ });
228
+
229
+ // Return an object with getters for fragment properties so they always reference the current fragment
230
+ return {
231
+ get fragment() {
232
+ return fragment;
233
+ },
234
+ get services() {
235
+ return fragment.services;
236
+ },
237
+ get initRoutes() {
238
+ return fragment.initRoutes;
239
+ },
240
+ get handler() {
241
+ return fragment.handler;
242
+ },
243
+ get config() {
244
+ return fragment.config;
245
+ },
246
+ get deps() {
247
+ return fragment.deps;
248
+ },
249
+ get additionalContext() {
250
+ return fragment.additionalContext;
251
+ },
252
+ test: testContext,
253
+ };
254
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "@fragno-private/typescript-config/tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "./dist",
5
+ "rootDir": ".",
6
+ "composite": true
7
+ },
8
+ "include": ["src"],
9
+ "exclude": ["node_modules", "dist"]
10
+ }
@@ -0,0 +1,7 @@
1
+ import { defineConfig } from "tsdown";
2
+
3
+ export default defineConfig({
4
+ entry: ["./src/index.ts"],
5
+ dts: true,
6
+ unbundle: true,
7
+ });
@@ -0,0 +1,4 @@
1
+ import { defineConfig, mergeConfig } from "vitest/config";
2
+ import { baseConfig } from "@fragno-private/vitest-config";
3
+
4
+ export default defineConfig(mergeConfig(baseConfig, {}));