import { describe, it, expect, vi } from 'vitest' import { z } from 'zod' import { validate } from './validate' import type { Request, Response, NextFunction } from 'express' const schema = z.object({ name: z.string(), age: z.number() }) function makeReq(body: unknown): Request { return { body } as unknown as Request } function makeRes() { const res = { status: vi.fn(), json: vi.fn() } res.status.mockReturnValue(res as unknown as Response) return res } describe('validate', () => { it('calls next() and assigns parsed body on valid input', () => { const req = makeReq({ name: 'Su', age: 30 }) const res = makeRes() const next = vi.fn() as unknown as NextFunction validate(schema)(req, res as unknown as Response, next) expect(next).toHaveBeenCalledOnce() expect(req.body).toEqual({ name: 'Su', age: 30 }) expect(res.status).not.toHaveBeenCalled() }) it('returns 400 with error details on invalid input', () => { const req = makeReq({ name: 123 }) const res = makeRes() const next = vi.fn() as unknown as NextFunction validate(schema)(req, res as unknown as Response, next) expect(res.status).toHaveBeenCalledWith(400) expect(res.json).toHaveBeenCalledWith( expect.objectContaining({ error: expect.any(String), details: expect.any(Array) }) ) expect(next).not.toHaveBeenCalled() }) it('coerces Zod defaults into req.body', () => { const schemaWithDefault = z.object({ model: z.string().default('smart-router') }) const req = makeReq({}) const res = makeRes() const next = vi.fn() as unknown as NextFunction validate(schemaWithDefault)(req, res as unknown as Response, next) expect(next).toHaveBeenCalledOnce() expect((req.body as { model: string }).model).toBe('smart-router') }) })