refactor(auth): 重构认证模块架构 - 将Gateway层组件从Business层分离
范围:src/gateway/auth/, src/business/auth/, src/app.module.ts 涉及文件: - 新增:src/gateway/auth/ 目录及所有文件 - 移动:Controller、Guard、Decorator、DTO从business层移至gateway层 - 修改:src/business/auth/index.ts(移除Gateway层组件导出) - 修改:src/app.module.ts(使用AuthGatewayModule替代AuthModule) 主要改进: - 明确Gateway层和Business层的职责边界 - Controller、Guard、Decorator属于Gateway层职责 - Business层专注于业务逻辑和服务 - 符合分层架构设计原则
This commit is contained in:
164
src/gateway/auth/jwt_auth.guard.spec.ts
Normal file
164
src/gateway/auth/jwt_auth.guard.spec.ts
Normal file
@@ -0,0 +1,164 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user