import { ValidationMiddleware } from '../../../src/Application/Services/ValidationMiddleware'; import { Request, Response, NextFunction } from 'express'; import { ErrorResponseService } from '../../../src/Application/Services/ErrorResponseService'; jest.mock('../../../src/Application/Services/ErrorResponseService'); jest.mock('../../../src/Application/Services/Logger'); describe('ValidationMiddleware', () => { let req: Partial; let res: Partial; let next: NextFunction; beforeEach(() => { req = { body: {}, params: {}, query: {}, path: '/test' }; res = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis() }; next = jest.fn(); jest.clearAllMocks(); }); describe('validateRequiredFields', () => { it('should pass validation when all required fields are present', () => { req.body = { username: 'testuser', email: 'test@example.com' }; const middleware = ValidationMiddleware.validateRequiredFields(['username', 'email']); middleware(req as Request, res as Response, next); expect(next).toHaveBeenCalledWith(); expect(ErrorResponseService.sendBadRequest).not.toHaveBeenCalled(); }); it('should fail validation when required fields are missing', () => { req.body = { username: 'testuser' }; // missing email const middleware = ValidationMiddleware.validateRequiredFields(['username', 'email']); middleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'Missing required fields', { missingFields: ['email'] } ); expect(next).not.toHaveBeenCalled(); }); it('should fail validation when fields are empty strings', () => { req.body = { username: '', email: 'test@example.com' }; const middleware = ValidationMiddleware.validateRequiredFields(['username', 'email']); middleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'Missing required fields', { missingFields: ['username'] } ); }); }); describe('validateEmailFormat', () => { it('should pass validation for valid email', () => { req.body = { email: 'test@example.com' }; const middleware = ValidationMiddleware.validateEmailFormat(['email']); middleware(req as Request, res as Response, next); expect(next).toHaveBeenCalledWith(); expect(ErrorResponseService.sendBadRequest).not.toHaveBeenCalled(); }); it('should fail validation for invalid email', () => { req.body = { email: 'invalid-email' }; const middleware = ValidationMiddleware.validateEmailFormat(['email']); middleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'Email format validation failed', { errors: ["Field 'email' must contain a valid email address"] } ); expect(next).not.toHaveBeenCalled(); }); }); describe('validateUUIDFormat', () => { it('should pass validation for valid UUID', () => { req.params = { userId: '123e4567-e89b-12d3-a456-426614174000' }; const middleware = ValidationMiddleware.validateUUIDFormat(['userId']); middleware(req as Request, res as Response, next); expect(next).toHaveBeenCalledWith(); expect(ErrorResponseService.sendBadRequest).not.toHaveBeenCalled(); }); it('should fail validation for invalid UUID', () => { req.params = { userId: 'invalid-uuid' }; const middleware = ValidationMiddleware.validateUUIDFormat(['userId']); middleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'UUID format validation failed', { errors: ["Field 'userId' must contain a valid UUID"] } ); expect(next).not.toHaveBeenCalled(); }); }); describe('validateStringLength', () => { it('should pass validation for strings within length constraints', () => { req.body = { username: 'testuser', password: 'password123' }; const middleware = ValidationMiddleware.validateStringLength({ username: { min: 3, max: 20 }, password: { min: 8, max: 50 } }); middleware(req as Request, res as Response, next); expect(next).toHaveBeenCalledWith(); expect(ErrorResponseService.sendBadRequest).not.toHaveBeenCalled(); }); it('should fail validation for strings that are too short', () => { req.body = { username: 'ab' }; // too short (min 3) const middleware = ValidationMiddleware.validateStringLength({ username: { min: 3, max: 20 } }); middleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'String length validation failed', { errors: ["Field 'username' must be at least 3 characters"] } ); }); it('should fail validation for strings that are too long', () => { req.body = { username: 'a'.repeat(25) }; // too long (max 20) const middleware = ValidationMiddleware.validateStringLength({ username: { min: 3, max: 20 } }); middleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'String length validation failed', { errors: ["Field 'username' must not exceed 20 characters"] } ); }); }); describe('combine', () => { it('should run all validations in sequence and pass if all succeed', (done) => { req.body = { username: 'testuser', email: 'test@example.com' }; const nextSpy = jest.fn(() => { try { expect(nextSpy).toHaveBeenCalledWith(); expect(ErrorResponseService.sendBadRequest).not.toHaveBeenCalled(); done(); } catch (error) { done(error); } }); const combinedMiddleware = ValidationMiddleware.combine([ ValidationMiddleware.validateRequiredFields(['username', 'email']), ValidationMiddleware.validateEmailFormat(['email']), ValidationMiddleware.validateStringLength({ username: { min: 3, max: 20 } }) ]); combinedMiddleware(req as Request, res as Response, nextSpy); }); it('should stop at first validation failure', () => { req.body = { username: 'testuser' }; // missing email const combinedMiddleware = ValidationMiddleware.combine([ ValidationMiddleware.validateRequiredFields(['username', 'email']), ValidationMiddleware.validateEmailFormat(['email']), // this won't run ValidationMiddleware.validateStringLength({ username: { min: 3, max: 20 } }) // this won't run ]); combinedMiddleware(req as Request, res as Response, next); expect(ErrorResponseService.sendBadRequest).toHaveBeenCalledWith( res, 'Missing required fields', { missingFields: ['email'] } ); expect(next).not.toHaveBeenCalled(); }); }); });