Backend Complete: Interface Refactoring & Service Container Enhancements
Repository Interface Optimization: - Created IBaseRepository.ts and IPaginatedRepository.ts - Refactored all 7 repository interfaces to extend base interfaces - Eliminated ~200 lines of redundant code (70% reduction) - Improved type safety and maintainability Dependency Injection Improvements: - Added EmailService and GameTokenService to DIContainer - Updated CreateUserCommandHandler constructor for DI - Updated RequestPasswordResetCommandHandler constructor for DI - Enhanced testability and service consistency Environment Configuration: - Created comprehensive .env.example with 40+ variables - Organized into 12 logical sections (Database, Security, Email, etc.) - Added security guidelines and best practices - Documented all backend environment requirements Documentation: - Added comprehensive codebase review - Created refactoring summary report - Added frontend implementation guide Impact: Improved code quality, reduced maintenance overhead, enhanced developer experience
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
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<Request>;
|
||||
let res: Partial<Response>;
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user