import { Request, Response, NextFunction } from 'express'; import { rateLimiter, authLimiter, orderLimiter } from '../../../src/middleware/rate-limiter'; // Mock express-rate-limit模块 jest.mock('express-rate-limit', () => { return jest.fn().mockImplementation((options) => { return jest.fn().mockImplementation((req: Request, res: Response, next: NextFunction) => { // 模拟限流逻辑 const count = (req as any).rateLimitCount ? (req as any).rateLimitCount + 1 : 1; (req as any).rateLimitCount = count; if (count > options.max) { // 如果有自定义处理器,使用它 if (options.handler) { return options.handler(req, res, next); } // 否则使用默认的message return res.status(429).json(options.message); } return next(); }); }); }); describe('Rate Limit Middleware 单元测试', () => { let mockRequest: Partial; let mockResponse: Partial; let nextFunction: jest.Mock; beforeEach(() => { // 重置所有模拟 jest.clearAllMocks(); // 创建模拟请求对象 mockRequest = { ip: '127.0.0.1', path: '/api/test', method: 'GET', app: {} as any }; // 创建模拟响应对象 mockResponse = { status: jest.fn().mockReturnThis(), json: jest.fn().mockReturnThis(), }; // 创建模拟next函数 nextFunction = jest.fn(); }); describe('通用限流中间件', () => { it('应该在请求限制内允许通过', () => { // 调用限流中间件 rateLimiter(mockRequest as Request, mockResponse as Response, nextFunction); // 验证next被调用 expect(nextFunction).toHaveBeenCalledTimes(1); expect(mockResponse.status).not.toHaveBeenCalled(); }); it('应该在超过限制时返回429状态码', () => { // 超过限制的请求 for (let i = 0; i < 101; i++) { rateLimiter(mockRequest as Request, mockResponse as Response, nextFunction); } // 验证响应 expect(nextFunction).toHaveBeenCalledTimes(100); expect(mockResponse.status).toHaveBeenCalledWith(429); expect(mockResponse.json).toHaveBeenCalledWith({ code: 429, error: 'RATE_LIMIT_EXCEEDED', message: '请求过于频繁,请稍后再试', timestamp: expect.any(Number), }); }); }); describe('登录限流中间件', () => { it('应该在登录尝试限制内允许通过', () => { // 调用登录限流中间件 authLimiter(mockRequest as Request, mockResponse as Response, nextFunction); // 验证next被调用 expect(nextFunction).toHaveBeenCalledTimes(1); expect(mockResponse.status).not.toHaveBeenCalled(); }); it('应该在超过登录尝试限制时返回429状态码', () => { // 超过限制的登录尝试 for (let i = 0; i < 6; i++) { authLimiter(mockRequest as Request, mockResponse as Response, nextFunction); } // 验证响应 expect(nextFunction).toHaveBeenCalledTimes(5); expect(mockResponse.status).toHaveBeenCalledWith(429); expect(mockResponse.json).toHaveBeenCalledWith({ code: 429, error: 'RATE_LIMIT_EXCEEDED', message: '登录尝试次数过多,请1小时后再试', timestamp: expect.any(Number), }); }); }); describe('创建订单限流中间件', () => { it('应该在订单创建限制内允许通过', () => { // 模拟已登录用户 (mockRequest as any).user = { id: 'user123' }; // 调用订单限流中间件 orderLimiter(mockRequest as Request, mockResponse as Response, nextFunction); // 验证next被调用 expect(nextFunction).toHaveBeenCalledTimes(1); expect(mockResponse.status).not.toHaveBeenCalled(); }); it('应该在超过订单创建限制时返回429状态码', () => { // 模拟已登录用户 (mockRequest as any).user = { id: 'user123' }; // 超过限制的订单创建请求 for (let i = 0; i < 11; i++) { orderLimiter(mockRequest as Request, mockResponse as Response, nextFunction); } // 验证响应 expect(nextFunction).toHaveBeenCalledTimes(10); expect(mockResponse.status).toHaveBeenCalledWith(429); expect(mockResponse.json).toHaveBeenCalledWith({ code: 429, error: 'RATE_LIMIT_EXCEEDED', message: '下单过于频繁,请稍后再试', timestamp: expect.any(Number), }); }); it('应该对未登录用户使用IP作为限流键', () => { // 不设置用户信息,模拟未登录用户 delete (mockRequest as any).user; // 调用订单限流中间件 orderLimiter(mockRequest as Request, mockResponse as Response, nextFunction); // 验证next被调用 expect(nextFunction).toHaveBeenCalledTimes(1); }); }); });