refactor:项目架构重构和命名规范化
- 统一文件命名为snake_case格式(kebab-case snake_case) - 重构zulip模块为zulip_core,明确Core层职责 - 重构user-mgmt模块为user_mgmt,统一命名规范 - 调整模块依赖关系,优化架构分层 - 删除过时的文件和目录结构 - 更新相关文档和配置文件 本次重构涉及大量文件重命名和模块重组, 旨在建立更清晰的项目架构和统一的命名规范。
This commit is contained in:
118
src/core/security_core/throttle.guard.spec.ts
Normal file
118
src/core/security_core/throttle.guard.spec.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/**
|
||||
* 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();
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user