@vertz/testing 0.2.1 → 0.2.3

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/dist/index.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- import { NamedMiddlewareDef, NamedModule, NamedServiceDef } from "@vertz/server";
2
- type DeepPartial<T> = { [P in keyof T]? : T[P] extends object ? DeepPartial<T[P]> : T[P] };
1
+ import { HandlerCtx, NamedMiddlewareDef } from "@vertz/server";
3
2
  /**
4
3
  * Route map entry shape that each route in AppRouteMap must follow.
5
4
  */
@@ -20,11 +19,6 @@ interface TestResponse<TResponse = unknown> {
20
19
  ok: boolean;
21
20
  }
22
21
  interface TestRequestBuilder<TResponse = unknown> extends PromiseLike<TestResponse<TResponse>> {
23
- mock<
24
- TDeps,
25
- TState,
26
- TMethods
27
- >(service: NamedServiceDef<TDeps, TState, TMethods>, impl: DeepPartial<TMethods>): TestRequestBuilder<TResponse>;
28
22
  mockMiddleware<
29
23
  TReq extends Record<string, unknown>,
30
24
  TProv extends Record<string, unknown>
@@ -37,16 +31,42 @@ interface RequestOptions<TBody = unknown> {
37
31
  body?: TBody;
38
32
  headers?: Record<string, string>;
39
33
  }
34
+ type SchemaLike = {
35
+ parse(value: unknown): {
36
+ ok: boolean;
37
+ data?: unknown;
38
+ error?: unknown;
39
+ };
40
+ };
40
41
  /**
41
- * Untyped test app interface for backwards compatibility.
42
+ * Route entry for configuring test app routes.
43
+ */
44
+ interface TestRouteEntry {
45
+ method: string;
46
+ path: string;
47
+ handler: (ctx: HandlerCtx) => unknown;
48
+ bodySchema?: SchemaLike;
49
+ querySchema?: SchemaLike;
50
+ headersSchema?: SchemaLike;
51
+ responseSchema?: {
52
+ safeParse(value: unknown): {
53
+ ok: boolean;
54
+ error?: {
55
+ message: string;
56
+ };
57
+ };
58
+ };
59
+ }
60
+ /**
61
+ * Configuration for creating a test app.
62
+ */
63
+ interface TestAppConfig {
64
+ routes?: TestRouteEntry[];
65
+ }
66
+ /**
67
+ * Test app interface.
42
68
  */
43
69
  interface TestApp {
44
- register(module: NamedModule, options?: Record<string, unknown>): TestApp;
45
- mock<
46
- TDeps,
47
- TState,
48
- TMethods
49
- >(service: NamedServiceDef<TDeps, TState, TMethods>, impl: DeepPartial<TMethods>): TestApp;
50
70
  mockMiddleware<
51
71
  TReq extends Record<string, unknown>,
52
72
  TProv extends Record<string, unknown>
@@ -64,12 +84,6 @@ interface TestApp {
64
84
  * @template TRouteMap - Route map interface mapping route keys (e.g., 'GET /users') to their types.
65
85
  */
66
86
  interface TestAppWithRoutes<TRouteMap extends RouteMapEntry> {
67
- register(module: NamedModule, options?: Record<string, unknown>): TestAppWithRoutes<TRouteMap>;
68
- mock<
69
- TDeps,
70
- TState,
71
- TMethods
72
- >(service: NamedServiceDef<TDeps, TState, TMethods>, impl: DeepPartial<TMethods>): TestAppWithRoutes<TRouteMap>;
73
87
  mockMiddleware<
74
88
  TReq extends Record<string, unknown>,
75
89
  TProv extends Record<string, unknown>
@@ -107,38 +121,15 @@ interface TestAppWithRoutes<TRouteMap extends RouteMapEntry> {
107
121
  } ? TResponse : unknown>;
108
122
  }
109
123
  /**
110
- * Creates a test app for making HTTP requests against registered modules.
111
- * Returns an untyped app for backwards compatibility.
112
- * @returns A test app instance for registering modules and making requests.
124
+ * Creates a test app for making HTTP requests against registered routes.
125
+ * @returns A test app instance for making requests.
113
126
  */
114
- declare function createTestApp(): TestApp;
127
+ declare function createTestApp(config?: TestAppConfig): TestApp;
115
128
  /**
116
129
  * Creates a typed test app with route map for type-safe requests.
117
130
  * @template TRouteMap - Route map interface mapping route keys to their types.
118
131
  * @returns A typed test app instance.
119
132
  */
120
- declare function createTestApp<TRouteMap extends RouteMapEntry>(): TestAppWithRoutes<TRouteMap>;
121
- import { NamedServiceDef as NamedServiceDef2 } from "@vertz/core";
122
- interface TestServiceBuilder<
123
- TDeps,
124
- TState,
125
- TMethods,
126
- TOptions extends Record<string, unknown> = Record<string, unknown>,
127
- TEnv extends Record<string, unknown> = Record<string, unknown>
128
- > extends PromiseLike<TMethods> {
129
- mock<
130
- TDep,
131
- TDepState,
132
- TMock
133
- >(service: NamedServiceDef2<TDep, TDepState, TMock>, impl: DeepPartial<TMock>): TestServiceBuilder<TDeps, TState, TMethods, TOptions, TEnv>;
134
- options(opts: Partial<TOptions>): TestServiceBuilder<TDeps, TState, TMethods, TOptions, TEnv>;
135
- env(env: Partial<TEnv>): TestServiceBuilder<TDeps, TState, TMethods, TOptions, TEnv>;
136
- }
137
- declare function createTestService<
138
- TDeps,
139
- TState,
140
- TMethods,
141
- TOptions extends Record<string, unknown> = Record<string, unknown>,
142
- TEnv extends Record<string, unknown> = Record<string, unknown>
143
- >(serviceDef: NamedServiceDef2<TDeps, TState, TMethods, TOptions, TEnv>): TestServiceBuilder<TDeps, TState, TMethods, TOptions, TEnv>;
144
- export { createTestService, createTestApp, TestServiceBuilder, TestResponse, TestRequestBuilder, TestApp, RouteMapEntry, RequestOptions, DeepPartial };
133
+ declare function createTestApp<TRouteMap extends RouteMapEntry>(config?: TestAppConfig): TestAppWithRoutes<TRouteMap>;
134
+ type DeepPartial<T> = { [P in keyof T]? : T[P] extends object ? DeepPartial<T[P]> : T[P] };
135
+ export { createTestApp, TestRouteEntry, TestResponse, TestRequestBuilder, TestAppConfig, TestApp, RouteMapEntry, RequestOptions, DeepPartial };
package/dist/index.js CHANGED
@@ -8,9 +8,7 @@ import {
8
8
  runMiddlewareChain,
9
9
  Trie
10
10
  } from "@vertz/core/internals";
11
- import {
12
- BadRequestException
13
- } from "@vertz/server";
11
+ import { BadRequestException } from "@vertz/server";
14
12
  function validateSchema(schema, value, label) {
15
13
  const result = schema.parse(value);
16
14
  if (!result.ok) {
@@ -20,54 +18,21 @@ function validateSchema(schema, value, label) {
20
18
  return result.data;
21
19
  }
22
20
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"];
23
- function createTestApp() {
24
- const serviceMocks = new Map;
21
+ function createTestApp(config) {
25
22
  const middlewareMocks = new Map;
26
- const registrations = [];
23
+ const routes = config?.routes ?? [];
27
24
  let envOverrides = {};
28
25
  function buildHandler(perRequest) {
29
26
  const trie = new Trie;
30
- const realServices = new Map;
31
- for (const { module, options } of registrations) {
32
- for (const service of module.services) {
33
- if (!realServices.has(service)) {
34
- let parsedOptions = {};
35
- if (service.options && options) {
36
- const parsed = service.options.safeParse(options);
37
- if (parsed.ok) {
38
- parsedOptions = parsed.data;
39
- } else {
40
- throw new Error(`Invalid options for service ${service.moduleName}: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
41
- }
42
- }
43
- const env = {};
44
- realServices.set(service, service.methods({}, undefined, parsedOptions, env));
45
- }
46
- }
47
- }
48
- const serviceMap = new Map([...realServices, ...serviceMocks, ...perRequest.services]);
49
- for (const { module, options } of registrations) {
50
- for (const router of module.routers) {
51
- const resolvedServices = {};
52
- if (router.inject) {
53
- for (const [name, serviceDef] of Object.entries(router.inject)) {
54
- resolvedServices[name] = serviceMap.get(serviceDef);
55
- }
56
- }
57
- for (const route of router.routes) {
58
- const fullPath = router.prefix + route.path;
59
- const entry = {
60
- handler: route.config.handler,
61
- options: options ?? {},
62
- services: resolvedServices,
63
- responseSchema: route.config.response,
64
- bodySchema: route.config.body,
65
- querySchema: route.config.query,
66
- headersSchema: route.config.headers
67
- };
68
- trie.add(route.method, fullPath, entry);
69
- }
70
- }
27
+ for (const route of routes) {
28
+ const entry = {
29
+ handler: route.handler,
30
+ responseSchema: route.responseSchema,
31
+ bodySchema: route.bodySchema,
32
+ querySchema: route.querySchema,
33
+ headersSchema: route.headersSchema
34
+ };
35
+ trie.add(route.method, route.path, entry);
71
36
  }
72
37
  const effectiveMiddlewareMocks = new Map([...middlewareMocks, ...perRequest.middlewares]);
73
38
  return async (request) => {
@@ -112,8 +77,8 @@ function createTestApp() {
112
77
  headers: validatedHeaders,
113
78
  raw,
114
79
  middlewareState,
115
- services: entry.services,
116
- options: entry.options,
80
+ services: {},
81
+ options: {},
117
82
  env: envOverrides
118
83
  });
119
84
  const result = await entry.handler(ctx);
@@ -155,14 +120,9 @@ function createTestApp() {
155
120
  }
156
121
  function createRequestBuilder(method, path, options) {
157
122
  const perRequest = {
158
- services: new Map,
159
123
  middlewares: new Map
160
124
  };
161
125
  const builder = {
162
- mock(service, impl) {
163
- perRequest.services.set(service, impl);
164
- return builder;
165
- },
166
126
  mockMiddleware(middleware, result) {
167
127
  perRequest.middlewares.set(middleware, result);
168
128
  return builder;
@@ -178,14 +138,6 @@ function createTestApp() {
178
138
  (path, options) => createRequestBuilder(m, path, options)
179
139
  ]));
180
140
  const app = {
181
- register(module, options) {
182
- registrations.push({ module, options });
183
- return app;
184
- },
185
- mock(service, impl) {
186
- serviceMocks.set(service, impl);
187
- return app;
188
- },
189
141
  mockMiddleware(middleware, result) {
190
142
  middlewareMocks.set(middleware, result);
191
143
  return app;
@@ -205,63 +157,6 @@ class ResponseValidationError extends Error {
205
157
  this.name = "ResponseValidationError";
206
158
  }
207
159
  }
208
- // src/test-service.ts
209
- function createTestService(serviceDef) {
210
- const serviceMocks = new Map;
211
- let providedOptions = {};
212
- let providedEnv = {};
213
- async function resolve() {
214
- const deps = {};
215
- if (serviceDef.inject) {
216
- for (const [name, depDef] of Object.entries(serviceDef.inject)) {
217
- const mock = serviceMocks.get(depDef);
218
- if (mock === undefined) {
219
- throw new Error(`Missing mock for injected dependency "${name}". Call .mock(${name}Service, impl) before awaiting.`);
220
- }
221
- deps[name] = mock;
222
- }
223
- }
224
- let options = {};
225
- if (serviceDef.options) {
226
- const parsed = serviceDef.options.safeParse(providedOptions);
227
- if (parsed.ok) {
228
- options = parsed.data;
229
- } else {
230
- throw new Error(`Invalid options: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
231
- }
232
- }
233
- let env = {};
234
- if (serviceDef.env) {
235
- const parsed = serviceDef.env.safeParse(providedEnv);
236
- if (parsed.ok) {
237
- env = parsed.data;
238
- } else {
239
- throw new Error(`Invalid env: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
240
- }
241
- }
242
- const state = serviceDef.onInit ? await serviceDef.onInit(deps, options, env) : undefined;
243
- return serviceDef.methods(deps, state, options, env);
244
- }
245
- const builder = {
246
- mock(service, impl) {
247
- serviceMocks.set(service, impl);
248
- return builder;
249
- },
250
- options(opts) {
251
- providedOptions = opts;
252
- return builder;
253
- },
254
- env(envVars) {
255
- providedEnv = envVars;
256
- return builder;
257
- },
258
- then(onfulfilled, onrejected) {
259
- return resolve().then(onfulfilled, onrejected);
260
- }
261
- };
262
- return builder;
263
- }
264
160
  export {
265
- createTestService,
266
161
  createTestApp
267
162
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/testing",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Testing utilities for Vertz applications",
@@ -31,14 +31,14 @@
31
31
  "typecheck": "tsc --noEmit"
32
32
  },
33
33
  "dependencies": {
34
- "@vertz/core": "0.2.1",
35
- "@vertz/server": "0.2.1"
34
+ "@vertz/core": "0.2.2",
35
+ "@vertz/server": "0.2.2"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^25.3.1",
39
- "@vertz/schema": "0.2.1",
39
+ "@vertz/schema": "0.2.2",
40
40
  "@vitest/coverage-v8": "^4.0.18",
41
- "bunup": "latest",
41
+ "bunup": "^0.16.31",
42
42
  "typescript": "^5.7.0",
43
43
  "vitest": "^4.0.18"
44
44
  },