Files
whale-town-end/src/business/admin/admin_operation_log.controller.spec.ts
moyin 5f662ef091 feat: 完善管理员系统和用户管理模块
- 更新管理员控制器和数据库管理功能
- 完善管理员操作日志系统
- 添加全面的属性测试覆盖
- 优化用户管理和用户档案服务
- 更新代码检查规范文档

功能改进:
- 增强管理员权限验证
- 完善操作日志记录
- 优化数据库管理接口
- 提升系统安全性和可维护性
2026-01-09 17:05:08 +08:00

284 lines
8.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* AdminOperationLogController 单元测试
*
* 功能描述:
* - 测试管理员操作日志控制器的所有HTTP端点
* - 验证请求参数处理和响应格式
* - 测试权限验证和异常处理
*
* 职责分离:
* - HTTP层测试不涉及业务逻辑实现
* - Mock业务服务专注控制器逻辑
* - 验证请求响应的正确性
*
* 最近修改:
* - 2026-01-09: 功能新增 - 创建AdminOperationLogController单元测试 (修改者: moyin)
*
* @author moyin
* @version 1.0.0
* @since 2026-01-09
* @lastModified 2026-01-09
*/
import { Test, TestingModule } from '@nestjs/testing';
import { AdminOperationLogController } from './admin_operation_log.controller';
import { AdminOperationLogService } from './admin_operation_log.service';
import { AdminGuard } from './admin.guard';
import { AdminOperationLog } from './admin_operation_log.entity';
describe('AdminOperationLogController', () => {
let controller: AdminOperationLogController;
let logService: jest.Mocked<AdminOperationLogService>;
const mockLogService = {
queryLogs: jest.fn(),
getLogById: jest.fn(),
getStatistics: jest.fn(),
getSensitiveOperations: jest.fn(),
getAdminOperationHistory: jest.fn(),
cleanupExpiredLogs: jest.fn(),
};
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [AdminOperationLogController],
providers: [
{
provide: AdminOperationLogService,
useValue: mockLogService,
},
],
})
.overrideGuard(AdminGuard)
.useValue({ canActivate: () => true })
.compile();
controller = module.get<AdminOperationLogController>(AdminOperationLogController);
logService = module.get(AdminOperationLogService);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('getOperationLogs', () => {
it('should query logs with default parameters', async () => {
const mockLogs = [
{ id: 'log1', operation_type: 'CREATE' },
{ id: 'log2', operation_type: 'UPDATE' },
] as AdminOperationLog[];
logService.queryLogs.mockResolvedValue({ logs: mockLogs, total: 2 });
const result = await controller.getOperationLogs(50, 0);
expect(logService.queryLogs).toHaveBeenCalledWith({
limit: 50,
offset: 0
});
expect(result.success).toBe(true);
expect(result.data.items).toEqual(mockLogs);
expect(result.data.total).toBe(2);
});
it('should query logs with custom parameters', async () => {
const mockLogs = [] as AdminOperationLog[];
logService.queryLogs.mockResolvedValue({ logs: mockLogs, total: 0 });
const result = await controller.getOperationLogs(
20,
10,
'admin1',
'CREATE',
'users',
'SUCCESS',
'2026-01-01',
'2026-01-31',
'true'
);
expect(logService.queryLogs).toHaveBeenCalledWith({
adminUserId: 'admin1',
operationType: 'CREATE',
targetType: 'users',
operationResult: 'SUCCESS',
startDate: new Date('2026-01-01'),
endDate: new Date('2026-01-31'),
isSensitive: true,
limit: 20,
offset: 10
});
expect(result.success).toBe(true);
});
it('should handle invalid date parameters', async () => {
await expect(controller.getOperationLogs(
50,
0,
undefined,
undefined,
undefined,
undefined,
'invalid',
'invalid'
)).rejects.toThrow('日期格式无效请使用ISO格式');
});
it('should handle service error', async () => {
logService.queryLogs.mockRejectedValue(new Error('Database error'));
await expect(controller.getOperationLogs(50, 0)).rejects.toThrow('Database error');
});
});
describe('getOperationLogById', () => {
it('should get log by id successfully', async () => {
const mockLog = {
id: 'log1',
operation_type: 'CREATE',
target_type: 'users'
} as AdminOperationLog;
logService.getLogById.mockResolvedValue(mockLog);
const result = await controller.getOperationLogById('log1');
expect(logService.getLogById).toHaveBeenCalledWith('log1');
expect(result.success).toBe(true);
expect(result.data).toEqual(mockLog);
});
it('should handle log not found', async () => {
logService.getLogById.mockResolvedValue(null);
await expect(controller.getOperationLogById('nonexistent')).rejects.toThrow('操作日志不存在');
});
it('should handle service error', async () => {
logService.getLogById.mockRejectedValue(new Error('Database error'));
await expect(controller.getOperationLogById('log1')).rejects.toThrow('Database error');
});
});
describe('getOperationStatistics', () => {
it('should get statistics successfully', async () => {
const mockStats = {
totalOperations: 100,
successfulOperations: 80,
failedOperations: 20,
operationsByType: { CREATE: 50, UPDATE: 30, DELETE: 20 },
operationsByTarget: { users: 60, profiles: 40 },
operationsByAdmin: { admin1: 60, admin2: 40 },
averageDuration: 150.5,
sensitiveOperations: 10,
uniqueAdmins: 5
};
logService.getStatistics.mockResolvedValue(mockStats);
const result = await controller.getOperationStatistics();
expect(logService.getStatistics).toHaveBeenCalledWith(undefined, undefined);
expect(result.success).toBe(true);
expect(result.data).toEqual(mockStats);
});
it('should get statistics with date range', async () => {
const mockStats = {
totalOperations: 50,
successfulOperations: 40,
failedOperations: 10,
operationsByType: {},
operationsByTarget: {},
operationsByAdmin: {},
averageDuration: 100,
sensitiveOperations: 5,
uniqueAdmins: 3
};
logService.getStatistics.mockResolvedValue(mockStats);
const result = await controller.getOperationStatistics('2026-01-01', '2026-01-31');
expect(logService.getStatistics).toHaveBeenCalledWith(
new Date('2026-01-01'),
new Date('2026-01-31')
);
expect(result.success).toBe(true);
});
it('should handle invalid dates', async () => {
await expect(controller.getOperationStatistics('invalid', 'invalid')).rejects.toThrow('日期格式无效请使用ISO格式');
});
it('should handle service error', async () => {
logService.getStatistics.mockRejectedValue(new Error('Statistics error'));
await expect(controller.getOperationStatistics()).rejects.toThrow('Statistics error');
});
});
describe('getSensitiveOperations', () => {
it('should get sensitive operations successfully', async () => {
const mockLogs = [
{ id: 'log1', is_sensitive: true }
] as AdminOperationLog[];
logService.getSensitiveOperations.mockResolvedValue({ logs: mockLogs, total: 1 });
const result = await controller.getSensitiveOperations(50, 0);
expect(logService.getSensitiveOperations).toHaveBeenCalledWith(50, 0);
expect(result.success).toBe(true);
expect(result.data.items).toEqual(mockLogs);
expect(result.data.total).toBe(1);
});
it('should get sensitive operations with pagination', async () => {
logService.getSensitiveOperations.mockResolvedValue({ logs: [], total: 0 });
const result = await controller.getSensitiveOperations(20, 10);
expect(logService.getSensitiveOperations).toHaveBeenCalledWith(20, 10);
});
it('should handle service error', async () => {
logService.getSensitiveOperations.mockRejectedValue(new Error('Query error'));
await expect(controller.getSensitiveOperations(50, 0)).rejects.toThrow('Query error');
});
});
describe('cleanupExpiredLogs', () => {
it('should cleanup logs successfully', async () => {
logService.cleanupExpiredLogs.mockResolvedValue(25);
const result = await controller.cleanupExpiredLogs(90);
expect(logService.cleanupExpiredLogs).toHaveBeenCalledWith(90);
expect(result.success).toBe(true);
expect(result.data.deleted_count).toBe(25);
});
it('should cleanup logs with custom retention days', async () => {
logService.cleanupExpiredLogs.mockResolvedValue(10);
const result = await controller.cleanupExpiredLogs(30);
expect(logService.cleanupExpiredLogs).toHaveBeenCalledWith(30);
expect(result.data.deleted_count).toBe(10);
});
it('should handle invalid retention days', async () => {
await expect(controller.cleanupExpiredLogs(5)).rejects.toThrow('保留天数必须在7-365天之间');
});
it('should handle service error', async () => {
logService.cleanupExpiredLogs.mockRejectedValue(new Error('Cleanup error'));
await expect(controller.cleanupExpiredLogs(90)).rejects.toThrow('Cleanup error');
});
});
});