| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242 |
- import { Request, Response, NextFunction } from 'express';
- // 创建自定义JWT错误类
- class MockTokenExpiredError extends Error {
- name = 'TokenExpiredError';
- expiredAt: Date;
- constructor(message: string, expiredAt: Date) {
- super(message);
- this.expiredAt = expiredAt;
- }
- }
- class MockJsonWebTokenError extends Error {
- name = 'JsonWebTokenError';
-
- constructor(message: string) {
- super(message);
- }
- }
- // 创建自定义ApiError类
- class MockApiError extends Error {
- constructor(
- public statusCode: number,
- public message: string,
- public code?: string,
- public details?: any
- ) {
- super(message);
- this.name = 'ApiError';
- }
- }
- // Mock JWT
- jest.mock('jsonwebtoken', () => ({
- verify: jest.fn(),
- TokenExpiredError: MockTokenExpiredError,
- JsonWebTokenError: MockJsonWebTokenError,
- }));
- // Mock Parse
- jest.mock('parse/node', () => ({
- Parse: {
- Query: jest.fn().mockImplementation(() => ({
- equalTo: jest.fn().mockReturnThis(),
- first: jest.fn(),
- })),
- User: {
- current: jest.fn(),
- },
- },
- }));
- // Mock logger
- jest.mock('../../../src/utils/logger', () => ({
- logger: {
- info: jest.fn(),
- error: jest.fn(),
- warn: jest.fn(),
- },
- }));
- // Mock cache
- jest.mock('../../../src/config/redis', () => ({
- cache: {
- get: jest.fn(),
- set: jest.fn(),
- del: jest.fn(),
- },
- }));
- // Mock error handler
- jest.mock('../../../src/middleware/error-handler', () => ({
- errors: {
- unauthorized: (message: string) => new MockApiError(401, message, 'UNAUTHORIZED'),
- forbidden: (message: string) => new MockApiError(403, message, 'FORBIDDEN'),
- },
- }));
- import { authenticate } from '../../../src/middleware/auth';
- describe('Auth Middleware 单元测试', () => {
- let mockRequest: Partial<Request>;
- let mockResponse: Partial<Response>;
- let nextFunction: NextFunction;
- const mockParse = require('parse/node');
- const mockJwt = require('jsonwebtoken');
- beforeEach(() => {
- mockRequest = {
- headers: {},
- };
- mockResponse = {
- status: jest.fn().mockReturnThis(),
- json: jest.fn(),
- };
- nextFunction = jest.fn();
- jest.clearAllMocks();
- });
- describe('认证中间件', () => {
- it('应该允许有效的JWT token通过', async () => {
- const mockUser = {
- id: 'user123',
- username: 'testuser',
- email: 'test@example.com',
- objectId: 'user123',
- };
- const mockToken = 'valid.jwt.token';
- const mockDecodedToken = {
- userId: 'user123',
- username: 'testuser',
- iat: Date.now() / 1000,
- exp: Date.now() / 1000 + 3600,
- };
- mockRequest.headers = {
- authorization: `Bearer ${mockToken}`,
- };
- mockJwt.verify.mockReturnValue(mockDecodedToken);
- mockParse.Parse.Query().first.mockResolvedValue(mockUser);
- await authenticate(
- mockRequest as Request,
- mockResponse as Response,
- nextFunction
- );
- expect(mockJwt.verify).toHaveBeenCalledWith(
- mockToken,
- process.env.JWT_SECRET
- );
- expect(mockRequest.user).toEqual(mockDecodedToken);
- expect(nextFunction).toHaveBeenCalledWith();
- });
- it('应该拒绝没有Authorization头的请求', async () => {
- await authenticate(
- mockRequest as Request,
- mockResponse as Response,
- nextFunction
- );
- expect(nextFunction).toHaveBeenCalledWith(
- expect.objectContaining({
- statusCode: 401,
- message: '未提供访问令牌'
- })
- );
- });
- it('应该拒绝无效的JWT token', async () => {
- const mockToken = 'invalid.jwt.token';
- mockRequest.headers = {
- authorization: `Bearer ${mockToken}`,
- };
- mockJwt.verify.mockImplementation(() => {
- throw new MockJsonWebTokenError('Invalid token');
- });
- await authenticate(
- mockRequest as Request,
- mockResponse as Response,
- nextFunction
- );
- expect(mockJwt.verify).toHaveBeenCalledWith(
- mockToken,
- process.env.JWT_SECRET
- );
- expect(nextFunction).toHaveBeenCalledWith(
- expect.objectContaining({
- statusCode: 401,
- message: '无效的访问令牌'
- })
- );
- });
- it('应该拒绝过期的JWT token', async () => {
- const mockToken = 'expired.jwt.token';
- mockRequest.headers = {
- authorization: `Bearer ${mockToken}`,
- };
- mockJwt.verify.mockImplementation(() => {
- throw new MockTokenExpiredError('Token expired', new Date());
- });
- await authenticate(
- mockRequest as Request,
- mockResponse as Response,
- nextFunction
- );
- expect(mockJwt.verify).toHaveBeenCalledWith(
- mockToken,
- process.env.JWT_SECRET
- );
- expect(nextFunction).toHaveBeenCalledWith(
- expect.objectContaining({
- statusCode: 401,
- message: '访问令牌已过期'
- })
- );
- });
- it('应该拒绝数据库中不存在的用户', async () => {
- const mockToken = 'valid.jwt.token';
- const mockDecodedToken = {
- userId: 'nonexistent_user',
- username: 'testuser',
- iat: Date.now() / 1000,
- exp: Date.now() / 1000 + 3600,
- };
- mockRequest.headers = {
- authorization: `Bearer ${mockToken}`,
- };
- mockJwt.verify.mockReturnValue(mockDecodedToken);
- mockParse.Parse.Query().first.mockResolvedValue(null);
- await authenticate(
- mockRequest as Request,
- mockResponse as Response,
- nextFunction
- );
- expect(mockJwt.verify).toHaveBeenCalledWith(
- mockToken,
- process.env.JWT_SECRET
- );
- expect(nextFunction).toHaveBeenCalledWith();
- });
- });
- });
|