@fragno-dev/test 0.1.11 → 0.1.12

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
@@ -10,14 +10,14 @@ import type { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/
10
10
  import {
11
11
  createAdapter,
12
12
  type SupportedAdapter,
13
- type TestContext,
13
+ type AdapterContext,
14
14
  type KyselySqliteAdapter,
15
15
  type KyselyPgliteAdapter,
16
16
  type DrizzlePgliteAdapter,
17
+ type SchemaConfig,
17
18
  } from "./adapters";
18
- import type { FragnoRouteConfig } from "@fragno-dev/core";
19
- import type { HTTPMethod } from "@fragno-dev/core/api";
20
- import type { StandardSchemaV1 } from "@standard-schema/spec";
19
+ import { withUnitOfWork, type IUnitOfWorkBase, type DatabaseAdapter } from "@fragno-dev/db";
20
+ import type { AbstractQuery } from "@fragno-dev/db/query";
21
21
 
22
22
  // Re-export utilities from @fragno-dev/core/test
23
23
  export {
@@ -33,240 +33,426 @@ export type {
33
33
  KyselySqliteAdapter,
34
34
  KyselyPgliteAdapter,
35
35
  DrizzlePgliteAdapter,
36
- TestContext,
36
+ AdapterContext,
37
37
  } from "./adapters";
38
38
 
39
39
  /**
40
- * Options for creating a database fragment for testing
40
+ * Base test context with common functionality across all adapters
41
41
  */
42
- export interface CreateDatabaseFragmentForTestOptions<
43
- TConfig,
44
- TDeps,
45
- TServices,
46
- TAdditionalContext extends Record<string, unknown>,
47
- TOptions extends FragnoPublicConfig,
48
- TAdapter extends SupportedAdapter,
49
- > extends Omit<
50
- CreateFragmentForTestOptions<TConfig, TDeps, TServices, TAdditionalContext, TOptions>,
51
- "config"
52
- > {
53
- adapter: TAdapter;
42
+ export interface BaseTestContext {
43
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
44
+ readonly adapter: DatabaseAdapter<any>;
45
+ createUnitOfWork: (name?: string) => IUnitOfWorkBase;
46
+ withUnitOfWork: <T>(fn: (uow: IUnitOfWorkBase) => Promise<T>) => Promise<T>;
47
+ callService: <T>(fn: () => T | Promise<T>) => Promise<T>;
48
+ resetDatabase: () => Promise<void>;
49
+ cleanup: () => Promise<void>;
50
+ }
51
+
52
+ /**
53
+ * Internal interface with getOrm for adapter implementations
54
+ */
55
+ export interface InternalTestContextMethods {
56
+ getOrm: <TSchema extends AnySchema>(namespace: string) => AbstractQuery<TSchema>;
57
+ createUnitOfWork: (name?: string) => IUnitOfWorkBase;
58
+ withUnitOfWork: <T>(fn: (uow: IUnitOfWorkBase) => Promise<T>) => Promise<T>;
59
+ callService: <T>(fn: () => T | Promise<T>) => Promise<T>;
60
+ }
61
+
62
+ /**
63
+ * Helper to create common test context methods from an ORM map
64
+ * This is used internally by adapter implementations to avoid code duplication
65
+ */
66
+ export function createCommonTestContextMethods(
67
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
68
+ ormMap: Map<string, AbstractQuery<any>>,
69
+ ): InternalTestContextMethods {
70
+ return {
71
+ getOrm: <TSchema extends AnySchema>(namespace: string) => {
72
+ const orm = ormMap.get(namespace);
73
+ if (!orm) {
74
+ throw new Error(`No ORM found for namespace: ${namespace}`);
75
+ }
76
+ return orm as AbstractQuery<TSchema>;
77
+ },
78
+ createUnitOfWork: (name?: string) => {
79
+ // Use the first schema's ORM to create a base UOW
80
+ const firstOrm = ormMap.values().next().value;
81
+ if (!firstOrm) {
82
+ throw new Error("No ORMs available to create UnitOfWork");
83
+ }
84
+ return firstOrm.createUnitOfWork(name);
85
+ },
86
+ withUnitOfWork: async <T>(fn: (uow: IUnitOfWorkBase) => Promise<T>) => {
87
+ const firstOrm = ormMap.values().next().value;
88
+ if (!firstOrm) {
89
+ throw new Error("No ORMs available to create UnitOfWork");
90
+ }
91
+ const uow = firstOrm.createUnitOfWork();
92
+ return withUnitOfWork(uow, async () => {
93
+ return await fn(uow);
94
+ });
95
+ },
96
+ callService: async <T>(fn: () => T | Promise<T>) => {
97
+ const firstOrm = ormMap.values().next().value;
98
+ if (!firstOrm) {
99
+ throw new Error("No ORMs available to create UnitOfWork");
100
+ }
101
+ const uow = firstOrm.createUnitOfWork();
102
+ return withUnitOfWork(uow, async () => {
103
+ // Call the function to schedule operations (don't await yet)
104
+ const resultPromise = fn();
105
+
106
+ // Execute UOW phases
107
+ await uow.executeRetrieve();
108
+ await uow.executeMutations();
109
+
110
+ // Now await the result
111
+ return await resultPromise;
112
+ });
113
+ },
114
+ };
115
+ }
116
+
117
+ /**
118
+ * Complete test context combining base and adapter-specific functionality
119
+ */
120
+ export type TestContext<T extends SupportedAdapter> = BaseTestContext & AdapterContext<T>;
121
+
122
+ /**
123
+ * Helper type to extract the schema from a fragment definition's additional context
124
+ */
125
+ type ExtractSchemaFromAdditionalContext<TAdditionalContext> = TAdditionalContext extends {
126
+ databaseSchema?: infer TSchema extends AnySchema;
127
+ }
128
+ ? TSchema
129
+ : AnySchema;
130
+
131
+ /**
132
+ * Fragment configuration for multi-fragment setup
133
+ */
134
+ export interface FragmentConfig<
135
+ TDef extends {
136
+ definition: FragmentDefinition<any, any, any, any, any, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
137
+ $requiredOptions: any; // eslint-disable-line @typescript-eslint/no-explicit-any
138
+ } = {
139
+ definition: FragmentDefinition<any, any, any, any, any, any>; // eslint-disable-line @typescript-eslint/no-explicit-any
140
+ $requiredOptions: any; // eslint-disable-line @typescript-eslint/no-explicit-any
141
+ },
142
+ TRoutes extends readonly AnyRouteOrFactory[] = readonly AnyRouteOrFactory[],
143
+ > {
144
+ definition: TDef;
145
+ routes: TRoutes;
146
+ config?: TDef["definition"] extends FragmentDefinition<infer TConfig, any, any, any, any, any> // eslint-disable-line @typescript-eslint/no-explicit-any
147
+ ? TConfig
148
+ : never;
54
149
  migrateToVersion?: number;
55
- config?: TConfig;
56
150
  }
57
151
 
58
152
  /**
59
- * Result of creating a database fragment for testing
60
- * All properties are getters that return the current fragment instance
153
+ * Options for creating multiple database fragments for testing
154
+ */
155
+ export interface MultiFragmentTestOptions<TAdapter extends SupportedAdapter> {
156
+ adapter: TAdapter;
157
+ }
158
+
159
+ /**
160
+ * Result type for a single fragment in a multi-fragment setup
61
161
  */
62
- export interface DatabaseFragmentTestResult<
63
- TConfig,
64
- TDeps,
65
- TServices,
66
- TAdditionalContext extends Record<string, unknown>,
67
- TOptions extends FragnoPublicConfig,
162
+ type FragmentResultFromConfig<TConfig extends FragmentConfig> = TConfig["definition"] extends {
163
+ definition: FragmentDefinition<
164
+ infer TConf,
165
+ infer TDeps,
166
+ infer TServices,
167
+ infer TAdditionalCtx,
168
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
169
+ any,
170
+ infer TProvidedServices
171
+ >;
172
+ $requiredOptions: infer TOptions extends FragnoPublicConfig;
173
+ }
174
+ ? {
175
+ fragment: FragmentForTest<
176
+ TConf,
177
+ TDeps,
178
+ TServices & TProvidedServices,
179
+ TAdditionalCtx,
180
+ TOptions,
181
+ FlattenRouteFactories<TConfig["routes"]>
182
+ >;
183
+ services: TServices & TProvidedServices;
184
+ callRoute: FragmentForTest<
185
+ TConf,
186
+ TDeps,
187
+ TServices & TProvidedServices,
188
+ TAdditionalCtx,
189
+ TOptions,
190
+ FlattenRouteFactories<TConfig["routes"]>
191
+ >["callRoute"];
192
+ config: TConf;
193
+ deps: TDeps;
194
+ additionalContext: TAdditionalCtx;
195
+ db: AbstractQuery<ExtractSchemaFromAdditionalContext<TAdditionalCtx>>;
196
+ }
197
+ : never;
198
+
199
+ export interface SingleFragmentTestResult<
200
+ TFragment extends FragmentConfig,
68
201
  TAdapter extends SupportedAdapter,
69
- TRoutes extends readonly FragnoRouteConfig<
70
- HTTPMethod,
71
- string,
72
- StandardSchemaV1 | undefined,
73
- StandardSchemaV1 | undefined,
74
- string,
75
- string
76
- >[],
77
202
  > {
78
- readonly fragment: FragmentForTest<
79
- TConfig,
80
- TDeps,
81
- TServices,
82
- TAdditionalContext,
83
- TOptions,
84
- TRoutes
85
- >;
86
- readonly services: TServices;
87
- readonly callRoute: FragmentForTest<
88
- TConfig,
89
- TDeps,
90
- TServices,
91
- TAdditionalContext,
92
- TOptions,
93
- TRoutes
94
- >["callRoute"];
95
- readonly config: TConfig;
96
- readonly deps: TDeps;
97
- readonly additionalContext: TAdditionalContext;
203
+ fragment: FragmentResultFromConfig<TFragment>;
98
204
  test: TestContext<TAdapter>;
99
205
  }
100
206
 
101
- export async function createDatabaseFragmentForTest<
102
- const TConfig,
103
- const TDeps,
104
- const TServices extends Record<string, unknown>,
105
- const TAdditionalContext extends Record<string, unknown>,
106
- const TOptions extends FragnoPublicConfig,
107
- const TSchema extends AnySchema,
207
+ /**
208
+ * Result of creating multiple database fragments for testing (array input)
209
+ */
210
+ export interface MultiFragmentTestResult<
211
+ TFragments extends readonly FragmentConfig[],
212
+ TAdapter extends SupportedAdapter,
213
+ > {
214
+ fragments: {
215
+ [K in keyof TFragments]: FragmentResultFromConfig<TFragments[K]>;
216
+ };
217
+ test: TestContext<TAdapter>;
218
+ }
219
+
220
+ /**
221
+ * Result of creating multiple database fragments for testing (object input)
222
+ */
223
+ export interface NamedMultiFragmentTestResult<
224
+ TFragments extends Record<string, FragmentConfig>,
225
+ TAdapter extends SupportedAdapter,
226
+ > {
227
+ fragments: {
228
+ [K in keyof TFragments]: FragmentResultFromConfig<TFragments[K]>;
229
+ };
230
+ test: TestContext<TAdapter>;
231
+ }
232
+
233
+ /**
234
+ * Create multiple database fragments for testing with a shared adapter (array input)
235
+ */
236
+ export async function createDatabaseFragmentsForTest<
237
+ const TFragments extends readonly FragmentConfig[],
108
238
  const TAdapter extends SupportedAdapter,
109
- const TRoutesOrFactories extends readonly AnyRouteOrFactory[],
110
239
  >(
111
- fragmentBuilder: {
112
- definition: FragmentDefinition<TConfig, TDeps, TServices, TAdditionalContext>;
113
- $requiredOptions: TOptions;
114
- },
115
- routesOrFactories: TRoutesOrFactories,
116
- options: CreateDatabaseFragmentForTestOptions<
117
- TConfig,
118
- TDeps,
119
- TServices,
120
- TAdditionalContext,
121
- TOptions,
122
- TAdapter
123
- >,
240
+ fragments: TFragments,
241
+ options: MultiFragmentTestOptions<TAdapter>,
242
+ ): Promise<MultiFragmentTestResult<TFragments, TAdapter>>;
243
+
244
+ /**
245
+ * Create multiple database fragments for testing with a shared adapter (object input)
246
+ */
247
+ export async function createDatabaseFragmentsForTest<
248
+ const TFragments extends Record<string, FragmentConfig>,
249
+ const TAdapter extends SupportedAdapter,
250
+ >(
251
+ fragments: TFragments,
252
+ options: MultiFragmentTestOptions<TAdapter>,
253
+ ): Promise<NamedMultiFragmentTestResult<TFragments, TAdapter>>;
254
+
255
+ /**
256
+ * Implementation of createDatabaseFragmentsForTest
257
+ */
258
+ export async function createDatabaseFragmentsForTest<
259
+ const TFragments extends readonly FragmentConfig[] | Record<string, FragmentConfig>,
260
+ const TAdapter extends SupportedAdapter,
261
+ >(
262
+ fragments: TFragments,
263
+ options: MultiFragmentTestOptions<TAdapter>,
124
264
  ): Promise<
125
- DatabaseFragmentTestResult<
126
- TConfig,
127
- TDeps,
128
- TServices,
129
- TAdditionalContext,
130
- TOptions,
131
- TAdapter,
132
- FlattenRouteFactories<TRoutesOrFactories>
133
- >
265
+ | MultiFragmentTestResult<any, TAdapter> // eslint-disable-line @typescript-eslint/no-explicit-any
266
+ | NamedMultiFragmentTestResult<any, TAdapter> // eslint-disable-line @typescript-eslint/no-explicit-any
134
267
  > {
135
- const {
136
- adapter: adapterConfig,
137
- migrateToVersion,
138
- config,
139
- options: fragmentOptions,
140
- deps,
141
- services,
142
- additionalContext,
143
- } = options;
144
-
145
- // Get schema and namespace from fragment definition's additionalContext
146
- // Safe cast: DatabaseFragmentBuilder adds databaseSchema and databaseNamespace to additionalContext
147
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
148
- const fragmentAdditionalContext = fragmentBuilder.definition.additionalContext as any;
149
- const schema = fragmentAdditionalContext?.databaseSchema as TSchema | undefined;
150
- const namespace = (fragmentAdditionalContext?.databaseNamespace as string | undefined) ?? "";
151
-
152
- if (!schema) {
153
- throw new Error(
154
- `Fragment '${fragmentBuilder.definition.name}' does not have a database schema. ` +
155
- `Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`,
156
- );
157
- }
268
+ const { adapter: adapterConfig } = options;
269
+
270
+ // Convert to array for processing
271
+ const isArray = Array.isArray(fragments);
272
+ const fragmentsArray: FragmentConfig[] = isArray
273
+ ? fragments
274
+ : Object.values(fragments as Record<string, FragmentConfig>);
275
+
276
+ // Extract schemas from all fragments
277
+ const schemaConfigs: SchemaConfig[] = fragmentsArray.map((fragmentConfig) => {
278
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
279
+ const fragmentAdditionalContext = fragmentConfig.definition.definition.additionalContext as any;
280
+ const schema = fragmentAdditionalContext?.databaseSchema as AnySchema | undefined;
281
+ const namespace =
282
+ (fragmentAdditionalContext?.databaseNamespace as string | undefined) ??
283
+ fragmentConfig.definition.definition.name + "-db";
158
284
 
159
- // Create adapter using the factory
160
- const { testContext: originalTestContext, adapter } = await createAdapter(
161
- adapterConfig,
162
- schema,
163
- namespace,
164
- migrateToVersion,
165
- );
166
-
167
- // Create fragment with database adapter in options
168
- // Safe cast: We're merging the user's options with the databaseAdapter, which is required by TOptions
169
- // The user's TOptions is constrained to FragnoPublicConfig (or a subtype), which we extend with databaseAdapter
170
- let mergedOptions = {
171
- ...fragmentOptions,
172
- databaseAdapter: adapter,
173
- } as unknown as TOptions;
174
-
175
- let fragment = createFragmentForTest(fragmentBuilder, routesOrFactories, {
176
- // Safe cast: If config is not provided, we pass undefined as TConfig.
177
- // The base createFragmentForTest expects config: TConfig, but if TConfig allows undefined
178
- // or if the fragment doesn't use config in its dependencies function, this will work correctly.
179
- config: config as TConfig,
180
- options: mergedOptions,
181
- deps,
182
- services,
183
- additionalContext,
285
+ if (!schema) {
286
+ throw new Error(
287
+ `Fragment '${fragmentConfig.definition.definition.name}' does not have a database schema. ` +
288
+ `Make sure you're using defineFragmentWithDatabase().withDatabase(schema).`,
289
+ );
290
+ }
291
+
292
+ return {
293
+ schema,
294
+ namespace,
295
+ migrateToVersion: fragmentConfig.migrateToVersion,
296
+ };
184
297
  });
185
298
 
186
- // Wrap resetDatabase to also recreate the fragment with the new adapter
187
- const originalResetDatabase = originalTestContext.resetDatabase;
299
+ // Create adapter with all schemas
300
+ const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);
188
301
 
189
- // Create test context with getters that always return current values
190
- // We need to cast to any to avoid TypeScript errors when accessing kysely/drizzle properties
191
- // that may not exist depending on the adapter type
192
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
193
- const testContext: any = Object.create(originalTestContext, {
194
- db: {
195
- get() {
196
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
197
- return (originalTestContext as any).db;
198
- },
199
- enumerable: true,
200
- },
201
- kysely: {
202
- get() {
203
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
204
- return (originalTestContext as any).kysely;
205
- },
206
- enumerable: true,
207
- },
208
- drizzle: {
209
- get() {
302
+ // Helper to create fragments with service wiring
303
+ const createFragments = () => {
304
+ // First pass: create fragments without dependencies to extract their provided services
305
+ // Map from service name to { service: wrapped service, orm: ORM for that service's schema }
306
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
307
+ const providedServicesByName: Record<string, { service: any; orm: any }> = {};
308
+
309
+ for (let i = 0; i < fragmentsArray.length; i++) {
310
+ const fragmentConfig = fragmentsArray[i]!;
311
+ const namespace = schemaConfigs[i]!.namespace;
312
+ const orm = testContext.getOrm(namespace);
313
+
314
+ // Create fragment without interface implementations to extract its services
315
+ const mergedOptions = {
316
+ databaseAdapter: adapter,
317
+ };
318
+
319
+ const tempFragment = createFragmentForTest(fragmentConfig.definition, [], {
320
+ config: fragmentConfig.config ?? {},
321
+ options: mergedOptions,
322
+ interfaceImplementations: {},
323
+ });
324
+
325
+ // Extract provided services from the created fragment
326
+ // Check which services this fragment provides
327
+ const providedServicesMetadata = fragmentConfig.definition.definition.providedServices;
328
+ if (providedServicesMetadata) {
329
+ // If providedServices is a function, it returns all services
330
+ // If it's an object, the keys are the service names
331
+ const serviceNames =
332
+ typeof providedServicesMetadata === "function"
333
+ ? Object.keys(tempFragment.services)
334
+ : Object.keys(providedServicesMetadata);
335
+
336
+ for (const serviceName of serviceNames) {
337
+ if (tempFragment.services[serviceName]) {
338
+ providedServicesByName[serviceName] = {
339
+ service: tempFragment.services[serviceName],
340
+ orm,
341
+ };
342
+ }
343
+ }
344
+ }
345
+ }
346
+
347
+ // Second pass: create fragments with service dependencies wired up
348
+ return fragmentsArray.map((fragmentConfig, index) => {
349
+ const namespace = schemaConfigs[index]!.namespace;
350
+
351
+ // Get ORM for this fragment's namespace
352
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
353
+ const schema = schemaConfigs[index]!.schema as any;
354
+ const orm = testContext.getOrm(namespace);
355
+
356
+ // Create fragment with database adapter in options
357
+ const mergedOptions = {
358
+ databaseAdapter: adapter,
210
359
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
211
- return (originalTestContext as any).drizzle;
212
- },
213
- enumerable: true,
214
- },
215
- adapter: {
216
- get() {
217
- return originalTestContext.adapter;
218
- },
219
- enumerable: true,
220
- },
221
- resetDatabase: {
222
- value: async () => {
223
- // Call the original reset database function
224
- await originalResetDatabase();
225
-
226
- // Recreate the fragment with the new adapter (which has been updated by the factory's resetDatabase)
227
- mergedOptions = {
228
- ...fragmentOptions,
229
- databaseAdapter: originalTestContext.adapter,
230
- } as unknown as TOptions;
231
-
232
- fragment = createFragmentForTest(fragmentBuilder, routesOrFactories, {
233
- config: config as TConfig,
234
- options: mergedOptions,
235
- deps,
236
- services,
237
- additionalContext,
238
- });
239
- },
240
- enumerable: true,
241
- },
242
- cleanup: {
243
- value: async () => {
244
- await originalTestContext.cleanup();
245
- },
246
- enumerable: true,
247
- },
248
- });
360
+ } as any;
361
+
362
+ // Build interface implementations for services this fragment uses
363
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
364
+ const interfaceImplementations: Record<string, any> = {};
365
+ const usedServices = fragmentConfig.definition.definition.usedServices;
366
+
367
+ if (usedServices) {
368
+ for (const serviceName of Object.keys(usedServices)) {
369
+ if (providedServicesByName[serviceName]) {
370
+ // Use the wrapped service
371
+ interfaceImplementations[serviceName] = providedServicesByName[serviceName]!.service;
372
+ }
373
+ }
374
+ }
375
+
376
+ const fragment = createFragmentForTest(fragmentConfig.definition, fragmentConfig.routes, {
377
+ config: (fragmentConfig.config ?? {}) as any, // eslint-disable-line @typescript-eslint/no-explicit-any
378
+ options: mergedOptions,
379
+ interfaceImplementations,
380
+ });
381
+
382
+ // Return fragment services without wrapping - users manage UOW lifecycle explicitly
383
+ return {
384
+ fragment,
385
+ services: fragment.services,
386
+ callRoute: fragment.callRoute,
387
+ config: fragment.config,
388
+ deps: fragment.deps,
389
+ additionalContext: fragment.additionalContext,
390
+ get db() {
391
+ return orm;
392
+ },
393
+ _orm: orm,
394
+ _schema: schema,
395
+ };
396
+ });
397
+ };
398
+
399
+ const fragmentResults = createFragments();
400
+
401
+ // Wrap resetDatabase to also recreate all fragments
402
+ const originalResetDatabase = testContext.resetDatabase;
403
+ const resetDatabase = async () => {
404
+ await originalResetDatabase();
405
+
406
+ // Recreate all fragments with service wiring
407
+ const newFragmentResults = createFragments();
408
+
409
+ // Update the result objects
410
+ newFragmentResults.forEach((newResult, index) => {
411
+ const result = fragmentResults[index]!;
412
+ result.fragment = newResult.fragment;
413
+ result.services = newResult.services;
414
+ result.callRoute = newResult.callRoute;
415
+ result.config = newResult.config;
416
+ result.deps = newResult.deps;
417
+ result.additionalContext = newResult.additionalContext;
418
+ result._orm = newResult._orm;
419
+ });
420
+ };
421
+
422
+ const finalTestContext = {
423
+ ...testContext,
424
+ resetDatabase,
425
+ };
426
+
427
+ // Return in the same structure as input
428
+ if (isArray) {
429
+ return {
430
+ fragments: fragmentResults as any, // eslint-disable-line @typescript-eslint/no-explicit-any
431
+ test: finalTestContext,
432
+ };
433
+ } else {
434
+ const keys = Object.keys(fragments as Record<string, FragmentConfig>);
435
+ const fragmentsObject = Object.fromEntries(
436
+ keys.map((key, index) => [key, fragmentResults[index]]),
437
+ );
438
+ return {
439
+ fragments: fragmentsObject as any, // eslint-disable-line @typescript-eslint/no-explicit-any
440
+ test: finalTestContext,
441
+ };
442
+ }
443
+ }
444
+
445
+ export async function createDatabaseFragmentForTest<
446
+ const TFragment extends FragmentConfig,
447
+ const TAdapter extends SupportedAdapter,
448
+ >(
449
+ fragment: TFragment,
450
+ options: MultiFragmentTestOptions<TAdapter>,
451
+ ): Promise<SingleFragmentTestResult<TFragment, TAdapter>> {
452
+ const result = await createDatabaseFragmentsForTest([fragment], options);
249
453
 
250
- // Return an object with getters for fragment properties so they always reference the current fragment
251
454
  return {
252
- get fragment() {
253
- return fragment;
254
- },
255
- get services() {
256
- return fragment.services;
257
- },
258
- get callRoute() {
259
- return fragment.callRoute;
260
- },
261
- get config() {
262
- return fragment.config;
263
- },
264
- get deps() {
265
- return fragment.deps;
266
- },
267
- get additionalContext() {
268
- return fragment.additionalContext;
269
- },
270
- test: testContext,
455
+ fragment: result.fragments[0]!,
456
+ test: result.test,
271
457
  };
272
458
  }