@fragno-dev/test 1.0.2 → 2.0.2

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 (63) hide show
  1. package/.turbo/turbo-build.log +41 -15
  2. package/CHANGELOG.md +112 -0
  3. package/dist/adapters.d.ts +20 -5
  4. package/dist/adapters.d.ts.map +1 -1
  5. package/dist/adapters.js +20 -210
  6. package/dist/adapters.js.map +1 -1
  7. package/dist/db-test.d.ts +120 -18
  8. package/dist/db-test.d.ts.map +1 -1
  9. package/dist/db-test.js +236 -57
  10. package/dist/db-test.js.map +1 -1
  11. package/dist/durable-hooks.d.ts +11 -0
  12. package/dist/durable-hooks.d.ts.map +1 -0
  13. package/dist/durable-hooks.js +17 -0
  14. package/dist/durable-hooks.js.map +1 -0
  15. package/dist/index.d.ts +9 -5
  16. package/dist/index.d.ts.map +1 -1
  17. package/dist/index.js +6 -2
  18. package/dist/index.js.map +1 -1
  19. package/dist/model-checker-actors.d.ts +41 -0
  20. package/dist/model-checker-actors.d.ts.map +1 -0
  21. package/dist/model-checker-actors.js +406 -0
  22. package/dist/model-checker-actors.js.map +1 -0
  23. package/dist/model-checker-adapter.d.ts +32 -0
  24. package/dist/model-checker-adapter.d.ts.map +1 -0
  25. package/dist/model-checker-adapter.js +109 -0
  26. package/dist/model-checker-adapter.js.map +1 -0
  27. package/dist/model-checker.d.ts +128 -0
  28. package/dist/model-checker.d.ts.map +1 -0
  29. package/dist/model-checker.js +443 -0
  30. package/dist/model-checker.js.map +1 -0
  31. package/dist/test-adapters/drizzle-pglite.js +116 -0
  32. package/dist/test-adapters/drizzle-pglite.js.map +1 -0
  33. package/dist/test-adapters/in-memory.js +39 -0
  34. package/dist/test-adapters/in-memory.js.map +1 -0
  35. package/dist/test-adapters/kysely-pglite.js +105 -0
  36. package/dist/test-adapters/kysely-pglite.js.map +1 -0
  37. package/dist/test-adapters/kysely-sqlite.js +87 -0
  38. package/dist/test-adapters/kysely-sqlite.js.map +1 -0
  39. package/dist/test-adapters/model-checker.js +41 -0
  40. package/dist/test-adapters/model-checker.js.map +1 -0
  41. package/dist/tsconfig.tsbuildinfo +1 -0
  42. package/package.json +34 -34
  43. package/src/adapter-conformance.test.ts +324 -0
  44. package/src/adapters.ts +52 -320
  45. package/src/db-roundtrip-guard.test.ts +206 -0
  46. package/src/db-test.test.ts +133 -79
  47. package/src/db-test.ts +583 -99
  48. package/src/durable-hooks.test.ts +58 -0
  49. package/src/durable-hooks.ts +28 -0
  50. package/src/index.test.ts +250 -89
  51. package/src/index.ts +45 -6
  52. package/src/model-checker-actors.test.ts +81 -0
  53. package/src/model-checker-actors.ts +643 -0
  54. package/src/model-checker-adapter.ts +201 -0
  55. package/src/model-checker.test.ts +402 -0
  56. package/src/model-checker.ts +800 -0
  57. package/src/test-adapters/drizzle-pglite.ts +162 -0
  58. package/src/test-adapters/in-memory.ts +56 -0
  59. package/src/test-adapters/kysely-pglite.ts +151 -0
  60. package/src/test-adapters/kysely-sqlite.ts +119 -0
  61. package/src/test-adapters/model-checker.ts +58 -0
  62. package/tsconfig.json +1 -1
  63. package/vitest.config.ts +1 -0
package/dist/db-test.d.ts CHANGED
@@ -1,24 +1,102 @@
1
1
  import { AdapterContext, SupportedAdapter } from "./adapters.js";
2
2
  import { BaseTestContext } from "./index.js";
3
- import { DatabaseAdapter } from "@fragno-dev/db";
3
+ import { DatabaseAdapter, FragnoPublicConfigWithDatabase } from "@fragno-dev/db";
4
4
  import { AnySchema } from "@fragno-dev/db/schema";
5
5
  import { SimpleQueryInterface } from "@fragno-dev/db/query";
6
- import { FragmentInstantiationBuilder, FragnoInstantiatedFragment, FragnoPublicConfig, RequestThisContext } from "@fragno-dev/core";
7
6
  import { AnyRouteOrFactory, FlattenRouteFactories } from "@fragno-dev/core/route";
7
+ import { FragmentDefinition, FragmentInstantiationBuilder, FragnoInstantiatedFragment, FragnoPublicConfig, RequestThisContext } from "@fragno-dev/core";
8
8
 
9
9
  //#region src/db-test.d.ts
10
10
  type BoundServices<T> = { [K in keyof T]: T[K] extends ((this: any, ...args: infer A) => infer R) ? (...args: A) => R : T[K] extends Record<string, unknown> ? BoundServices<T[K]> : T[K] };
11
- type ExtractSchemaFromDeps<TDeps> = TDeps extends {
11
+ type FragmentFactoryContext = {
12
+ adapter: DatabaseAdapter<any>;
13
+ test: BaseTestContext & AdapterContext<SupportedAdapter>;
14
+ };
15
+ type FragmentFactoryResult = FragmentInstantiationBuilder<any,
16
+ // eslint-disable-line @typescript-eslint/no-explicit-any
17
+ any,
18
+ // eslint-disable-line @typescript-eslint/no-explicit-any
19
+ any,
20
+ // eslint-disable-line @typescript-eslint/no-explicit-any
21
+ any,
22
+ // eslint-disable-line @typescript-eslint/no-explicit-any
23
+ any,
24
+ // eslint-disable-line @typescript-eslint/no-explicit-any
25
+ any,
26
+ // eslint-disable-line @typescript-eslint/no-explicit-any
27
+ any,
28
+ // eslint-disable-line @typescript-eslint/no-explicit-any
29
+ any,
30
+ // eslint-disable-line @typescript-eslint/no-explicit-any
31
+ any,
32
+ // eslint-disable-line @typescript-eslint/no-explicit-any
33
+ any,
34
+ // eslint-disable-line @typescript-eslint/no-explicit-any
35
+ any,
36
+ // eslint-disable-line @typescript-eslint/no-explicit-any
37
+ any> | FragnoInstantiatedFragment<any,
38
+ // eslint-disable-line @typescript-eslint/no-explicit-any
39
+ any,
40
+ // eslint-disable-line @typescript-eslint/no-explicit-any
41
+ any,
42
+ // eslint-disable-line @typescript-eslint/no-explicit-any
43
+ any,
44
+ // eslint-disable-line @typescript-eslint/no-explicit-any
45
+ any,
46
+ // eslint-disable-line @typescript-eslint/no-explicit-any
47
+ any,
48
+ // eslint-disable-line @typescript-eslint/no-explicit-any
49
+ any>;
50
+ type HandlerThisContextFromFactoryResult<T> = T extends FragmentInstantiationBuilder<any,
51
+ // eslint-disable-line @typescript-eslint/no-explicit-any
52
+ any,
53
+ // eslint-disable-line @typescript-eslint/no-explicit-any
54
+ any,
55
+ // eslint-disable-line @typescript-eslint/no-explicit-any
56
+ any,
57
+ // eslint-disable-line @typescript-eslint/no-explicit-any
58
+ any,
59
+ // eslint-disable-line @typescript-eslint/no-explicit-any
60
+ any,
61
+ // eslint-disable-line @typescript-eslint/no-explicit-any
62
+ any,
63
+ // eslint-disable-line @typescript-eslint/no-explicit-any
64
+ any,
65
+ // eslint-disable-line @typescript-eslint/no-explicit-any
66
+ infer THandlerThisContext, any,
67
+ // eslint-disable-line @typescript-eslint/no-explicit-any
68
+ any,
69
+ // eslint-disable-line @typescript-eslint/no-explicit-any
70
+ any> ? THandlerThisContext : T extends FragnoInstantiatedFragment<any,
71
+ // eslint-disable-line @typescript-eslint/no-explicit-any
72
+ any,
73
+ // eslint-disable-line @typescript-eslint/no-explicit-any
74
+ any,
75
+ // eslint-disable-line @typescript-eslint/no-explicit-any
76
+ any,
77
+ // eslint-disable-line @typescript-eslint/no-explicit-any
78
+ infer THandlerThisContext, any,
79
+ // eslint-disable-line @typescript-eslint/no-explicit-any
80
+ any> ? THandlerThisContext : RequestThisContext;
81
+ type FragmentResultFromFactoryResult<T> = T extends FragmentInstantiationBuilder<any,
82
+ // eslint-disable-line @typescript-eslint/no-explicit-any
83
+ any,
84
+ // eslint-disable-line @typescript-eslint/no-explicit-any
85
+ infer TDeps, infer TBaseServices, infer TServices, any,
86
+ // eslint-disable-line @typescript-eslint/no-explicit-any
87
+ any,
88
+ // eslint-disable-line @typescript-eslint/no-explicit-any
89
+ infer TServiceThisContext, infer THandlerThisContext, infer TRequestStorage, infer TRoutesOrFactories, any> ? FragmentResult<TDeps, BoundServices<TBaseServices & TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage, FlattenRouteFactories<TRoutesOrFactories>, ExtractSchemaFromDeps<TDeps>> : T extends FragnoInstantiatedFragment<infer TRoutes, infer TDeps, infer TServices, infer TServiceThisContext, infer THandlerThisContext, infer TRequestStorage, any> ? FragmentResult<TDeps, BoundServices<TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage, TRoutes, ExtractSchemaFromDeps<TDeps>> : never;
90
+ type ExtractSchemaFromDeps<TDeps$1> = TDeps$1 extends {
12
91
  schema: infer TSchema extends AnySchema;
13
92
  } ? TSchema : AnySchema;
14
- type AnyLinkedFragments = Record<string, any>;
15
- interface FragmentResult<TDeps, TServices extends Record<string, unknown>, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, TRoutes extends readonly any[],
16
- // eslint-disable-line @typescript-eslint/no-explicit-any
17
- TSchema$1 extends AnySchema, TLinkedFragments extends AnyLinkedFragments = {}> {
18
- fragment: FragnoInstantiatedFragment<TRoutes, TDeps, TServices, TServiceThisContext, THandlerThisContext, TRequestStorage, FragnoPublicConfig, TLinkedFragments>;
19
- services: TServices;
20
- deps: TDeps;
21
- callRoute: FragnoInstantiatedFragment<TRoutes, TDeps, TServices, TServiceThisContext, THandlerThisContext, TRequestStorage, FragnoPublicConfig, TLinkedFragments>["callRoute"];
93
+ interface FragmentResult<TDeps$1, TServices$1 extends Record<string, unknown>, TServiceThisContext$1 extends RequestThisContext, THandlerThisContext$1 extends RequestThisContext, TRequestStorage$1, TRoutes$1 extends readonly any[],
94
+ // eslint-disable-line @typescript-eslint/no-explicit-any
95
+ TSchema$1 extends AnySchema> {
96
+ fragment: FragnoInstantiatedFragment<TRoutes$1, TDeps$1, TServices$1, TServiceThisContext$1, THandlerThisContext$1, TRequestStorage$1, FragnoPublicConfig>;
97
+ services: TServices$1;
98
+ deps: TDeps$1;
99
+ callRoute: FragnoInstantiatedFragment<TRoutes$1, TDeps$1, TServices$1, TServiceThisContext$1, THandlerThisContext$1, TRequestStorage$1, FragnoPublicConfig>["callRoute"];
22
100
  db: SimpleQueryInterface<TSchema$1>;
23
101
  }
24
102
  type AnyFragmentResult = FragmentResult<any,
@@ -33,8 +111,6 @@ any,
33
111
  // eslint-disable-line @typescript-eslint/no-explicit-any
34
112
  any,
35
113
  // eslint-disable-line @typescript-eslint/no-explicit-any
36
- any,
37
- // eslint-disable-line @typescript-eslint/no-explicit-any
38
114
  any>;
39
115
  /**
40
116
  * Test context combining base and adapter-specific functionality
@@ -64,6 +140,11 @@ declare class DatabaseFragmentsTestBuilder<TFragments extends Record<string, Any
64
140
  * Set the test adapter configuration
65
141
  */
66
142
  withTestAdapter<TNewAdapter extends SupportedAdapter>(adapter: TNewAdapter): DatabaseFragmentsTestBuilder<TFragments, TNewAdapter, TFirstFragmentThisContext>;
143
+ /**
144
+ * Opt out of the default roundtrip guard (enabled by default), or override its configuration.
145
+ * Useful for allowing multi-roundtrip routes in tests.
146
+ */
147
+ withDbRoundtripGuard(guard?: FragnoPublicConfigWithDatabase["dbRoundtripGuard"]): DatabaseFragmentsTestBuilder<TFragments, TAdapter, TFirstFragmentThisContext>;
67
148
  /**
68
149
  * Add a fragment to the test setup
69
150
  *
@@ -71,11 +152,32 @@ declare class DatabaseFragmentsTestBuilder<TFragments extends Record<string, Any
71
152
  * @param builder - Pre-configured instantiation builder
72
153
  * @param options - Additional options (optional)
73
154
  */
74
- withFragment<TName extends string, TConfig, TOptions extends FragnoPublicConfig, TDeps, TBaseServices extends Record<string, unknown>, TServices extends Record<string, unknown>, TServiceDependencies, TPrivateServices extends Record<string, unknown>, TServiceThisContext extends RequestThisContext, THandlerThisContext extends RequestThisContext, TRequestStorage, TRoutesOrFactories extends readonly AnyRouteOrFactory[], TLinkedFragments extends AnyLinkedFragments>(name: TName, builder: FragmentInstantiationBuilder<TConfig, TOptions, TDeps, TBaseServices, TServices, TServiceDependencies, TPrivateServices, TServiceThisContext, THandlerThisContext, TRequestStorage, TRoutesOrFactories, TLinkedFragments>, options?: {
155
+ withFragment<TName extends string, TConfig, TOptions extends FragnoPublicConfig, TDeps$1, TBaseServices$1 extends Record<string, unknown>, TServices$1 extends Record<string, unknown>, TServiceDependencies, TPrivateServices extends Record<string, unknown>, TServiceThisContext$1 extends RequestThisContext, THandlerThisContext$1 extends RequestThisContext, TRequestStorage$1, TRoutesOrFactories$1 extends readonly AnyRouteOrFactory[], TInternalRoutes extends readonly AnyRouteOrFactory[]>(name: TName, builder: FragmentInstantiationBuilder<TConfig, TOptions, TDeps$1, TBaseServices$1, TServices$1, TServiceDependencies, TPrivateServices, TServiceThisContext$1, THandlerThisContext$1, TRequestStorage$1, TRoutesOrFactories$1, TInternalRoutes>, options?: {
156
+ migrateToVersion?: number;
157
+ }): DatabaseFragmentsTestBuilder<TFragments & { [K in TName]: FragmentResult<TDeps$1, BoundServices<TBaseServices$1 & TServices$1>, TServiceThisContext$1, THandlerThisContext$1, TRequestStorage$1, FlattenRouteFactories<TRoutesOrFactories$1>, ExtractSchemaFromDeps<TDeps$1>> }, TAdapter, keyof TFragments extends never ? THandlerThisContext$1 : TFirstFragmentThisContext>;
158
+ /**
159
+ * Add a pre-built fragment instance to the test setup
160
+ *
161
+ * @param name - Unique name for the fragment
162
+ * @param fragment - Already-built fragment instance
163
+ */
164
+ withFragmentInstance<TName extends string, TRoutes$1 extends readonly any[],
165
+ // eslint-disable-line @typescript-eslint/no-explicit-any
166
+ TDeps$1, TServices$1 extends Record<string, unknown>, TServiceThisContext$1 extends RequestThisContext, THandlerThisContext$1 extends RequestThisContext, TRequestStorage$1>(name: TName, fragment: FragnoInstantiatedFragment<TRoutes$1, TDeps$1, TServices$1, TServiceThisContext$1, THandlerThisContext$1, TRequestStorage$1, FragnoPublicConfig>, options?: {
167
+ migrateToVersion?: number;
168
+ }): DatabaseFragmentsTestBuilder<TFragments & { [K in TName]: FragmentResult<TDeps$1, BoundServices<TServices$1>, TServiceThisContext$1, THandlerThisContext$1, TRequestStorage$1, TRoutes$1, ExtractSchemaFromDeps<TDeps$1>> }, TAdapter, keyof TFragments extends never ? THandlerThisContext$1 : TFirstFragmentThisContext>;
169
+ /**
170
+ * Add a fragment factory to the test setup.
171
+ * The factory runs after the adapter is created.
172
+ *
173
+ * @param name - Unique name for the fragment
174
+ * @param definition - Fragment definition (used to extract schema/namespace)
175
+ * @param factory - Factory that returns a builder or a pre-built fragment
176
+ */
177
+ withFragmentFactory<TName extends string, TFactoryResult extends FragmentFactoryResult>(name: TName, definition: FragmentDefinition<any, any, any, any, any, any, any, any, any, any, any>, factory: (context: FragmentFactoryContext) => TFactoryResult, options?: {
75
178
  migrateToVersion?: number;
76
- }): DatabaseFragmentsTestBuilder<TFragments & { [K in TName]: FragmentResult<TDeps, BoundServices<TBaseServices & TServices>, TServiceThisContext, THandlerThisContext, TRequestStorage, FlattenRouteFactories<TRoutesOrFactories>, ExtractSchemaFromDeps<TDeps>,
77
- // Extract actual schema type from deps
78
- TLinkedFragments> }, TAdapter, keyof TFragments extends never ? THandlerThisContext : TFirstFragmentThisContext>;
179
+ config?: any;
180
+ }): DatabaseFragmentsTestBuilder<TFragments & { [K in TName]: FragmentResultFromFactoryResult<TFactoryResult> }, TAdapter, keyof TFragments extends never ? HandlerThisContextFromFactoryResult<TFactoryResult> : TFirstFragmentThisContext>;
79
181
  /**
80
182
  * Build the test setup and return fragments and test context
81
183
  */
@@ -125,5 +227,5 @@ declare class DatabaseFragmentsTestBuilder<TFragments extends Record<string, Any
125
227
  */
126
228
  declare function buildDatabaseFragmentsTest(): DatabaseFragmentsTestBuilder<{}, undefined, RequestThisContext>;
127
229
  //#endregion
128
- export { DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest };
230
+ export { AnyFragmentResult, DatabaseFragmentsTestBuilder, buildDatabaseFragmentsTest };
129
231
  //# sourceMappingURL=db-test.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"db-test.d.ts","names":[],"sources":["../src/db-test.ts"],"sourcesContent":[],"mappings":"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"}
1
+ {"version":3,"file":"db-test.d.ts","names":[],"sources":["../src/db-test.ts"],"sourcesContent":[],"mappings":"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"}
package/dist/db-test.js CHANGED
@@ -1,12 +1,24 @@
1
1
  import { createAdapter } from "./adapters.js";
2
+ import { drainDurableHooks } from "./durable-hooks.js";
2
3
 
3
4
  //#region src/db-test.ts
5
+ const disableAutoSchedule = (options) => {
6
+ const durableHooks = options.durableHooks ?? {};
7
+ return {
8
+ ...options,
9
+ durableHooks: {
10
+ ...durableHooks,
11
+ autoSchedule: false
12
+ }
13
+ };
14
+ };
4
15
  /**
5
16
  * Builder for creating multiple database fragments for testing
6
17
  */
7
18
  var DatabaseFragmentsTestBuilder = class {
8
19
  #adapter;
9
20
  #fragments = /* @__PURE__ */ new Map();
21
+ #dbRoundtripGuard = true;
10
22
  /**
11
23
  * Set the test adapter configuration
12
24
  */
@@ -15,6 +27,14 @@ var DatabaseFragmentsTestBuilder = class {
15
27
  return this;
16
28
  }
17
29
  /**
30
+ * Opt out of the default roundtrip guard (enabled by default), or override its configuration.
31
+ * Useful for allowing multi-roundtrip routes in tests.
32
+ */
33
+ withDbRoundtripGuard(guard = false) {
34
+ this.#dbRoundtripGuard = guard;
35
+ return this;
36
+ }
37
+ /**
18
38
  * Add a fragment to the test setup
19
39
  *
20
40
  * @param name - Unique name for the fragment
@@ -23,6 +43,7 @@ var DatabaseFragmentsTestBuilder = class {
23
43
  */
24
44
  withFragment(name, builder, options) {
25
45
  this.#fragments.set(name, {
46
+ kind: "builder",
26
47
  definition: builder.definition,
27
48
  builder,
28
49
  migrateToVersion: options?.migrateToVersion
@@ -30,30 +51,73 @@ var DatabaseFragmentsTestBuilder = class {
30
51
  return this;
31
52
  }
32
53
  /**
54
+ * Add a pre-built fragment instance to the test setup
55
+ *
56
+ * @param name - Unique name for the fragment
57
+ * @param fragment - Already-built fragment instance
58
+ */
59
+ withFragmentInstance(name, fragment, options) {
60
+ this.#fragments.set(name, {
61
+ kind: "instance",
62
+ fragment,
63
+ migrateToVersion: options?.migrateToVersion
64
+ });
65
+ return this;
66
+ }
67
+ /**
68
+ * Add a fragment factory to the test setup.
69
+ * The factory runs after the adapter is created.
70
+ *
71
+ * @param name - Unique name for the fragment
72
+ * @param definition - Fragment definition (used to extract schema/namespace)
73
+ * @param factory - Factory that returns a builder or a pre-built fragment
74
+ */
75
+ withFragmentFactory(name, definition, factory, options) {
76
+ this.#fragments.set(name, {
77
+ kind: "factory",
78
+ definition,
79
+ factory,
80
+ config: options?.config,
81
+ migrateToVersion: options?.migrateToVersion
82
+ });
83
+ return this;
84
+ }
85
+ /**
33
86
  * Build the test setup and return fragments and test context
34
87
  */
35
88
  async build() {
36
89
  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()");
90
+ if (this.#fragments.size === 0) throw new Error("At least one fragment must be added using withFragment(), withFragmentFactory(), or withFragmentInstance().");
38
91
  const adapterConfig = this.#adapter;
39
- const fragmentNames = Array.from(this.#fragments.keys());
40
- const fragmentConfigs = Array.from(this.#fragments.values());
92
+ const fragmentEntries = Array.from(this.#fragments.entries());
93
+ const fragmentNames = fragmentEntries.map(([name]) => name);
41
94
  const schemaConfigs = [];
42
- const builderConfigs = [];
43
- for (const fragmentConfig of fragmentConfigs) {
44
- const builder = fragmentConfig.builder;
45
- const definition = builder.definition;
95
+ const fragmentPlans = [];
96
+ const extractSchemaFromDefinition = (definition, actualConfig, actualOptions) => {
46
97
  let schema;
47
98
  let namespace;
48
99
  if (definition.dependencies) try {
49
100
  const mockAdapter = {
50
101
  createQueryEngine: () => ({ schema: null }),
51
- contextStorage: { run: (_data, fn) => fn() }
102
+ getSchemaVersion: async () => void 0,
103
+ namingStrategy: {
104
+ namespaceScope: "suffix",
105
+ namespaceToSchema: (value) => value,
106
+ tableName: (logicalTable, ns) => ns ? `${logicalTable}_${ns}` : logicalTable,
107
+ columnName: (logicalColumn) => logicalColumn,
108
+ indexName: (logicalIndex) => logicalIndex,
109
+ uniqueIndexName: (logicalIndex) => logicalIndex,
110
+ foreignKeyName: ({ referenceName }) => referenceName
111
+ },
112
+ contextStorage: { run: (_data, fn) => fn() },
113
+ close: async () => {}
52
114
  };
53
- const actualConfig = builder.config ?? {};
54
115
  const deps = definition.dependencies({
55
- config: actualConfig,
56
- options: { databaseAdapter: mockAdapter }
116
+ config: actualConfig ?? {},
117
+ options: {
118
+ ...actualOptions,
119
+ databaseAdapter: mockAdapter
120
+ }
57
121
  });
58
122
  if (deps && typeof deps === "object" && "schema" in deps) {
59
123
  schema = deps.schema;
@@ -64,69 +128,165 @@ var DatabaseFragmentsTestBuilder = class {
64
128
  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)).`);
65
129
  }
66
130
  if (!schema) throw new Error(`Fragment '${definition.name}' does not have a database schema. Make sure you're using defineFragment().extend(withDatabase(schema)).`);
67
- if (!namespace) throw new Error(`Fragment '${definition.name}' does not have a namespace in dependencies. This should be automatically provided by withDatabase().`);
68
- schemaConfigs.push({
131
+ if (namespace === void 0) throw new Error(`Fragment '${definition.name}' does not have a namespace in dependencies. This should be automatically provided by withDatabase().`);
132
+ return {
69
133
  schema,
70
- namespace,
134
+ namespace
135
+ };
136
+ };
137
+ for (const [name, fragmentConfig] of fragmentEntries) {
138
+ if (fragmentConfig.kind === "builder") {
139
+ const builder = fragmentConfig.builder;
140
+ const definition = builder.definition;
141
+ const { schema, namespace } = extractSchemaFromDefinition(definition, builder.config ?? {}, builder.options ?? {});
142
+ schemaConfigs.push({
143
+ schema,
144
+ namespace,
145
+ migrateToVersion: fragmentConfig.migrateToVersion
146
+ });
147
+ fragmentPlans.push({
148
+ name,
149
+ kind: "builder",
150
+ schema,
151
+ namespace,
152
+ migrateToVersion: fragmentConfig.migrateToVersion,
153
+ builderConfig: {
154
+ builder: fragmentConfig.builder,
155
+ definition: fragmentConfig.definition,
156
+ config: builder.config ?? {},
157
+ routes: builder.routes ?? [],
158
+ options: builder.options ?? {}
159
+ }
160
+ });
161
+ continue;
162
+ }
163
+ if (fragmentConfig.kind === "factory") {
164
+ const definition = fragmentConfig.definition;
165
+ const { schema, namespace } = extractSchemaFromDefinition(definition, fragmentConfig.config ?? {});
166
+ schemaConfigs.push({
167
+ schema,
168
+ namespace,
169
+ migrateToVersion: fragmentConfig.migrateToVersion
170
+ });
171
+ fragmentPlans.push({
172
+ name,
173
+ kind: "factory",
174
+ schema,
175
+ namespace,
176
+ migrateToVersion: fragmentConfig.migrateToVersion,
177
+ factory: fragmentConfig.factory
178
+ });
179
+ continue;
180
+ }
181
+ const fragment = fragmentConfig.fragment;
182
+ const deps = fragment.$internal?.deps;
183
+ if (!deps?.schema) throw new Error(`Fragment '${name}' does not have a database schema in deps. Make sure you're using defineFragment().extend(withDatabase(schema)).`);
184
+ schemaConfigs.push({
185
+ schema: deps.schema,
186
+ namespace: deps.namespace ?? null,
71
187
  migrateToVersion: fragmentConfig.migrateToVersion
72
188
  });
73
- builderConfigs.push({
74
- config: builder.config ?? {},
75
- routes: builder.routes ?? [],
76
- options: builder.options ?? {}
189
+ fragmentPlans.push({
190
+ name,
191
+ kind: "instance",
192
+ schema: deps.schema,
193
+ namespace: deps.namespace ?? null,
194
+ migrateToVersion: fragmentConfig.migrateToVersion,
195
+ fragment
77
196
  });
78
197
  }
79
198
  const { testContext, adapter } = await createAdapter(adapterConfig, schemaConfigs);
199
+ const resolveDbRoundtripGuardOption = (options) => {
200
+ if (options && typeof options === "object") {
201
+ if (Object.prototype.hasOwnProperty.call(options, "dbRoundtripGuard")) return options.dbRoundtripGuard;
202
+ }
203
+ return this.#dbRoundtripGuard;
204
+ };
205
+ const mergeBuilderOptions = (options) => {
206
+ const resolvedOptions = disableAutoSchedule(options ?? {});
207
+ const merged = {
208
+ ...resolvedOptions,
209
+ databaseAdapter: adapter
210
+ };
211
+ const guardOption = resolveDbRoundtripGuardOption(resolvedOptions);
212
+ if (guardOption !== void 0) merged["dbRoundtripGuard"] = guardOption;
213
+ return merged;
214
+ };
80
215
  const createFragments = () => {
216
+ const resolveBuilderConfig = (builder) => ({
217
+ builder,
218
+ definition: builder.definition,
219
+ config: builder.config ?? {},
220
+ routes: builder.routes ?? [],
221
+ options: builder.options ?? {}
222
+ });
223
+ const isBuilder = (value) => Boolean(value) && typeof value === "object" && "build" in value && "definition" in value;
81
224
  const providedServicesByName = {};
82
- const fragmentResults$1 = [];
83
- for (let i = 0; i < fragmentConfigs.length; i++) {
84
- const fragmentConfig = fragmentConfigs[i];
85
- const builderConfig = builderConfigs[i];
86
- const namespace = schemaConfigs[i].namespace;
87
- const schema = schemaConfigs[i].schema;
88
- const orm = testContext.getOrm(namespace);
89
- const mergedOptions = {
90
- ...builderConfig.options,
91
- databaseAdapter: adapter
92
- };
93
- const fragment = fragmentConfig.builder.withOptions(mergedOptions).build();
225
+ const instanceResults = /* @__PURE__ */ new Map();
226
+ const builderConfigs = /* @__PURE__ */ new Map();
227
+ for (const plan of fragmentPlans) {
228
+ const orm = testContext.getOrm(plan.namespace);
229
+ let fragment;
230
+ let builderConfig = plan.builderConfig;
231
+ if (plan.kind === "factory") {
232
+ const result = plan.cachedFactoryResult ?? plan.factory({
233
+ adapter,
234
+ test: testContext
235
+ });
236
+ if (!plan.cachedFactoryResult) plan.cachedFactoryResult = result;
237
+ if (isBuilder(result)) builderConfig = resolveBuilderConfig(result);
238
+ else fragment = result;
239
+ }
240
+ const usesBuilder = plan.kind === "builder" || !!builderConfig;
241
+ if (usesBuilder) {
242
+ const resolvedBuilderConfig = builderConfig ?? plan.builderConfig;
243
+ const mergedOptions = mergeBuilderOptions(resolvedBuilderConfig.options);
244
+ fragment = resolvedBuilderConfig.builder.withOptions(mergedOptions).build();
245
+ builderConfigs.set(plan.name, resolvedBuilderConfig);
246
+ } else {
247
+ fragment = fragment ?? plan.fragment;
248
+ const deps = fragment.$internal?.deps;
249
+ if (deps?.databaseAdapter && deps.databaseAdapter !== adapter) throw new Error(`Fragment '${plan.name}' was built with a different database adapter instance. Use withFragment() or ensure the fragment uses the same adapter instance as the test builder.`);
250
+ }
251
+ if (!fragment) throw new Error(`Fragment '${plan.name}' did not return a valid fragment instance from its factory.`);
94
252
  for (const [serviceName, serviceImpl] of Object.entries(fragment.services)) providedServicesByName[serviceName] = {
95
253
  service: serviceImpl,
96
254
  orm
97
255
  };
98
- const deps = fragment.$internal?.deps;
99
- fragmentResults$1.push({
100
- fragment,
101
- services: fragment.services,
102
- deps: deps || {},
103
- callRoute: fragment.callRoute.bind(fragment),
104
- get db() {
105
- return orm;
106
- },
107
- _orm: orm,
108
- _schema: schema
109
- });
256
+ if (!usesBuilder) {
257
+ const deps = fragment.$internal?.deps;
258
+ instanceResults.set(plan.name, {
259
+ fragment,
260
+ services: fragment.services,
261
+ deps: deps || {},
262
+ callRoute: fragment.callRoute.bind(fragment),
263
+ get db() {
264
+ return orm;
265
+ },
266
+ _orm: orm,
267
+ _schema: plan.schema
268
+ });
269
+ }
110
270
  }
111
- for (let i = 0; i < fragmentConfigs.length; i++) {
112
- const fragmentConfig = fragmentConfigs[i];
113
- const definition = fragmentConfig.builder.definition;
114
- const builderConfig = builderConfigs[i];
115
- const namespace = schemaConfigs[i].namespace;
116
- const schema = schemaConfigs[i].schema;
117
- const orm = testContext.getOrm(namespace);
271
+ const fragmentResults$1 = [];
272
+ for (const plan of fragmentPlans) {
273
+ const orm = testContext.getOrm(plan.namespace);
274
+ if (instanceResults.has(plan.name)) {
275
+ fragmentResults$1.push(instanceResults.get(plan.name));
276
+ continue;
277
+ }
278
+ const builderConfig = builderConfigs.get(plan.name);
279
+ if (!builderConfig) throw new Error(`Fragment '${plan.name}' was expected to produce a builder for service wiring.`);
280
+ const definition = builderConfig.definition;
118
281
  const serviceImplementations = {};
119
282
  const serviceDependencies = definition.serviceDependencies;
120
283
  if (serviceDependencies) {
121
284
  for (const serviceName of Object.keys(serviceDependencies)) if (providedServicesByName[serviceName]) serviceImplementations[serviceName] = providedServicesByName[serviceName].service;
122
285
  }
123
- const mergedOptions = {
124
- ...builderConfig.options,
125
- databaseAdapter: adapter
126
- };
127
- const fragment = fragmentConfig.builder.withOptions(mergedOptions).withServices(serviceImplementations).build();
286
+ const mergedOptions = mergeBuilderOptions(builderConfig.options);
287
+ const fragment = builderConfig.builder.withOptions(mergedOptions).withServices(serviceImplementations).build();
128
288
  const deps = fragment.$internal?.deps;
129
- fragmentResults$1[i] = {
289
+ fragmentResults$1.push({
130
290
  fragment,
131
291
  services: fragment.services,
132
292
  deps: deps || {},
@@ -135,8 +295,8 @@ var DatabaseFragmentsTestBuilder = class {
135
295
  return orm;
136
296
  },
137
297
  _orm: orm,
138
- _schema: schema
139
- };
298
+ _schema: plan.schema
299
+ });
140
300
  }
141
301
  return fragmentResults$1;
142
302
  };
@@ -155,9 +315,28 @@ var DatabaseFragmentsTestBuilder = class {
155
315
  };
156
316
  const firstFragment = fragmentResults[0]?.fragment;
157
317
  if (!firstFragment) throw new Error("At least one fragment must be added");
318
+ const originalCleanup = testContext.cleanup;
319
+ const cleanup = async () => {
320
+ let drainError;
321
+ let cleanupError;
322
+ for (const result of fragmentResults) try {
323
+ await drainDurableHooks(result.fragment);
324
+ } catch (error) {
325
+ if (!drainError) drainError = error;
326
+ }
327
+ try {
328
+ await originalCleanup();
329
+ } catch (error) {
330
+ cleanupError = error;
331
+ }
332
+ if (drainError && cleanupError) throw new AggregateError([drainError, cleanupError], "Failed to drain durable hooks and clean up test context");
333
+ if (drainError) throw drainError;
334
+ if (cleanupError) throw cleanupError;
335
+ };
158
336
  const finalTestContext = {
159
337
  ...testContext,
160
338
  resetDatabase,
339
+ cleanup,
161
340
  adapter,
162
341
  inContext: firstFragment.inContext.bind(firstFragment)
163
342
  };