@fragno-dev/test 0.1.11 → 0.1.13
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/.turbo/turbo-build.log +15 -11
- package/CHANGELOG.md +35 -0
- package/dist/adapters.d.ts +3 -11
- package/dist/adapters.d.ts.map +1 -1
- package/dist/adapters.js +65 -50
- package/dist/adapters.js.map +1 -1
- package/dist/db-test.d.ts +129 -0
- package/dist/db-test.d.ts.map +1 -0
- package/dist/db-test.js +214 -0
- package/dist/db-test.js.map +1 -0
- package/dist/index.d.ts +23 -28
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -91
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/src/adapters.ts +137 -118
- package/src/db-test.test.ts +352 -0
- package/src/db-test.ts +574 -0
- package/src/index.test.ts +287 -546
- package/src/index.ts +39 -241
package/dist/db-test.js
ADDED
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { createAdapter } from "./adapters.js";
|
|
2
|
+
|
|
3
|
+
//#region src/db-test.ts
|
|
4
|
+
/**
|
|
5
|
+
* Builder for creating multiple database fragments for testing
|
|
6
|
+
*/
|
|
7
|
+
var DatabaseFragmentsTestBuilder = class {
|
|
8
|
+
#adapter;
|
|
9
|
+
#fragments = /* @__PURE__ */ new Map();
|
|
10
|
+
/**
|
|
11
|
+
* Set the test adapter configuration
|
|
12
|
+
*/
|
|
13
|
+
withTestAdapter(adapter) {
|
|
14
|
+
this.#adapter = adapter;
|
|
15
|
+
return this;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Add a fragment to the test setup
|
|
19
|
+
*
|
|
20
|
+
* @param name - Unique name for the fragment
|
|
21
|
+
* @param builder - Pre-configured instantiation builder
|
|
22
|
+
* @param options - Additional options (optional)
|
|
23
|
+
*/
|
|
24
|
+
withFragment(name, builder, options) {
|
|
25
|
+
this.#fragments.set(name, {
|
|
26
|
+
definition: builder.definition,
|
|
27
|
+
builder,
|
|
28
|
+
migrateToVersion: options?.migrateToVersion
|
|
29
|
+
});
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build the test setup and return fragments and test context
|
|
34
|
+
*/
|
|
35
|
+
async build() {
|
|
36
|
+
if (!this.#adapter) throw new Error("Test adapter must be set using withTestAdapter()");
|
|
37
|
+
if (this.#fragments.size === 0) throw new Error("At least one fragment must be added using withFragment()");
|
|
38
|
+
const adapterConfig = this.#adapter;
|
|
39
|
+
const fragmentNames = Array.from(this.#fragments.keys());
|
|
40
|
+
const fragmentConfigs = Array.from(this.#fragments.values());
|
|
41
|
+
const schemaConfigs = [];
|
|
42
|
+
const builderConfigs = [];
|
|
43
|
+
for (const fragmentConfig of fragmentConfigs) {
|
|
44
|
+
const builder = fragmentConfig.builder;
|
|
45
|
+
const definition = builder.definition;
|
|
46
|
+
let schema;
|
|
47
|
+
const namespace = definition.name + "-db";
|
|
48
|
+
if (definition.dependencies) try {
|
|
49
|
+
const mockAdapter = {
|
|
50
|
+
createQueryEngine: () => ({ schema: null }),
|
|
51
|
+
contextStorage: { run: (_data, fn) => fn() }
|
|
52
|
+
};
|
|
53
|
+
const actualConfig = builder.config ?? {};
|
|
54
|
+
const deps = definition.dependencies({
|
|
55
|
+
config: actualConfig,
|
|
56
|
+
options: { databaseAdapter: mockAdapter }
|
|
57
|
+
});
|
|
58
|
+
if (deps && typeof deps === "object" && "schema" in deps) schema = deps.schema;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
const errorMessage = error instanceof Error ? error.message : typeof error === "string" ? error : "Unknown error";
|
|
61
|
+
throw new Error(`Failed to extract schema from fragment '${definition.name}'.\nOriginal error: ${errorMessage}\n\nMake sure the fragment is a database fragment using defineFragment().extend(withDatabase(schema)).`);
|
|
62
|
+
}
|
|
63
|
+
if (!schema) throw new Error(`Fragment '${definition.name}' does not have a database schema. Make sure you're using defineFragment().extend(withDatabase(schema)).`);
|
|
64
|
+
schemaConfigs.push({
|
|
65
|
+
schema,
|
|
66
|
+
namespace,
|
|
67
|
+
migrateToVersion: fragmentConfig.migrateToVersion
|
|
68
|
+
});
|
|
69
|
+
builderConfigs.push({
|
|
70
|
+
config: builder.config ?? {},
|
|
71
|
+
routes: builder.routes ?? [],
|
|
72
|
+
options: builder.options ?? {}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);
|
|
76
|
+
const createFragments = () => {
|
|
77
|
+
const providedServicesByName = {};
|
|
78
|
+
const fragmentResults$1 = [];
|
|
79
|
+
for (let i = 0; i < fragmentConfigs.length; i++) {
|
|
80
|
+
const fragmentConfig = fragmentConfigs[i];
|
|
81
|
+
const builderConfig = builderConfigs[i];
|
|
82
|
+
const namespace = schemaConfigs[i].namespace;
|
|
83
|
+
const schema = schemaConfigs[i].schema;
|
|
84
|
+
const orm = testContext.getOrm(namespace);
|
|
85
|
+
const mergedOptions = {
|
|
86
|
+
...builderConfig.options,
|
|
87
|
+
databaseAdapter: adapter
|
|
88
|
+
};
|
|
89
|
+
const fragment = fragmentConfig.builder.withOptions(mergedOptions).build();
|
|
90
|
+
for (const [serviceName, serviceImpl] of Object.entries(fragment.services)) providedServicesByName[serviceName] = {
|
|
91
|
+
service: serviceImpl,
|
|
92
|
+
orm
|
|
93
|
+
};
|
|
94
|
+
const deps = fragment.$internal?.deps;
|
|
95
|
+
fragmentResults$1.push({
|
|
96
|
+
fragment,
|
|
97
|
+
services: fragment.services,
|
|
98
|
+
deps: deps || {},
|
|
99
|
+
callRoute: fragment.callRoute.bind(fragment),
|
|
100
|
+
get db() {
|
|
101
|
+
return orm;
|
|
102
|
+
},
|
|
103
|
+
_orm: orm,
|
|
104
|
+
_schema: schema
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
for (let i = 0; i < fragmentConfigs.length; i++) {
|
|
108
|
+
const fragmentConfig = fragmentConfigs[i];
|
|
109
|
+
const definition = fragmentConfig.builder.definition;
|
|
110
|
+
const builderConfig = builderConfigs[i];
|
|
111
|
+
const namespace = schemaConfigs[i].namespace;
|
|
112
|
+
const schema = schemaConfigs[i].schema;
|
|
113
|
+
const orm = testContext.getOrm(namespace);
|
|
114
|
+
const serviceImplementations = {};
|
|
115
|
+
const serviceDependencies = definition.serviceDependencies;
|
|
116
|
+
if (serviceDependencies) {
|
|
117
|
+
for (const serviceName of Object.keys(serviceDependencies)) if (providedServicesByName[serviceName]) serviceImplementations[serviceName] = providedServicesByName[serviceName].service;
|
|
118
|
+
}
|
|
119
|
+
const mergedOptions = {
|
|
120
|
+
...builderConfig.options,
|
|
121
|
+
databaseAdapter: adapter
|
|
122
|
+
};
|
|
123
|
+
const fragment = fragmentConfig.builder.withOptions(mergedOptions).withServices(serviceImplementations).build();
|
|
124
|
+
const deps = fragment.$internal?.deps;
|
|
125
|
+
fragmentResults$1[i] = {
|
|
126
|
+
fragment,
|
|
127
|
+
services: fragment.services,
|
|
128
|
+
deps: deps || {},
|
|
129
|
+
callRoute: fragment.callRoute.bind(fragment),
|
|
130
|
+
get db() {
|
|
131
|
+
return orm;
|
|
132
|
+
},
|
|
133
|
+
_orm: orm,
|
|
134
|
+
_schema: schema
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
return fragmentResults$1;
|
|
138
|
+
};
|
|
139
|
+
const fragmentResults = createFragments();
|
|
140
|
+
const originalResetDatabase = testContext.resetDatabase;
|
|
141
|
+
const resetDatabase = async () => {
|
|
142
|
+
await originalResetDatabase();
|
|
143
|
+
createFragments().forEach((newResult, index) => {
|
|
144
|
+
const result = fragmentResults[index];
|
|
145
|
+
result.fragment = newResult.fragment;
|
|
146
|
+
result.services = newResult.services;
|
|
147
|
+
result.deps = newResult.deps;
|
|
148
|
+
result.callRoute = newResult.callRoute;
|
|
149
|
+
result._orm = newResult._orm;
|
|
150
|
+
});
|
|
151
|
+
};
|
|
152
|
+
const firstFragment = fragmentResults[0]?.fragment;
|
|
153
|
+
if (!firstFragment) throw new Error("At least one fragment must be added");
|
|
154
|
+
const finalTestContext = {
|
|
155
|
+
...testContext,
|
|
156
|
+
resetDatabase,
|
|
157
|
+
adapter,
|
|
158
|
+
inContext: firstFragment.inContext.bind(firstFragment)
|
|
159
|
+
};
|
|
160
|
+
return {
|
|
161
|
+
fragments: Object.fromEntries(fragmentNames.map((name, index) => [name, fragmentResults[index]])),
|
|
162
|
+
test: finalTestContext
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
};
|
|
166
|
+
/**
|
|
167
|
+
* Create a builder for setting up multiple database fragments for testing.
|
|
168
|
+
* This is the new builder-based API that works with the new fragment instantiation builders.
|
|
169
|
+
*
|
|
170
|
+
* @example
|
|
171
|
+
* ```typescript
|
|
172
|
+
* const userFragmentDef = defineFragment("user")
|
|
173
|
+
* .extend(withDatabase(userSchema))
|
|
174
|
+
* .withDependencies(...)
|
|
175
|
+
* .build();
|
|
176
|
+
*
|
|
177
|
+
* const postFragmentDef = defineFragment("post")
|
|
178
|
+
* .extend(withDatabase(postSchema))
|
|
179
|
+
* .withDependencies(...)
|
|
180
|
+
* .build();
|
|
181
|
+
*
|
|
182
|
+
* const { fragments, test } = await buildDatabaseFragmentsTest()
|
|
183
|
+
* .withTestAdapter({ type: "kysely-sqlite" })
|
|
184
|
+
* .withFragment("user",
|
|
185
|
+
* instantiate(userFragmentDef)
|
|
186
|
+
* .withConfig({ ... })
|
|
187
|
+
* .withRoutes([...])
|
|
188
|
+
* )
|
|
189
|
+
* .withFragment("post",
|
|
190
|
+
* instantiate(postFragmentDef)
|
|
191
|
+
* .withRoutes([...])
|
|
192
|
+
* )
|
|
193
|
+
* .build();
|
|
194
|
+
*
|
|
195
|
+
* // Access fragments by name
|
|
196
|
+
* await fragments.user.services.createUser(...);
|
|
197
|
+
* await fragments.post.services.createPost(...);
|
|
198
|
+
*
|
|
199
|
+
* // Access dependencies directly
|
|
200
|
+
* const userDeps = fragments.user.deps;
|
|
201
|
+
*
|
|
202
|
+
* // Shared test context
|
|
203
|
+
* await test.resetDatabase();
|
|
204
|
+
* await test.cleanup();
|
|
205
|
+
* const adapter = test.adapter; // Access the database adapter
|
|
206
|
+
* ```
|
|
207
|
+
*/
|
|
208
|
+
function buildDatabaseFragmentsTest() {
|
|
209
|
+
return new DatabaseFragmentsTestBuilder();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
//#endregion
|
|
213
|
+
export { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest };
|
|
214
|
+
//# sourceMappingURL=db-test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db-test.js","names":["#adapter","#fragments","schemaConfigs: SchemaConfig[]","builderConfigs: Array<{ config: any; routes: any; options: any }>","schema: AnySchema | undefined","providedServicesByName: Record<string, { service: any; orm: any }>","fragmentResults: any[]","serviceImplementations: Record<string, any>","fragmentResults"],"sources":["../src/db-test.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type {\n RequestThisContext,\n FragnoPublicConfig,\n FragmentInstantiationBuilder,\n FragnoInstantiatedFragment,\n FragmentDefinition,\n} from \"@fragno-dev/core\";\nimport type { AnyRouteOrFactory, FlattenRouteFactories } from \"@fragno-dev/core/route\";\nimport {\n createAdapter,\n type SupportedAdapter,\n type AdapterContext,\n type SchemaConfig,\n} from \"./adapters\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db\";\nimport type { AbstractQuery } from \"@fragno-dev/db/query\";\nimport type { BaseTestContext } from \".\";\n\n// BoundServices is an internal type that strips 'this' parameters from service methods\n// It's used to represent services after they've been bound to a context\ntype BoundServices<T> = {\n [K in keyof T]: T[K] extends (this: any, ...args: infer A) => infer R // eslint-disable-line @typescript-eslint/no-explicit-any\n ? (...args: A) => R\n : T[K] extends Record<string, unknown>\n ? BoundServices<T[K]>\n : T[K];\n};\n\n// Extract the schema type from database fragment dependencies\n// Database fragments have ImplicitDatabaseDependencies<TSchema> which includes `schema: TSchema`\ntype ExtractSchemaFromDeps<TDeps> = TDeps extends { schema: infer TSchema extends AnySchema }\n ? TSchema\n : AnySchema;\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype AnyLinkedFragments = Record<string, any>;\n\n// Forward declarations for recursive type references\ninterface FragmentResult<\n TDeps,\n TServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutes extends readonly any[], // eslint-disable-line @typescript-eslint/no-explicit-any\n TSchema extends AnySchema,\n TLinkedFragments extends AnyLinkedFragments = {},\n> {\n fragment: FragnoInstantiatedFragment<\n TRoutes,\n TDeps,\n TServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FragnoPublicConfig,\n TLinkedFragments\n >;\n services: TServices;\n deps: TDeps;\n callRoute: FragnoInstantiatedFragment<\n TRoutes,\n TDeps,\n TServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FragnoPublicConfig,\n TLinkedFragments\n >[\"callRoute\"];\n db: AbstractQuery<TSchema>;\n}\n\n// Safe: Catch-all for any fragment result type\ntype AnyFragmentResult = FragmentResult<\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any // eslint-disable-line @typescript-eslint/no-explicit-any\n>;\n\n// Safe: Catch-all for any fragment builder config type\ntype AnyFragmentBuilderConfig = FragmentBuilderConfig<\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any, // eslint-disable-line @typescript-eslint/no-explicit-any\n any // eslint-disable-line @typescript-eslint/no-explicit-any\n>;\n\n/**\n * Configuration for a single fragment in the test builder\n */\ninterface FragmentBuilderConfig<\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends AnyLinkedFragments,\n> {\n definition: FragmentDefinition<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TLinkedFragments\n >;\n builder: FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TRoutesOrFactories,\n TLinkedFragments\n >;\n migrateToVersion?: number;\n}\n\n/**\n * Test context combining base and adapter-specific functionality\n */\ntype TestContext<\n T extends SupportedAdapter,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> = BaseTestContext &\n AdapterContext<T> & {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n adapter: DatabaseAdapter<any>;\n /**\n * Execute a callback within the first fragment's request context.\n * This is useful for calling services outside of route handlers in tests.\n */\n inContext<TResult>(callback: (this: TFirstFragmentThisContext) => TResult): TResult;\n inContext<TResult>(\n callback: (this: TFirstFragmentThisContext) => Promise<TResult>,\n ): Promise<TResult>;\n };\n\n/**\n * Result of building the database fragments test\n */\ninterface DatabaseFragmentsTestResult<\n TFragments extends Record<string, AnyFragmentResult>,\n TAdapter extends SupportedAdapter,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> {\n fragments: TFragments;\n test: TestContext<TAdapter, TFirstFragmentThisContext>;\n}\n\n/**\n * Internal storage for fragment configurations\n */\ntype FragmentConfigMap = Map<string, AnyFragmentBuilderConfig>;\n\n/**\n * Builder for creating multiple database fragments for testing\n */\nexport class DatabaseFragmentsTestBuilder<\n TFragments extends Record<string, AnyFragmentResult>,\n TAdapter extends SupportedAdapter | undefined = undefined,\n TFirstFragmentThisContext extends RequestThisContext = RequestThisContext,\n> {\n #adapter?: SupportedAdapter;\n #fragments: FragmentConfigMap = new Map();\n\n /**\n * Set the test adapter configuration\n */\n withTestAdapter<TNewAdapter extends SupportedAdapter>(\n adapter: TNewAdapter,\n ): DatabaseFragmentsTestBuilder<TFragments, TNewAdapter, TFirstFragmentThisContext> {\n this.#adapter = adapter;\n return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n /**\n * Add a fragment to the test setup\n *\n * @param name - Unique name for the fragment\n * @param builder - Pre-configured instantiation builder\n * @param options - Additional options (optional)\n */\n withFragment<\n TName extends string,\n TConfig,\n TOptions extends FragnoPublicConfig,\n TDeps,\n TBaseServices extends Record<string, unknown>,\n TServices extends Record<string, unknown>,\n TServiceDependencies,\n TPrivateServices extends Record<string, unknown>,\n TServiceThisContext extends RequestThisContext,\n THandlerThisContext extends RequestThisContext,\n TRequestStorage,\n TRoutesOrFactories extends readonly AnyRouteOrFactory[],\n TLinkedFragments extends AnyLinkedFragments,\n >(\n name: TName,\n builder: FragmentInstantiationBuilder<\n TConfig,\n TOptions,\n TDeps,\n TBaseServices,\n TServices,\n TServiceDependencies,\n TPrivateServices,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n TRoutesOrFactories,\n TLinkedFragments\n >,\n options?: {\n migrateToVersion?: number;\n },\n ): DatabaseFragmentsTestBuilder<\n TFragments & {\n [K in TName]: FragmentResult<\n TDeps,\n BoundServices<TBaseServices & TServices>,\n TServiceThisContext,\n THandlerThisContext,\n TRequestStorage,\n FlattenRouteFactories<TRoutesOrFactories>,\n ExtractSchemaFromDeps<TDeps>, // Extract actual schema type from deps\n TLinkedFragments\n >;\n },\n TAdapter,\n // If this is the first fragment (TFragments is empty {}), use THandlerThisContext; otherwise keep existing\n keyof TFragments extends never ? THandlerThisContext : TFirstFragmentThisContext\n > {\n this.#fragments.set(name, {\n definition: builder.definition,\n builder,\n migrateToVersion: options?.migrateToVersion,\n });\n return this as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n\n /**\n * Build the test setup and return fragments and test context\n */\n async build(): Promise<\n TAdapter extends SupportedAdapter\n ? DatabaseFragmentsTestResult<TFragments, TAdapter, TFirstFragmentThisContext>\n : never\n > {\n if (!this.#adapter) {\n throw new Error(\"Test adapter must be set using withTestAdapter()\");\n }\n\n if (this.#fragments.size === 0) {\n throw new Error(\"At least one fragment must be added using withFragment()\");\n }\n\n const adapterConfig = this.#adapter;\n\n // Extract fragment names and configs\n const fragmentNames = Array.from(this.#fragments.keys());\n const fragmentConfigs = Array.from(this.#fragments.values());\n\n // Extract schemas from definitions and prepare schema configs\n const schemaConfigs: SchemaConfig[] = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const builderConfigs: Array<{ config: any; routes: any; options: any }> = [];\n\n for (const fragmentConfig of fragmentConfigs) {\n const builder = fragmentConfig.builder;\n const definition = builder.definition;\n\n // Extract schema from definition by calling dependencies with a mock adapter\n let schema: AnySchema | undefined;\n const namespace = definition.name + \"-db\";\n\n if (definition.dependencies) {\n try {\n // Create a mock adapter to extract the schema\n const mockAdapter = {\n createQueryEngine: () => ({ schema: null }),\n contextStorage: { run: (_data: unknown, fn: () => unknown) => fn() },\n };\n\n // Use the actual config from the builder instead of an empty mock\n // This ensures dependencies can be properly initialized (e.g., Stripe with API keys)\n const actualConfig = builder.config ?? {};\n\n const deps = definition.dependencies({\n config: actualConfig,\n options: {\n databaseAdapter: mockAdapter as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n } as any, // eslint-disable-line @typescript-eslint/no-explicit-any\n });\n\n // The schema is in deps.schema for database fragments\n if (deps && typeof deps === \"object\" && \"schema\" in deps) {\n schema = (deps as any).schema; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n } catch (error) {\n // If extraction fails, provide a helpful error message\n const errorMessage =\n error instanceof Error\n ? error.message\n : typeof error === \"string\"\n ? error\n : \"Unknown error\";\n\n throw new Error(\n `Failed to extract schema from fragment '${definition.name}'.\\n` +\n `Original error: ${errorMessage}\\n\\n` +\n `Make sure the fragment is a database fragment using defineFragment().extend(withDatabase(schema)).`,\n );\n }\n }\n\n if (!schema) {\n throw new Error(\n `Fragment '${definition.name}' does not have a database schema. ` +\n `Make sure you're using defineFragment().extend(withDatabase(schema)).`,\n );\n }\n\n schemaConfigs.push({\n schema,\n namespace,\n migrateToVersion: fragmentConfig.migrateToVersion,\n });\n\n // Extract config, routes, and options from builder using public getters\n builderConfigs.push({\n config: builder.config ?? {},\n routes: builder.routes ?? [],\n options: builder.options ?? {},\n });\n }\n\n // Create adapter with all schemas\n const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);\n\n // Helper to create fragments with service wiring\n const createFragments = () => {\n // First pass: create fragments without service dependencies to extract provided services\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const providedServicesByName: Record<string, { service: any; orm: any }> = {};\n const fragmentResults: any[] = []; // eslint-disable-line @typescript-eslint/no-explicit-any\n\n for (let i = 0; i < fragmentConfigs.length; i++) {\n const fragmentConfig = fragmentConfigs[i]!;\n const builderConfig = builderConfigs[i]!;\n const namespace = schemaConfigs[i]!.namespace;\n const schema = schemaConfigs[i]!.schema;\n const orm = testContext.getOrm(namespace);\n\n // Merge builder options with database adapter\n const mergedOptions = {\n ...builderConfig.options,\n databaseAdapter: adapter,\n };\n\n // Instantiate fragment using the builder\n const fragment = fragmentConfig.builder.withOptions(mergedOptions).build();\n\n // Extract provided services based on serviceDependencies metadata\n // Note: serviceDependencies lists services this fragment USES, not provides\n // For provided services, we need to check what's actually in fragment.services\n // and match against other fragments' service dependencies\n\n // Store all services as potentially provided\n for (const [serviceName, serviceImpl] of Object.entries(fragment.services)) {\n providedServicesByName[serviceName] = {\n service: serviceImpl,\n orm,\n };\n }\n\n // Store the fragment result\n const deps = fragment.$internal?.deps;\n\n fragmentResults.push({\n fragment,\n services: fragment.services,\n deps: deps || {},\n callRoute: fragment.callRoute.bind(fragment),\n get db() {\n return orm;\n },\n _orm: orm,\n _schema: schema,\n });\n }\n\n // Second pass: rebuild fragments with service dependencies wired up\n for (let i = 0; i < fragmentConfigs.length; i++) {\n const fragmentConfig = fragmentConfigs[i]!;\n const definition = fragmentConfig.builder.definition;\n const builderConfig = builderConfigs[i]!;\n const namespace = schemaConfigs[i]!.namespace;\n const schema = schemaConfigs[i]!.schema;\n const orm = testContext.getOrm(namespace);\n\n // Build service implementations for services this fragment uses\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const serviceImplementations: Record<string, any> = {};\n const serviceDependencies = definition.serviceDependencies;\n\n if (serviceDependencies) {\n for (const serviceName of Object.keys(serviceDependencies)) {\n if (providedServicesByName[serviceName]) {\n serviceImplementations[serviceName] = providedServicesByName[serviceName]!.service;\n }\n }\n }\n\n // Merge builder options with database adapter\n const mergedOptions = {\n ...builderConfig.options,\n databaseAdapter: adapter,\n };\n\n // Rebuild the fragment with service implementations using the builder\n const fragment = fragmentConfig.builder\n .withOptions(mergedOptions)\n .withServices(serviceImplementations as any) // eslint-disable-line @typescript-eslint/no-explicit-any\n .build();\n\n // Update the result\n // Access deps from the internal property\n const deps = fragment.$internal?.deps;\n\n fragmentResults[i] = {\n fragment,\n services: fragment.services,\n deps: deps || {},\n callRoute: fragment.callRoute.bind(fragment),\n get db() {\n return orm;\n },\n _orm: orm,\n _schema: schema,\n };\n }\n\n return fragmentResults;\n };\n\n const fragmentResults = createFragments();\n\n // Wrap resetDatabase to also recreate all fragments\n const originalResetDatabase = testContext.resetDatabase;\n const resetDatabase = async () => {\n await originalResetDatabase();\n\n // Recreate all fragments with service wiring\n const newFragmentResults = createFragments();\n\n // Update the result objects\n newFragmentResults.forEach((newResult, index) => {\n const result = fragmentResults[index]!;\n result.fragment = newResult.fragment;\n result.services = newResult.services;\n result.deps = newResult.deps;\n result.callRoute = newResult.callRoute;\n result._orm = newResult._orm;\n });\n };\n\n // Get the first fragment's inContext method\n const firstFragment = fragmentResults[0]?.fragment;\n if (!firstFragment) {\n throw new Error(\"At least one fragment must be added\");\n }\n\n const finalTestContext = {\n ...testContext,\n resetDatabase,\n adapter,\n inContext: firstFragment.inContext.bind(firstFragment),\n };\n\n // Build result object with named fragments\n const fragmentsObject = Object.fromEntries(\n fragmentNames.map((name, index) => [name, fragmentResults[index]]),\n );\n\n // Safe cast: We've already validated that adapterConfig is SupportedAdapter at the beginning of build()\n // TypeScript can't infer this through the conditional return type, so we use 'as any'\n return {\n fragments: fragmentsObject as TFragments,\n test: finalTestContext,\n } as any; // eslint-disable-line @typescript-eslint/no-explicit-any\n }\n}\n\n/**\n * Create a builder for setting up multiple database fragments for testing.\n * This is the new builder-based API that works with the new fragment instantiation builders.\n *\n * @example\n * ```typescript\n * const userFragmentDef = defineFragment(\"user\")\n * .extend(withDatabase(userSchema))\n * .withDependencies(...)\n * .build();\n *\n * const postFragmentDef = defineFragment(\"post\")\n * .extend(withDatabase(postSchema))\n * .withDependencies(...)\n * .build();\n *\n * const { fragments, test } = await buildDatabaseFragmentsTest()\n * .withTestAdapter({ type: \"kysely-sqlite\" })\n * .withFragment(\"user\",\n * instantiate(userFragmentDef)\n * .withConfig({ ... })\n * .withRoutes([...])\n * )\n * .withFragment(\"post\",\n * instantiate(postFragmentDef)\n * .withRoutes([...])\n * )\n * .build();\n *\n * // Access fragments by name\n * await fragments.user.services.createUser(...);\n * await fragments.post.services.createPost(...);\n *\n * // Access dependencies directly\n * const userDeps = fragments.user.deps;\n *\n * // Shared test context\n * await test.resetDatabase();\n * await test.cleanup();\n * const adapter = test.adapter; // Access the database adapter\n * ```\n */\nexport function buildDatabaseFragmentsTest(): DatabaseFragmentsTestBuilder<\n {},\n undefined,\n RequestThisContext\n> {\n return new DatabaseFragmentsTestBuilder();\n}\n"],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,40 +1,35 @@
|
|
|
1
|
-
import { DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter
|
|
2
|
-
import {
|
|
1
|
+
import { AdapterContext, DrizzlePgliteAdapter, KyselyPgliteAdapter, KyselySqliteAdapter, SupportedAdapter } from "./adapters.js";
|
|
2
|
+
import { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest } from "./db-test.js";
|
|
3
|
+
import { CreateFragmentForTestOptions, RouteHandlerInputOptions, createFragmentForTest } from "@fragno-dev/core/test";
|
|
3
4
|
import { AnySchema } from "@fragno-dev/db/schema";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/api/route";
|
|
7
|
-
import { FragnoRouteConfig } from "@fragno-dev/core";
|
|
8
|
-
import { HTTPMethod } from "@fragno-dev/core/api";
|
|
9
|
-
import { StandardSchemaV1 } from "@standard-schema/spec";
|
|
5
|
+
import { AbstractQuery } from "@fragno-dev/db/query";
|
|
6
|
+
import { DatabaseAdapter } from "@fragno-dev/db";
|
|
10
7
|
|
|
11
8
|
//#region src/index.d.ts
|
|
12
9
|
|
|
13
10
|
/**
|
|
14
|
-
*
|
|
11
|
+
* Base test context with common functionality across all adapters
|
|
15
12
|
*/
|
|
16
|
-
interface
|
|
17
|
-
adapter:
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
interface BaseTestContext {
|
|
14
|
+
readonly adapter: DatabaseAdapter<any>;
|
|
15
|
+
resetDatabase: () => Promise<void>;
|
|
16
|
+
cleanup: () => Promise<void>;
|
|
20
17
|
}
|
|
21
18
|
/**
|
|
22
|
-
*
|
|
23
|
-
* All properties are getters that return the current fragment instance
|
|
19
|
+
* Internal interface with getOrm for adapter implementations
|
|
24
20
|
*/
|
|
25
|
-
interface
|
|
26
|
-
|
|
27
|
-
readonly services: TServices;
|
|
28
|
-
readonly callRoute: FragmentForTest$1<TConfig, TDeps, TServices, TAdditionalContext, TOptions, TRoutes>["callRoute"];
|
|
29
|
-
readonly config: TConfig;
|
|
30
|
-
readonly deps: TDeps;
|
|
31
|
-
readonly additionalContext: TAdditionalContext;
|
|
32
|
-
test: TestContext<TAdapter>;
|
|
21
|
+
interface InternalTestContextMethods {
|
|
22
|
+
getOrm: <TSchema extends AnySchema>(namespace: string) => AbstractQuery<TSchema>;
|
|
33
23
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Helper to create common test context methods from an ORM map
|
|
26
|
+
* This is used internally by adapter implementations to avoid code duplication
|
|
27
|
+
*/
|
|
28
|
+
declare function createCommonTestContextMethods(ormMap: Map<string, AbstractQuery<any>>): InternalTestContextMethods;
|
|
29
|
+
/**
|
|
30
|
+
* Complete test context combining base and adapter-specific functionality
|
|
31
|
+
*/
|
|
32
|
+
type TestContext<T extends SupportedAdapter> = BaseTestContext & AdapterContext<T>;
|
|
38
33
|
//#endregion
|
|
39
|
-
export {
|
|
34
|
+
export { type AdapterContext, BaseTestContext, type CreateFragmentForTestOptions, DatabaseFragmentsTestBuilder, type DrizzlePgliteAdapter, InternalTestContextMethods, type KyselyPgliteAdapter, type KyselySqliteAdapter, type RouteHandlerInputOptions, type SupportedAdapter, TestContext, buildDatabaseFragmentsTest, createCommonTestContextMethods, createFragmentForTest };
|
|
40
35
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;;;;AAiCA;;AAGuB,UAHN,eAAA,CAGM;EACN,SAAA,OAAA,EAFG,eAEH,CAAA,GAAA,CAAA;EAAO,aAAA,EAAA,GAAA,GADD,OACC,CAAA,IAAA,CAAA;EAMP,OAAA,EAAA,GAAA,GANA,OAMA,CAAA,IAAA,CAAA;;;;;AAQD,UARC,0BAAA,CAQ6B;EAExB,MAAA,EAAA,CAAA,gBATK,SASL,CAAA,CAAA,SAAA,EAAA,MAAA,EAAA,GATsC,aAStC,CAToD,OASpD,CAAA;;;;AAgBtB;;AAAsD,iBAlBtC,8BAAA,CAkBsC,MAAA,EAhB5C,GAgB4C,CAAA,MAAA,EAhBhC,aAgBgC,CAAA,GAAA,CAAA,CAAA,CAAA,EAfnD,0BAemD;;;;KAA1C,sBAAsB,oBAAoB,kBAAkB,eAAe"}
|
package/dist/index.js
CHANGED
|
@@ -1,97 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createFragmentForTest
|
|
1
|
+
import { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest } from "./db-test.js";
|
|
2
|
+
import { createFragmentForTest } from "@fragno-dev/core/test";
|
|
3
3
|
|
|
4
4
|
//#region src/index.ts
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
};
|
|
16
|
-
let fragment = createFragmentForTest$1(fragmentBuilder, routesOrFactories, {
|
|
17
|
-
config,
|
|
18
|
-
options: mergedOptions,
|
|
19
|
-
deps,
|
|
20
|
-
services,
|
|
21
|
-
additionalContext
|
|
22
|
-
});
|
|
23
|
-
const originalResetDatabase = originalTestContext.resetDatabase;
|
|
24
|
-
return {
|
|
25
|
-
get fragment() {
|
|
26
|
-
return fragment;
|
|
27
|
-
},
|
|
28
|
-
get services() {
|
|
29
|
-
return fragment.services;
|
|
30
|
-
},
|
|
31
|
-
get callRoute() {
|
|
32
|
-
return fragment.callRoute;
|
|
33
|
-
},
|
|
34
|
-
get config() {
|
|
35
|
-
return fragment.config;
|
|
36
|
-
},
|
|
37
|
-
get deps() {
|
|
38
|
-
return fragment.deps;
|
|
39
|
-
},
|
|
40
|
-
get additionalContext() {
|
|
41
|
-
return fragment.additionalContext;
|
|
42
|
-
},
|
|
43
|
-
test: Object.create(originalTestContext, {
|
|
44
|
-
db: {
|
|
45
|
-
get() {
|
|
46
|
-
return originalTestContext.db;
|
|
47
|
-
},
|
|
48
|
-
enumerable: true
|
|
49
|
-
},
|
|
50
|
-
kysely: {
|
|
51
|
-
get() {
|
|
52
|
-
return originalTestContext.kysely;
|
|
53
|
-
},
|
|
54
|
-
enumerable: true
|
|
55
|
-
},
|
|
56
|
-
drizzle: {
|
|
57
|
-
get() {
|
|
58
|
-
return originalTestContext.drizzle;
|
|
59
|
-
},
|
|
60
|
-
enumerable: true
|
|
61
|
-
},
|
|
62
|
-
adapter: {
|
|
63
|
-
get() {
|
|
64
|
-
return originalTestContext.adapter;
|
|
65
|
-
},
|
|
66
|
-
enumerable: true
|
|
67
|
-
},
|
|
68
|
-
resetDatabase: {
|
|
69
|
-
value: async () => {
|
|
70
|
-
await originalResetDatabase();
|
|
71
|
-
mergedOptions = {
|
|
72
|
-
...fragmentOptions,
|
|
73
|
-
databaseAdapter: originalTestContext.adapter
|
|
74
|
-
};
|
|
75
|
-
fragment = createFragmentForTest$1(fragmentBuilder, routesOrFactories, {
|
|
76
|
-
config,
|
|
77
|
-
options: mergedOptions,
|
|
78
|
-
deps,
|
|
79
|
-
services,
|
|
80
|
-
additionalContext
|
|
81
|
-
});
|
|
82
|
-
},
|
|
83
|
-
enumerable: true
|
|
84
|
-
},
|
|
85
|
-
cleanup: {
|
|
86
|
-
value: async () => {
|
|
87
|
-
await originalTestContext.cleanup();
|
|
88
|
-
},
|
|
89
|
-
enumerable: true
|
|
90
|
-
}
|
|
91
|
-
})
|
|
92
|
-
};
|
|
5
|
+
/**
|
|
6
|
+
* Helper to create common test context methods from an ORM map
|
|
7
|
+
* This is used internally by adapter implementations to avoid code duplication
|
|
8
|
+
*/
|
|
9
|
+
function createCommonTestContextMethods(ormMap) {
|
|
10
|
+
return { getOrm: (namespace) => {
|
|
11
|
+
const orm = ormMap.get(namespace);
|
|
12
|
+
if (!orm) throw new Error(`No ORM found for namespace: ${namespace}`);
|
|
13
|
+
return orm;
|
|
14
|
+
} };
|
|
93
15
|
}
|
|
94
16
|
|
|
95
17
|
//#endregion
|
|
96
|
-
export {
|
|
18
|
+
export { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest, createCommonTestContextMethods, createFragmentForTest };
|
|
97
19
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AnySchema } from \"@fragno-dev/db/schema\";\nimport type {\n SupportedAdapter,\n AdapterContext,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n} from \"./adapters\";\nimport type { DatabaseAdapter } from \"@fragno-dev/db\";\nimport type { AbstractQuery } from \"@fragno-dev/db/query\";\n\n// Re-export utilities from @fragno-dev/core/test\nexport {\n createFragmentForTest,\n type CreateFragmentForTestOptions,\n type RouteHandlerInputOptions,\n} from \"@fragno-dev/core/test\";\n\n// Re-export adapter types\nexport type {\n SupportedAdapter,\n KyselySqliteAdapter,\n KyselyPgliteAdapter,\n DrizzlePgliteAdapter,\n AdapterContext,\n} from \"./adapters\";\n\n// Re-export new builder-based database test utilities\nexport { buildDatabaseFragmentsTest, DatabaseFragmentsTestBuilder } from \"./db-test\";\n\n/**\n * Base test context with common functionality across all adapters\n */\nexport interface BaseTestContext {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n readonly adapter: DatabaseAdapter<any>;\n resetDatabase: () => Promise<void>;\n cleanup: () => Promise<void>;\n}\n\n/**\n * Internal interface with getOrm for adapter implementations\n */\nexport interface InternalTestContextMethods {\n getOrm: <TSchema extends AnySchema>(namespace: string) => AbstractQuery<TSchema>;\n}\n\n/**\n * Helper to create common test context methods from an ORM map\n * This is used internally by adapter implementations to avoid code duplication\n */\nexport function createCommonTestContextMethods(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ormMap: Map<string, AbstractQuery<any>>,\n): InternalTestContextMethods {\n return {\n getOrm: <TSchema extends AnySchema>(namespace: string) => {\n const orm = ormMap.get(namespace);\n if (!orm) {\n throw new Error(`No ORM found for namespace: ${namespace}`);\n }\n return orm as AbstractQuery<TSchema>;\n },\n };\n}\n\n/**\n * Complete test context combining base and adapter-specific functionality\n */\nexport type TestContext<T extends SupportedAdapter> = BaseTestContext & AdapterContext<T>;\n"],"mappings":";;;;;;;;AAmDA,SAAgB,+BAEd,QAC4B;AAC5B,QAAO,EACL,SAAoC,cAAsB;EACxD,MAAM,MAAM,OAAO,IAAI,UAAU;AACjC,MAAI,CAAC,IACH,OAAM,IAAI,MAAM,+BAA+B,YAAY;AAE7D,SAAO;IAEV"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/test",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@standard-schema/spec": "^1.0.0",
|
|
17
17
|
"kysely": "^0.28.7",
|
|
18
|
-
"sqlocal": "^0.15.2"
|
|
19
|
-
"@fragno-dev/core": "0.1.7",
|
|
20
|
-
"@fragno-dev/db": "0.1.13"
|
|
18
|
+
"sqlocal": "^0.15.2"
|
|
21
19
|
},
|
|
22
20
|
"peerDependencies": {
|
|
23
21
|
"@electric-sql/pglite": "^0.3.11",
|
|
24
22
|
"drizzle-kit": "^0.30.3",
|
|
25
23
|
"drizzle-orm": "^0.44.7",
|
|
26
|
-
"kysely-pglite": "^0.6.1"
|
|
24
|
+
"kysely-pglite": "^0.6.1",
|
|
25
|
+
"@fragno-dev/core": "0.1.9",
|
|
26
|
+
"@fragno-dev/db": "0.1.15"
|
|
27
27
|
},
|
|
28
28
|
"peerDependenciesMeta": {
|
|
29
29
|
"@electric-sql/pglite": {
|
|
@@ -49,6 +49,8 @@
|
|
|
49
49
|
"kysely-pglite": "^0.6.1",
|
|
50
50
|
"vitest": "^3.2.4",
|
|
51
51
|
"zod": "^4.1.12",
|
|
52
|
+
"@fragno-dev/core": "0.1.9",
|
|
53
|
+
"@fragno-dev/db": "0.1.15",
|
|
52
54
|
"@fragno-private/typescript-config": "0.0.1",
|
|
53
55
|
"@fragno-private/vitest-config": "0.0.0"
|
|
54
56
|
},
|