| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153 |
- 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<Request>;
- let mockResponse: Partial<Response>;
- 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);
- });
- });
- });
|