Files
whale-town-end/src/core/security_core/throttle.guard.spec.ts
moyin 8816b29b0a chore: 更新项目配置和核心服务
- 更新package.json和jest配置
- 更新main.ts启动配置
- 完善用户管理和数据库服务
- 更新安全核心模块
- 优化Zulip核心服务

配置改进:
- 统一项目依赖管理
- 优化测试配置
- 完善服务模块化架构
2026-01-09 17:03:57 +08:00

120 lines
3.1 KiB
TypeScript

/**
* ThrottleGuard 单元测试
*
* @author moyin
* @version 1.0.0
* @since 2026-01-07
*/
import { Test, TestingModule } from '@nestjs/testing';
import { ExecutionContext, HttpException, HttpStatus } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ThrottleGuard } from './throttle.guard';
import { ThrottleConfig } from './throttle.decorator';
describe('ThrottleGuard', () => {
let guard: ThrottleGuard;
let reflector: jest.Mocked<Reflector>;
beforeEach(async () => {
const mockReflector = {
get: jest.fn(),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
ThrottleGuard,
{ provide: Reflector, useValue: mockReflector },
],
}).compile();
guard = module.get<ThrottleGuard>(ThrottleGuard);
reflector = module.get(Reflector);
});
afterEach(() => {
jest.clearAllMocks();
guard.clearAllRecords();
// 确保清理定时器
guard.onModuleDestroy();
});
describe('canActivate', () => {
it('should allow request when no throttle config is found', async () => {
// Arrange
reflector.get.mockReturnValue(null);
const mockContext = createMockContext();
// Act
const result = await guard.canActivate(mockContext);
// Assert
expect(result).toBe(true);
});
it('should allow first request within limit', async () => {
// Arrange
const config: ThrottleConfig = { limit: 5, ttl: 60 };
reflector.get.mockReturnValueOnce(config).mockReturnValueOnce(null);
const mockContext = createMockContext();
// Act
const result = await guard.canActivate(mockContext);
// Assert
expect(result).toBe(true);
});
it('should throw HttpException when limit exceeded', async () => {
// Arrange
const config: ThrottleConfig = { limit: 1, ttl: 60 };
reflector.get.mockReturnValue(config);
const mockContext = createMockContext();
// Act - first request should pass
await guard.canActivate(mockContext);
// Assert - second request should throw
await expect(guard.canActivate(mockContext)).rejects.toThrow(HttpException);
});
});
describe('getStats', () => {
it('should return empty stats initially', () => {
const stats = guard.getStats();
expect(stats.totalRecords).toBe(0);
});
});
describe('clearAllRecords', () => {
it('should clear all records', () => {
guard.clearAllRecords();
const stats = guard.getStats();
expect(stats.totalRecords).toBe(0);
});
});
describe('onModuleDestroy', () => {
it('should cleanup resources', () => {
expect(() => guard.onModuleDestroy()).not.toThrow();
});
});
function createMockContext(): ExecutionContext {
const mockRequest = {
ip: '127.0.0.1',
method: 'POST',
url: '/api/test',
route: { path: '/api/test' },
get: jest.fn(),
};
return {
switchToHttp: jest.fn().mockReturnValue({
getRequest: jest.fn().mockReturnValue(mockRequest),
}),
getHandler: jest.fn(),
getClass: jest.fn(),
} as any;
}
});