/**
 * JwtAuthGuard 单元测试
 * 
 * 功能描述：
 * - 测试JWT认证守卫的令牌验证功能
 * - 验证用户信息提取和注入
 * - 测试认证失败的异常处理
 * 
 * 最近修改：
 * - 2026-01-14: 架构重构 - 从business层移动到gateway层 (修改者: moyin)
 * - 2026-01-12: 代码规范优化 - 创建缺失的守卫测试文件 (修改者: moyin)
 * 
 * @author moyin
 * @version 1.1.0
 * @since 2026-01-12
 * @lastModified 2026-01-14
 */

import { Test, TestingModule } from '@nestjs/testing';
import { ExecutionContext, UnauthorizedException } from '@nestjs/common';
import { JwtAuthGuard } from './jwt_auth.guard';
import { LoginCoreService } from '../../core/login_core/login_core.service';

describe('JwtAuthGuard', () => {
  let guard: JwtAuthGuard;
  let loginCoreService: jest.Mocked<LoginCoreService>;
  let mockExecutionContext: jest.Mocked<ExecutionContext>;
  let mockRequest: any;

  beforeEach(async () => {
    const mockLoginCoreService = {
      verifyToken: jest.fn(),
    };

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        JwtAuthGuard,
        {
          provide: LoginCoreService,
          useValue: mockLoginCoreService,
        },
      ],
    }).compile();

    guard = module.get<JwtAuthGuard>(JwtAuthGuard);
    loginCoreService = module.get(LoginCoreService);

    // Mock request object
    mockRequest = {
      headers: {},
      user: undefined,
    };

    // Mock execution context
    mockExecutionContext = {
      switchToHttp: jest.fn().mockReturnValue({
        getRequest: jest.fn().mockReturnValue(mockRequest),
      }),
    } as any;
  });

  it('should be defined', () => {
    expect(guard).toBeDefined();
  });

  describe('canActivate', () => {
    it('should allow access with valid JWT token', async () => {
      const mockPayload = {
        sub: '1',
        username: 'testuser',
        role: 1,
        type: 'access' as const,
        iat: Math.floor(Date.now() / 1000),
        exp: Math.floor(Date.now() / 1000) + 3600,
      };

      mockRequest.headers.authorization = 'Bearer valid_jwt_token';
      loginCoreService.verifyToken.mockResolvedValue(mockPayload);

      const result = await guard.canActivate(mockExecutionContext);

      expect(result).toBe(true);
      expect(mockRequest.user).toEqual(mockPayload);
      expect(loginCoreService.verifyToken).toHaveBeenCalledWith('valid_jwt_token', 'access');
    });

    it('should deny access when authorization header is missing', async () => {
      mockRequest.headers.authorization = undefined;

      await expect(guard.canActivate(mockExecutionContext))
        .rejects.toThrow(UnauthorizedException);
      
      expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
    });

    it('should deny access when token format is invalid', async () => {
      mockRequest.headers.authorization = 'InvalidFormat token';

      await expect(guard.canActivate(mockExecutionContext))
        .rejects.toThrow(UnauthorizedException);
      
      expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
    });

    it('should deny access when token is not Bearer type', async () => {
      mockRequest.headers.authorization = 'Basic dXNlcjpwYXNz';

      await expect(guard.canActivate(mockExecutionContext))
        .rejects.toThrow(UnauthorizedException);
      
      expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
    });

    it('should deny access when JWT token verification fails', async () => {
      mockRequest.headers.authorization = 'Bearer invalid_jwt_token';
      loginCoreService.verifyToken.mockRejectedValue(new Error('Token expired'));

      await expect(guard.canActivate(mockExecutionContext))
        .rejects.toThrow(UnauthorizedException);
      
      expect(loginCoreService.verifyToken).toHaveBeenCalledWith('invalid_jwt_token', 'access');
    });

    it('should extract token correctly from Authorization header', async () => {
      const mockPayload = {
        sub: '1',
        username: 'testuser',
        role: 1,
        type: 'access' as const,
        iat: Math.floor(Date.now() / 1000),
        exp: Math.floor(Date.now() / 1000) + 3600,
      };

      mockRequest.headers.authorization = 'Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test.token';
      loginCoreService.verifyToken.mockResolvedValue(mockPayload);

      const result = await guard.canActivate(mockExecutionContext);

      expect(result).toBe(true);
      expect(loginCoreService.verifyToken).toHaveBeenCalledWith(
        'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.test.token',
        'access'
      );
    });

    it('should handle empty token after Bearer', async () => {
      mockRequest.headers.authorization = 'Bearer ';

      await expect(guard.canActivate(mockExecutionContext))
        .rejects.toThrow(UnauthorizedException);
      
      expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
    });

    it('should handle authorization header with only Bearer', async () => {
      mockRequest.headers.authorization = 'Bearer';

      await expect(guard.canActivate(mockExecutionContext))
        .rejects.toThrow(UnauthorizedException);
      
      expect(loginCoreService.verifyToken).not.toHaveBeenCalled();
    });
  });
});
