1+ import { describe , it , expect } from 'vitest' ;
2+ import express , { type Request , type Response } from 'express' ;
3+ import request from 'supertest' ;
4+ import { z } from 'zod' ;
5+ import { validate } from '../middleware/validate.js' ;
6+
7+ const TestSchema = z . object ( {
8+ name : z . string ( ) . min ( 1 , 'name is required' ) ,
9+ age : z . number ( ) . int ( 'age must be an integer' ) ,
10+ } ) ;
11+
12+ function makeApp ( ) {
13+ const app = express ( ) ;
14+ app . use ( express . json ( ) ) ;
15+ app . post ( '/test' , validate ( TestSchema ) , ( req : Request , res : Response ) => {
16+ res . json ( { received : req . body } ) ;
17+ } ) ;
18+ return app ;
19+ }
20+
21+ describe ( 'validate middleware' , ( ) => {
22+ const app = makeApp ( ) ;
23+
24+ it ( 'calls next and passes body through on valid input' , async ( ) => {
25+ const res = await request ( app ) . post ( '/test' ) . send ( { name : 'Alice' , age : 30 } ) ;
26+ expect ( res . status ) . toBe ( 200 ) ;
27+ expect ( res . body ) . toEqual ( { received : { name : 'Alice' , age : 30 } } ) ;
28+ } ) ;
29+
30+ it ( 'returns 400 with structured error on missing required field' , async ( ) => {
31+ const res = await request ( app ) . post ( '/test' ) . send ( { age : 25 } ) ;
32+ expect ( res . status ) . toBe ( 400 ) ;
33+ expect ( res . body . error ) . toBe ( 'Validation failed' ) ;
34+ expect ( Array . isArray ( res . body . issues ) ) . toBe ( true ) ;
35+ const fields = res . body . issues . map ( ( i : { field : string } ) => i . field ) ;
36+ expect ( fields ) . toContain ( 'name' ) ;
37+ } ) ;
38+
39+ it ( 'returns 400 with structured error on wrong type' , async ( ) => {
40+ const res = await request ( app ) . post ( '/test' ) . send ( { name : 'Bob' , age : 'not-a-number' } ) ;
41+ expect ( res . status ) . toBe ( 400 ) ;
42+ expect ( res . body . error ) . toBe ( 'Validation failed' ) ;
43+ expect ( res . body . issues [ 0 ] ) . toHaveProperty ( 'field' ) ;
44+ expect ( res . body . issues [ 0 ] ) . toHaveProperty ( 'message' ) ;
45+ } ) ;
46+
47+ it ( 'returns 400 with error for empty body' , async ( ) => {
48+ const res = await request ( app ) . post ( '/test' ) . send ( { } ) ;
49+ expect ( res . status ) . toBe ( 400 ) ;
50+ expect ( res . body . error ) . toBe ( 'Validation failed' ) ;
51+ expect ( res . body . issues . length ) . toBeGreaterThan ( 0 ) ;
52+ } ) ;
53+
54+ it ( 'issues array entries have field and message keys' , async ( ) => {
55+ const res = await request ( app ) . post ( '/test' ) . send ( { age : 10 } ) ;
56+ expect ( res . status ) . toBe ( 400 ) ;
57+ for ( const issue of res . body . issues as { field : string ; message : string } [ ] ) {
58+ expect ( issue ) . toHaveProperty ( 'field' ) ;
59+ expect ( issue ) . toHaveProperty ( 'message' ) ;
60+ expect ( typeof issue . field ) . toBe ( 'string' ) ;
61+ expect ( typeof issue . message ) . toBe ( 'string' ) ;
62+ }
63+ } ) ;
64+ } ) ;
65+
66+ describe ( 'auth route validation via validate middleware' , ( ) => {
67+ it ( 'validate middleware integrates as Express RequestHandler' , ( ) => {
68+ const handler = validate ( TestSchema ) ;
69+ expect ( typeof handler ) . toBe ( 'function' ) ;
70+ // Ensure it accepts (req, res, next) signature
71+ expect ( handler . length ) . toBe ( 3 ) ;
72+ } ) ;
73+ } ) ;
0 commit comments