@vertz/testing 0.2.32 → 0.2.33

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 (2) hide show
  1. package/README.md +108 -111
  2. package/package.json +14 -14
package/README.md CHANGED
@@ -29,24 +29,22 @@ import { describe, expect, it } from 'vitest';
29
29
  describe('User routes', () => {
30
30
  it('returns list of users', async () => {
31
31
  const app = createTestApp().register(UserModule);
32
-
32
+
33
33
  const res = await app.get('/users');
34
-
34
+
35
35
  expect(res.ok).toBe(true);
36
36
  expect(res.body).toEqual({
37
- users: expect.arrayContaining([
38
- expect.objectContaining({ name: expect.any(String) }),
39
- ]),
37
+ users: expect.arrayContaining([expect.objectContaining({ name: expect.any(String) })]),
40
38
  });
41
39
  });
42
-
40
+
43
41
  it('creates a new user', async () => {
44
42
  const app = createTestApp().register(UserModule);
45
-
43
+
46
44
  const res = await app.post('/users', {
47
45
  body: { name: 'Jane Doe', email: 'jane@example.com' },
48
46
  });
49
-
47
+
50
48
  expect(res.status).toBe(201);
51
49
  expect(res.body).toMatchObject({
52
50
  id: expect.any(String),
@@ -65,13 +63,12 @@ import { describe, expect, it } from 'vitest';
65
63
 
66
64
  describe('UserService', () => {
67
65
  it('finds user by ID', async () => {
68
- const userService = await createTestService(UserService)
69
- .mock(DatabaseService, {
70
- query: () => Promise.resolve([{ id: '1', name: 'Jane' }]),
71
- });
72
-
66
+ const userService = await createTestService(UserService).mock(DatabaseService, {
67
+ query: () => Promise.resolve([{ id: '1', name: 'Jane' }]),
68
+ });
69
+
73
70
  const user = await userService.findById('1');
74
-
71
+
75
72
  expect(user).toEqual({ id: '1', name: 'Jane' });
76
73
  });
77
74
  });
@@ -101,6 +98,7 @@ app.register(UserModule, { apiKey: 'test-key' });
101
98
  ```
102
99
 
103
100
  **Parameters:**
101
+
104
102
  - `module` — A Vertz module created with `createModule()`
105
103
  - `options` — Optional configuration passed to the module
106
104
 
@@ -117,6 +115,7 @@ app.mock(DatabaseService, {
117
115
  ```
118
116
 
119
117
  **Parameters:**
118
+
120
119
  - `service` — A service definition created with `moduleDef.service()`
121
120
  - `implementation` — Partial implementation of the service methods
122
121
 
@@ -133,6 +132,7 @@ app.mockMiddleware(AuthMiddleware, { user: { id: '1' } });
133
132
  ```
134
133
 
135
134
  **Parameters:**
135
+
136
136
  - `middleware` — A middleware definition created with `createMiddleware()`
137
137
  - `result` — The value the middleware should provide
138
138
 
@@ -147,6 +147,7 @@ app.env({ DATABASE_URL: 'test-db', API_KEY: 'test-key' });
147
147
  ```
148
148
 
149
149
  **Parameters:**
150
+
150
151
  - `vars` — Object with environment variable key-value pairs
151
152
 
152
153
  **Returns:** `TestApp` (chainable)
@@ -158,13 +159,14 @@ Make HTTP requests to your application.
158
159
  ```ts
159
160
  const res = await app.get('/users');
160
161
  const res = await app.post('/users', { body: { name: 'Jane' } });
161
- const res = await app.put('/users/1', {
162
+ const res = await app.put('/users/1', {
162
163
  body: { name: 'Jane Doe' },
163
- headers: { 'Authorization': 'Bearer token' },
164
+ headers: { Authorization: 'Bearer token' },
164
165
  });
165
166
  ```
166
167
 
167
168
  **Parameters:**
169
+
168
170
  - `path` — Request path (e.g., `/users`, `/users/123`)
169
171
  - `options` — Optional request options:
170
172
  - `body` — Request body (automatically serialized as JSON)
@@ -186,10 +188,10 @@ Returns a `TestResponse`:
186
188
 
187
189
  ```ts
188
190
  interface TestResponse {
189
- status: number; // HTTP status code
190
- body: unknown; // Parsed response body (JSON)
191
+ status: number; // HTTP status code
192
+ body: unknown; // Parsed response body (JSON)
191
193
  headers: Record<string, string>; // Response headers
192
- ok: boolean; // true if status 2xx
194
+ ok: boolean; // true if status 2xx
193
195
  }
194
196
  ```
195
197
 
@@ -198,7 +200,8 @@ interface TestResponse {
198
200
  Override mocks for a single request:
199
201
 
200
202
  ```ts
201
- const res = await app.get('/users')
203
+ const res = await app
204
+ .get('/users')
202
205
  .mock(DatabaseService, {
203
206
  query: () => Promise.resolve([{ id: '1', name: 'Mock User' }]),
204
207
  })
@@ -206,6 +209,7 @@ const res = await app.get('/users')
206
209
  ```
207
210
 
208
211
  **Methods:**
212
+
209
213
  - `mock(service, implementation)` — Mock a service for this request only
210
214
  - `mockMiddleware(middleware, result)` — Mock a middleware for this request only
211
215
 
@@ -222,6 +226,7 @@ const serviceInstance = await createTestService(UserService);
222
226
  ```
223
227
 
224
228
  **Parameters:**
229
+
225
230
  - `service` — A service definition created with `moduleDef.service()`
226
231
 
227
232
  **Returns:** `TestServiceBuilder` (awaitable, see below)
@@ -249,6 +254,7 @@ const service = await createTestService(UserService)
249
254
  ```
250
255
 
251
256
  **Method:**
257
+
252
258
  - `mock(dependency, implementation)` — Mock an injected dependency
253
259
 
254
260
  **Returns:** `TestServiceBuilder` (chainable and awaitable)
@@ -260,8 +266,7 @@ const service = await createTestService(UserService)
260
266
  const service = await createTestService(UserService);
261
267
 
262
268
  // ✅ Correct
263
- const service = await createTestService(UserService)
264
- .mock(DatabaseService, { query: () => [] });
269
+ const service = await createTestService(UserService).mock(DatabaseService, { query: () => [] });
265
270
  ```
266
271
 
267
272
  ### `DeepPartial<T>`
@@ -291,12 +296,12 @@ Mock the auth middleware to simulate authenticated requests:
291
296
  it('returns user profile when authenticated', async () => {
292
297
  const app = createTestApp()
293
298
  .register(UserModule)
294
- .mockMiddleware(AuthMiddleware, {
295
- user: { id: 'user-123', role: 'admin' }
299
+ .mockMiddleware(AuthMiddleware, {
300
+ user: { id: 'user-123', role: 'admin' },
296
301
  });
297
-
302
+
298
303
  const res = await app.get('/users/me');
299
-
304
+
300
305
  expect(res.ok).toBe(true);
301
306
  expect(res.body).toMatchObject({ id: 'user-123' });
302
307
  });
@@ -307,13 +312,14 @@ Per-request authentication:
307
312
  ```ts
308
313
  it('requires authentication', async () => {
309
314
  const app = createTestApp().register(UserModule);
310
-
315
+
311
316
  // No auth
312
317
  const unauthorized = await app.get('/users/me');
313
318
  expect(unauthorized.status).toBe(401);
314
-
319
+
315
320
  // With auth
316
- const authorized = await app.get('/users/me')
321
+ const authorized = await app
322
+ .get('/users/me')
317
323
  .mockMiddleware(AuthMiddleware, { user: { id: '1' } });
318
324
  expect(authorized.ok).toBe(true);
319
325
  });
@@ -329,16 +335,12 @@ it('creates a user in the database', async () => {
329
335
  insert: vi.fn().mockResolvedValue({ id: 'new-id' }),
330
336
  query: vi.fn(),
331
337
  };
332
-
333
- const app = createTestApp()
334
- .register(UserModule)
335
- .mock(DatabaseService, mockDb);
336
-
338
+
339
+ const app = createTestApp().register(UserModule).mock(DatabaseService, mockDb);
340
+
337
341
  await app.post('/users', { body: { name: 'Jane' } });
338
-
339
- expect(mockDb.insert).toHaveBeenCalledWith(
340
- expect.objectContaining({ name: 'Jane' })
341
- );
342
+
343
+ expect(mockDb.insert).toHaveBeenCalledWith(expect.objectContaining({ name: 'Jane' }));
342
344
  });
343
345
  ```
344
346
 
@@ -351,9 +353,9 @@ it('returns 404 for missing user', async () => {
351
353
  .mock(DatabaseService, {
352
354
  findById: () => null,
353
355
  });
354
-
356
+
355
357
  const res = await app.get('/users/999');
356
-
358
+
357
359
  expect(res.status).toBe(404);
358
360
  expect(res.body).toMatchObject({
359
361
  error: 'NotFound',
@@ -363,11 +365,11 @@ it('returns 404 for missing user', async () => {
363
365
 
364
366
  it('returns 400 for invalid input', async () => {
365
367
  const app = createTestApp().register(UserModule);
366
-
368
+
367
369
  const res = await app.post('/users', {
368
370
  body: { email: 'not-an-email' }, // Invalid email
369
371
  });
370
-
372
+
371
373
  expect(res.status).toBe(400);
372
374
  });
373
375
  ```
@@ -376,18 +378,17 @@ it('returns 400 for invalid input', async () => {
376
378
 
377
379
  ```ts
378
380
  it('user service finds user by email', async () => {
379
- const userService = await createTestService(UserService)
380
- .mock(DatabaseService, {
381
- query: (sql: string) => {
382
- if (sql.includes('email')) {
383
- return Promise.resolve([{ id: '1', email: 'jane@example.com' }]);
384
- }
385
- return Promise.resolve([]);
386
- },
387
- });
388
-
381
+ const userService = await createTestService(UserService).mock(DatabaseService, {
382
+ query: (sql: string) => {
383
+ if (sql.includes('email')) {
384
+ return Promise.resolve([{ id: '1', email: 'jane@example.com' }]);
385
+ }
386
+ return Promise.resolve([]);
387
+ },
388
+ });
389
+
389
390
  const user = await userService.findByEmail('jane@example.com');
390
-
391
+
391
392
  expect(user).toMatchObject({ id: '1', email: 'jane@example.com' });
392
393
  });
393
394
  ```
@@ -397,14 +398,12 @@ it('user service finds user by email', async () => {
397
398
  ```ts
398
399
  it('filters users by query params', async () => {
399
400
  const app = createTestApp().register(UserModule);
400
-
401
+
401
402
  const res = await app.get('/users?role=admin&active=true');
402
-
403
+
403
404
  expect(res.ok).toBe(true);
404
405
  expect(res.body.users).toEqual(
405
- expect.arrayContaining([
406
- expect.objectContaining({ role: 'admin', active: true }),
407
- ])
406
+ expect.arrayContaining([expect.objectContaining({ role: 'admin', active: true })]),
408
407
  );
409
408
  });
410
409
  ```
@@ -414,9 +413,9 @@ it('filters users by query params', async () => {
414
413
  ```ts
415
414
  it('sets cache headers', async () => {
416
415
  const app = createTestApp().register(UserModule);
417
-
416
+
418
417
  const res = await app.get('/users');
419
-
418
+
420
419
  expect(res.headers['cache-control']).toBe('public, max-age=3600');
421
420
  });
422
421
  ```
@@ -426,14 +425,14 @@ it('sets cache headers', async () => {
426
425
  ```ts
427
426
  it('accepts custom headers', async () => {
428
427
  const app = createTestApp().register(ApiModule);
429
-
428
+
430
429
  const res = await app.get('/data', {
431
430
  headers: {
432
431
  'X-API-Key': 'test-key',
433
432
  'Accept-Language': 'en-US',
434
433
  },
435
434
  });
436
-
435
+
437
436
  expect(res.ok).toBe(true);
438
437
  });
439
438
  ```
@@ -445,11 +444,11 @@ Vertz automatically validates request/response schemas. Test that validation wor
445
444
  ```ts
446
445
  it('validates request body against schema', async () => {
447
446
  const app = createTestApp().register(UserModule);
448
-
447
+
449
448
  const res = await app.post('/users', {
450
449
  body: { name: '', email: 'invalid' }, // Invalid data
451
450
  });
452
-
451
+
453
452
  expect(res.status).toBe(400);
454
453
  expect(res.body).toMatchObject({
455
454
  error: 'BadRequest',
@@ -462,9 +461,9 @@ it('validates request body against schema', async () => {
462
461
  ```ts
463
462
  it('handles route parameters', async () => {
464
463
  const app = createTestApp().register(UserModule);
465
-
464
+
466
465
  const res = await app.get('/users/user-123');
467
-
466
+
468
467
  expect(res.ok).toBe(true);
469
468
  expect(res.body.id).toBe('user-123');
470
469
  });
@@ -474,22 +473,19 @@ it('handles route parameters', async () => {
474
473
 
475
474
  ```ts
476
475
  it('integrates multiple modules', async () => {
477
- const app = createTestApp()
478
- .register(UserModule)
479
- .register(AuthModule)
480
- .register(PaymentModule);
481
-
476
+ const app = createTestApp().register(UserModule).register(AuthModule).register(PaymentModule);
477
+
482
478
  // Test cross-module behavior
483
479
  const loginRes = await app.post('/auth/login', {
484
480
  body: { email: 'jane@example.com', password: 'secret' },
485
481
  });
486
-
482
+
487
483
  const token = loginRes.body.token;
488
-
484
+
489
485
  const profileRes = await app.get('/users/me', {
490
486
  headers: { Authorization: `Bearer ${token}` },
491
487
  });
492
-
488
+
493
489
  expect(profileRes.ok).toBe(true);
494
490
  });
495
491
  ```
@@ -500,14 +496,13 @@ If a service maintains state via `onInit`:
500
496
 
501
497
  ```ts
502
498
  it('initializes service with state', async () => {
503
- const service = await createTestService(CounterService)
504
- .mock(StorageService, {
505
- load: () => Promise.resolve({ count: 5 }),
506
- });
507
-
499
+ const service = await createTestService(CounterService).mock(StorageService, {
500
+ load: () => Promise.resolve({ count: 5 }),
501
+ });
502
+
508
503
  const count = await service.getCount();
509
504
  expect(count).toBe(5);
510
-
505
+
511
506
  await service.increment();
512
507
  const newCount = await service.getCount();
513
508
  expect(newCount).toBe(6);
@@ -524,7 +519,7 @@ it('uses module options', async () => {
524
519
  maxUsers: 100,
525
520
  enableCache: false,
526
521
  });
527
-
522
+
528
523
  const res = await app.get('/users');
529
524
  expect(res.ok).toBe(true);
530
525
  });
@@ -553,11 +548,11 @@ import { describe, expect, it, beforeEach, vi } from 'vitest';
553
548
 
554
549
  describe('User routes', () => {
555
550
  let app: ReturnType<typeof createTestApp>;
556
-
551
+
557
552
  beforeEach(() => {
558
553
  app = createTestApp().register(UserModule);
559
554
  });
560
-
555
+
561
556
  it('returns users', async () => {
562
557
  const res = await app.get('/users');
563
558
  expect(res.ok).toBe(true);
@@ -572,17 +567,12 @@ Combine with `vi.fn()` for advanced mocking:
572
567
  ```ts
573
568
  it('calls database with correct params', async () => {
574
569
  const queryFn = vi.fn().mockResolvedValue([]);
575
-
576
- const app = createTestApp()
577
- .register(UserModule)
578
- .mock(DatabaseService, { query: queryFn });
579
-
570
+
571
+ const app = createTestApp().register(UserModule).mock(DatabaseService, { query: queryFn });
572
+
580
573
  await app.get('/users?role=admin');
581
-
582
- expect(queryFn).toHaveBeenCalledWith(
583
- expect.stringContaining('role = $1'),
584
- ['admin']
585
- );
574
+
575
+ expect(queryFn).toHaveBeenCalledWith(expect.stringContaining('role = $1'), ['admin']);
586
576
  });
587
577
  ```
588
578
 
@@ -627,14 +617,16 @@ When you need different behavior per test case, use per-request mocks:
627
617
  ```ts
628
618
  it('handles different user states', async () => {
629
619
  const app = createTestApp().register(UserModule);
630
-
620
+
631
621
  // Active user
632
- const activeRes = await app.get('/users/1')
622
+ const activeRes = await app
623
+ .get('/users/1')
633
624
  .mock(DatabaseService, { findById: () => ({ id: '1', active: true }) });
634
625
  expect(activeRes.body.active).toBe(true);
635
-
626
+
636
627
  // Inactive user
637
- const inactiveRes = await app.get('/users/1')
628
+ const inactiveRes = await app
629
+ .get('/users/1')
638
630
  .mock(DatabaseService, { findById: () => ({ id: '1', active: false }) });
639
631
  expect(inactiveRes.body.active).toBe(false);
640
632
  });
@@ -657,15 +649,19 @@ app.mock(DatabaseService, {
657
649
  ```ts
658
650
  describe('User API', () => {
659
651
  let app: ReturnType<typeof createTestApp>;
660
-
652
+
661
653
  beforeEach(() => {
662
- app = createTestApp()
663
- .register(UserModule)
664
- .mock(DatabaseService, { /* common mocks */ });
654
+ app = createTestApp().register(UserModule).mock(DatabaseService, {
655
+ /* common mocks */
656
+ });
657
+ });
658
+
659
+ it('test 1', async () => {
660
+ /* ... */
661
+ });
662
+ it('test 2', async () => {
663
+ /* ... */
665
664
  });
666
-
667
- it('test 1', async () => { /* ... */ });
668
- it('test 2', async () => { /* ... */ });
669
665
  });
670
666
  ```
671
667
 
@@ -678,11 +674,13 @@ it('handles database errors', async () => {
678
674
  const app = createTestApp()
679
675
  .register(UserModule)
680
676
  .mock(DatabaseService, {
681
- query: () => { throw new Error('Connection failed'); },
677
+ query: () => {
678
+ throw new Error('Connection failed');
679
+ },
682
680
  });
683
-
681
+
684
682
  const res = await app.get('/users');
685
-
683
+
686
684
  expect(res.status).toBe(500);
687
685
  });
688
686
  ```
@@ -694,9 +692,9 @@ If you define response schemas, Vertz validates them automatically:
694
692
  ```ts
695
693
  it('response matches schema', async () => {
696
694
  const app = createTestApp().register(UserModule);
697
-
695
+
698
696
  const res = await app.get('/users/1');
699
-
697
+
700
698
  // If the handler returns data that doesn't match the schema,
701
699
  // the test will throw a ResponseValidationError
702
700
  expect(res.ok).toBe(true);
@@ -715,8 +713,7 @@ Call .mock(dbService, impl) before awaiting.
715
713
  **Solution:** Mock all injected dependencies:
716
714
 
717
715
  ```ts
718
- const service = await createTestService(UserService)
719
- .mock(DatabaseService, { query: () => [] });
716
+ const service = await createTestService(UserService).mock(DatabaseService, { query: () => [] });
720
717
  ```
721
718
 
722
719
  ### Response Validation Error
package/package.json CHANGED
@@ -1,18 +1,17 @@
1
1
  {
2
2
  "name": "@vertz/testing",
3
- "version": "0.2.32",
4
- "type": "module",
5
- "license": "MIT",
3
+ "version": "0.2.33",
6
4
  "description": "Testing utilities for Vertz applications",
5
+ "license": "MIT",
7
6
  "repository": {
8
7
  "type": "git",
9
8
  "url": "https://github.com/vertz-dev/vertz.git",
10
9
  "directory": "packages/testing"
11
10
  },
12
- "publishConfig": {
13
- "access": "public",
14
- "provenance": true
15
- },
11
+ "files": [
12
+ "dist"
13
+ ],
14
+ "type": "module",
16
15
  "main": "dist/index.js",
17
16
  "types": "dist/index.d.ts",
18
17
  "exports": {
@@ -21,22 +20,23 @@
21
20
  "types": "./dist/index.d.ts"
22
21
  }
23
22
  },
24
- "files": [
25
- "dist"
26
- ],
23
+ "publishConfig": {
24
+ "access": "public",
25
+ "provenance": true
26
+ },
27
27
  "scripts": {
28
28
  "build": "bunup",
29
29
  "test": "bun test",
30
30
  "typecheck": "tsc --noEmit"
31
31
  },
32
32
  "dependencies": {
33
- "@vertz/core": "^0.2.30",
34
- "@vertz/db": "^0.2.30",
35
- "@vertz/server": "^0.2.30"
33
+ "@vertz/core": "^0.2.31",
34
+ "@vertz/db": "^0.2.31",
35
+ "@vertz/server": "^0.2.31"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/node": "^25.3.1",
39
- "@vertz/schema": "^0.2.30",
39
+ "@vertz/schema": "^0.2.31",
40
40
  "bunup": "^0.16.31",
41
41
  "typescript": "^5.7.0"
42
42
  },