const { describe, it, beforeEach, afterEach, before, after } = require('mocha'); const { expect } = require('chai'); const sinon = require('sinon'); const { validateRequest } = require('../../middleware/validation'); const Joi = require('joi'); const { mockRequest, mockResponse, mockNext } = require('../setup'); describe('Validation Middleware', () => { describe('validateRequest', () => { const testSchema = Joi.object({ name: Joi.string().required(), email: Joi.string().email().required(), age: Joi.number().integer().min(0).max(120).optional(), tags: Joi.array().items(Joi.string()).optional() }); it('should pass validation with valid data', () => { const req = mockRequest({ body: { name: 'John Doe', email: 'john@example.com', age: 30, tags: ['admin', 'user'] } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(next.errors).to.have.length(0); expect(req.body).to.deep.equal({ name: 'John Doe', email: 'john@example.com', age: 30, tags: ['admin', 'user'] }); }); it('should fail validation with missing required field', () => { const req = mockRequest({ body: { email: 'john@example.com' // missing name } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(res.statusCode).to.equal(400); expect(res.data.success).to.be.false; expect(res.data.message).to.include('name'); }); it('should fail validation with invalid email', () => { const req = mockRequest({ body: { name: 'John Doe', email: 'invalid-email' } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(res.statusCode).to.equal(400); expect(res.data.success).to.be.false; expect(res.data.message).to.include('email'); }); it('should fail validation with invalid age', () => { const req = mockRequest({ body: { name: 'John Doe', email: 'john@example.com', age: -5 } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(res.statusCode).to.equal(400); expect(res.data.success).to.be.false; expect(res.data.message).to.include('age'); }); it('should strip unknown fields', () => { const req = mockRequest({ body: { name: 'John Doe', email: 'john@example.com', unknownField: 'should be removed' } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(next.errors).to.have.length(0); expect(req.body).to.not.have.property('unknownField'); }); it('should handle array validation', () => { const req = mockRequest({ body: { name: 'John Doe', email: 'john@example.com', tags: ['valid', 'tags'] } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(next.errors).to.have.length(0); expect(req.body.tags).to.deep.equal(['valid', 'tags']); }); it('should fail with invalid array items', () => { const req = mockRequest({ body: { name: 'John Doe', email: 'john@example.com', tags: ['valid', 123, 'invalid-number'] } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(res.statusCode).to.equal(400); expect(res.data.success).to.be.false; }); it('should handle nested object validation', () => { const nestedSchema = Joi.object({ user: Joi.object({ name: Joi.string().required(), profile: Joi.object({ age: Joi.number().required(), preferences: Joi.array().items(Joi.string()) }).required() }).required() }); const req = mockRequest({ body: { user: { name: 'John', profile: { age: 30, preferences: ['dark-mode', 'notifications'] } } } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(nestedSchema); middleware(req, res, next); expect(next.errors).to.have.length(0); }); it('should provide detailed error messages', () => { const req = mockRequest({ body: { name: '', email: 'invalid', age: 150 } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(testSchema); middleware(req, res, next); expect(res.statusCode).to.equal(400); expect(res.data.success).to.be.false; expect(res.data.message).to.be.a('string'); expect(res.data.details).to.be.an('array'); expect(res.data.details.length).to.be.greaterThan(0); }); it('should handle alternative schemas', () => { const altSchema = Joi.alternatives().try( Joi.object({ type: Joi.string().valid('user').required(), username: Joi.string().required() }), Joi.object({ type: Joi.string().valid('device').required(), deviceId: Joi.number().required() }) ); const req1 = mockRequest({ body: { type: 'user', username: 'john' } }); const res1 = mockResponse(); const next1 = mockNext(); const middleware1 = validateRequest(altSchema); middleware1(req1, res1, next1); expect(next1.errors).to.have.length(0); const req2 = mockRequest({ body: { type: 'device', deviceId: 123 } }); const res2 = mockResponse(); const next2 = mockNext(); const middleware2 = validateRequest(altSchema); middleware2(req2, res2, next2); expect(next2.errors).to.have.length(0); }); it('should handle query parameter validation', () => { const querySchema = Joi.object({ page: Joi.number().integer().min(1).default(1), limit: Joi.number().integer().min(1).max(100).default(10), search: Joi.string().optional() }); const req = mockRequest({ query: { page: '2', limit: '20', search: 'test' } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(querySchema, 'query'); middleware(req, res, next); expect(next.errors).to.have.length(0); expect(req.query.page).to.equal(2); // Should be converted to number expect(req.query.limit).to.equal(20); }); it('should handle params validation', () => { const paramsSchema = Joi.object({ id: Joi.number().integer().positive().required(), slug: Joi.string().alphanum().optional() }); const req = mockRequest({ params: { id: '123', slug: 'test-slug' } }); const res = mockResponse(); const next = mockNext(); const middleware = validateRequest(paramsSchema, 'params'); middleware(req, res, next); expect(next.errors).to.have.length(0); expect(req.params.id).to.equal(123); }); }); });