@vertz/testing 0.2.0 → 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,68 +8,31 @@ 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
- try {
16
- return schema.parse(value);
17
- } catch (error) {
18
- if (error instanceof BadRequestException)
19
- throw error;
20
- const message = error instanceof Error ? error.message : `Invalid ${label}`;
13
+ const result = schema.parse(value);
14
+ if (!result.ok) {
15
+ const message = result.error instanceof Error ? result.error.message : `Invalid ${label}`;
21
16
  throw new BadRequestException(message);
22
17
  }
18
+ return result.data;
23
19
  }
24
20
  var HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"];
25
- function createTestApp() {
26
- const serviceMocks = new Map;
21
+ function createTestApp(config) {
27
22
  const middlewareMocks = new Map;
28
- const registrations = [];
23
+ const routes = config?.routes ?? [];
29
24
  let envOverrides = {};
30
25
  function buildHandler(perRequest) {
31
26
  const trie = new Trie;
32
- const realServices = new Map;
33
- for (const { module, options } of registrations) {
34
- for (const service of module.services) {
35
- if (!realServices.has(service)) {
36
- let parsedOptions = {};
37
- if (service.options && options) {
38
- const parsed = service.options.safeParse(options);
39
- if (parsed.success) {
40
- parsedOptions = parsed.data;
41
- } else {
42
- throw new Error(`Invalid options for service ${service.moduleName}: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
43
- }
44
- }
45
- const env = {};
46
- realServices.set(service, service.methods({}, undefined, parsedOptions, env));
47
- }
48
- }
49
- }
50
- const serviceMap = new Map([...realServices, ...serviceMocks, ...perRequest.services]);
51
- for (const { module, options } of registrations) {
52
- for (const router of module.routers) {
53
- const resolvedServices = {};
54
- if (router.inject) {
55
- for (const [name, serviceDef] of Object.entries(router.inject)) {
56
- resolvedServices[name] = serviceMap.get(serviceDef);
57
- }
58
- }
59
- for (const route of router.routes) {
60
- const fullPath = router.prefix + route.path;
61
- const entry = {
62
- handler: route.config.handler,
63
- options: options ?? {},
64
- services: resolvedServices,
65
- responseSchema: route.config.response,
66
- bodySchema: route.config.body,
67
- querySchema: route.config.query,
68
- headersSchema: route.config.headers
69
- };
70
- trie.add(route.method, fullPath, entry);
71
- }
72
- }
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);
73
36
  }
74
37
  const effectiveMiddlewareMocks = new Map([...middlewareMocks, ...perRequest.middlewares]);
75
38
  return async (request) => {
@@ -114,14 +77,14 @@ function createTestApp() {
114
77
  headers: validatedHeaders,
115
78
  raw,
116
79
  middlewareState,
117
- services: entry.services,
118
- options: entry.options,
80
+ services: {},
81
+ options: {},
119
82
  env: envOverrides
120
83
  });
121
84
  const result = await entry.handler(ctx);
122
85
  if (entry.responseSchema) {
123
86
  const validation = entry.responseSchema.safeParse(result);
124
- if (!validation.success) {
87
+ if (!validation.ok) {
125
88
  throw new ResponseValidationError(validation.error?.message ?? "Unknown validation error");
126
89
  }
127
90
  }
@@ -157,14 +120,9 @@ function createTestApp() {
157
120
  }
158
121
  function createRequestBuilder(method, path, options) {
159
122
  const perRequest = {
160
- services: new Map,
161
123
  middlewares: new Map
162
124
  };
163
125
  const builder = {
164
- mock(service, impl) {
165
- perRequest.services.set(service, impl);
166
- return builder;
167
- },
168
126
  mockMiddleware(middleware, result) {
169
127
  perRequest.middlewares.set(middleware, result);
170
128
  return builder;
@@ -180,14 +138,6 @@ function createTestApp() {
180
138
  (path, options) => createRequestBuilder(m, path, options)
181
139
  ]));
182
140
  const app = {
183
- register(module, options) {
184
- registrations.push({ module, options });
185
- return app;
186
- },
187
- mock(service, impl) {
188
- serviceMocks.set(service, impl);
189
- return app;
190
- },
191
141
  mockMiddleware(middleware, result) {
192
142
  middlewareMocks.set(middleware, result);
193
143
  return app;
@@ -207,63 +157,6 @@ class ResponseValidationError extends Error {
207
157
  this.name = "ResponseValidationError";
208
158
  }
209
159
  }
210
- // src/test-service.ts
211
- function createTestService(serviceDef) {
212
- const serviceMocks = new Map;
213
- let providedOptions = {};
214
- let providedEnv = {};
215
- async function resolve() {
216
- const deps = {};
217
- if (serviceDef.inject) {
218
- for (const [name, depDef] of Object.entries(serviceDef.inject)) {
219
- const mock = serviceMocks.get(depDef);
220
- if (mock === undefined) {
221
- throw new Error(`Missing mock for injected dependency "${name}". Call .mock(${name}Service, impl) before awaiting.`);
222
- }
223
- deps[name] = mock;
224
- }
225
- }
226
- let options = {};
227
- if (serviceDef.options) {
228
- const parsed = serviceDef.options.safeParse(providedOptions);
229
- if (parsed.success) {
230
- options = parsed.data;
231
- } else {
232
- throw new Error(`Invalid options: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
233
- }
234
- }
235
- let env = {};
236
- if (serviceDef.env) {
237
- const parsed = serviceDef.env.safeParse(providedEnv);
238
- if (parsed.success) {
239
- env = parsed.data;
240
- } else {
241
- throw new Error(`Invalid env: ${parsed.error.issues.map((i) => i.message).join(", ")}`);
242
- }
243
- }
244
- const state = serviceDef.onInit ? await serviceDef.onInit(deps, options, env) : undefined;
245
- return serviceDef.methods(deps, state, options, env);
246
- }
247
- const builder = {
248
- mock(service, impl) {
249
- serviceMocks.set(service, impl);
250
- return builder;
251
- },
252
- options(opts) {
253
- providedOptions = opts;
254
- return builder;
255
- },
256
- env(envVars) {
257
- providedEnv = envVars;
258
- return builder;
259
- },
260
- then(onfulfilled, onrejected) {
261
- return resolve().then(onfulfilled, onrejected);
262
- }
263
- };
264
- return builder;
265
- }
266
160
  export {
267
- createTestService,
268
161
  createTestApp
269
162
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vertz/testing",
3
- "version": "0.2.0",
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": "workspace:*",
35
- "@vertz/server": "workspace:*"
34
+ "@vertz/core": "0.2.2",
35
+ "@vertz/server": "0.2.2"
36
36
  },
37
37
  "devDependencies": {
38
- "@types/node": "^25.2.1",
39
- "@vertz/schema": "workspace:*",
38
+ "@types/node": "^25.3.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
  },