feat:实现管理员系统核心功能

- 添加管理员数据库管理控制器和服务
- 实现管理员操作日志记录系统
- 添加数据库异常处理过滤器
- 完善管理员权限验证和响应格式
- 添加全面的属性测试覆盖
This commit is contained in:
moyin
2026-01-08 23:05:34 +08:00
parent 0f37130832
commit 6924416bbd
34 changed files with 9481 additions and 199 deletions

View File

@@ -0,0 +1,541 @@
/**
* 性能监控属性测试
*
* Property 13: 性能监控准确性
*
* Validates: Requirements 8.1, 8.2
*
* 测试目标:
* - 验证性能监控数据的准确性
* - 确保性能指标收集的完整性
* - 验证性能警告机制的有效性
*
* 最近修改:
* - 2026-01-08: 注释规范优化 - 修正@author字段更新版本号和修改记录 (修改者: moyin)
* - 2026-01-08: 功能新增 - 创建性能监控属性测试 (修改者: assistant)
*
* @author moyin
* @version 1.0.1
* @since 2026-01-08
* @lastModified 2026-01-08
*/
import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AdminDatabaseController } from '../../controllers/admin_database.controller';
import { DatabaseManagementService } from '../../services/database_management.service';
import { AdminOperationLogService } from '../../services/admin_operation_log.service';
import { AdminOperationLogInterceptor } from '../../admin_operation_log.interceptor';
import { AdminDatabaseExceptionFilter } from '../../admin_database_exception.filter';
import { AdminGuard } from '../../admin.guard';
import { UserStatus } from '../../../../core/db/users/user_status.enum';
import {
PropertyTestRunner,
PropertyTestGenerators,
PropertyTestAssertions,
DEFAULT_PROPERTY_CONFIG
} from './admin_property_test.base';
describe('Property Test: 性能监控功能', () => {
let app: INestApplication;
let module: TestingModule;
let controller: AdminDatabaseController;
let performanceMetrics: any[] = [];
let mockUsersService: any;
let mockUserProfilesService: any;
let mockZulipAccountsService: any;
beforeAll(async () => {
performanceMetrics = [];
// 创建性能监控mock
const createPerformanceAwareMock = (serviceName: string, methodName: string, baseDelay: number = 50) => {
return jest.fn().mockImplementation(async (...args) => {
const startTime = Date.now();
// 模拟不同的执行时间
const randomDelay = baseDelay + Math.random() * 100;
await new Promise(resolve => setTimeout(resolve, randomDelay));
const endTime = Date.now();
const duration = endTime - startTime;
// 记录性能指标
performanceMetrics.push({
service: serviceName,
method: methodName,
duration,
timestamp: new Date().toISOString(),
args: args.length
});
// 根据方法返回适当的mock数据
if (methodName === 'findAll') {
return [];
} else if (methodName === 'count') {
return 0;
} else if (methodName === 'findOne' || methodName === 'findById') {
if (serviceName === 'UsersService') {
return { ...PropertyTestGenerators.generateUser(), id: BigInt(1) };
} else if (serviceName === 'UserProfilesService') {
return { ...PropertyTestGenerators.generateUserProfile(), id: BigInt(1) };
} else {
return { ...PropertyTestGenerators.generateZulipAccount(), id: '1' };
}
} else if (methodName === 'create') {
if (serviceName === 'UsersService') {
return { ...args[0], id: BigInt(1) };
} else if (serviceName === 'UserProfilesService') {
return { ...args[0], id: BigInt(1) };
} else {
return { ...args[0], id: '1' };
}
} else if (methodName === 'update') {
if (serviceName === 'UsersService') {
return { ...PropertyTestGenerators.generateUser(), ...args[1], id: args[0] };
} else if (serviceName === 'UserProfilesService') {
return { ...PropertyTestGenerators.generateUserProfile(), ...args[1], id: args[0] };
} else {
return { ...PropertyTestGenerators.generateZulipAccount(), ...args[1], id: args[0] };
}
} else if (methodName === 'findMany') {
return { accounts: [], total: 0 };
} else if (methodName === 'getStatusStatistics') {
return { active: 0, inactive: 0, suspended: 0, error: 0, total: 0 };
}
return {};
});
};
mockUsersService = {
findAll: createPerformanceAwareMock('UsersService', 'findAll', 30),
findOne: createPerformanceAwareMock('UsersService', 'findOne', 20),
create: createPerformanceAwareMock('UsersService', 'create', 80),
update: createPerformanceAwareMock('UsersService', 'update', 60),
remove: createPerformanceAwareMock('UsersService', 'remove', 40),
search: createPerformanceAwareMock('UsersService', 'search', 100),
count: createPerformanceAwareMock('UsersService', 'count', 25)
};
mockUserProfilesService = {
findAll: createPerformanceAwareMock('UserProfilesService', 'findAll', 35),
findOne: createPerformanceAwareMock('UserProfilesService', 'findOne', 25),
create: createPerformanceAwareMock('UserProfilesService', 'create', 90),
update: createPerformanceAwareMock('UserProfilesService', 'update', 70),
remove: createPerformanceAwareMock('UserProfilesService', 'remove', 45),
findByMap: createPerformanceAwareMock('UserProfilesService', 'findByMap', 120),
count: createPerformanceAwareMock('UserProfilesService', 'count', 30)
};
mockZulipAccountsService = {
findMany: createPerformanceAwareMock('ZulipAccountsService', 'findMany', 40),
findById: createPerformanceAwareMock('ZulipAccountsService', 'findById', 30),
create: createPerformanceAwareMock('ZulipAccountsService', 'create', 100),
update: createPerformanceAwareMock('ZulipAccountsService', 'update', 80),
delete: createPerformanceAwareMock('ZulipAccountsService', 'delete', 50),
getStatusStatistics: createPerformanceAwareMock('ZulipAccountsService', 'getStatusStatistics', 60)
};
module = await Test.createTestingModule({
imports: [
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.test', '.env']
})
],
controllers: [AdminDatabaseController],
providers: [
DatabaseManagementService,
{
provide: AdminOperationLogService,
useValue: {
createLog: jest.fn().mockResolvedValue({}),
queryLogs: jest.fn().mockResolvedValue({ logs: [], total: 0 }),
getLogById: jest.fn().mockResolvedValue(null),
getStatistics: jest.fn().mockResolvedValue({}),
cleanupExpiredLogs: jest.fn().mockResolvedValue(0),
getAdminOperationHistory: jest.fn().mockResolvedValue([]),
getSensitiveOperations: jest.fn().mockResolvedValue({ logs: [], total: 0 })
}
},
{
provide: AdminOperationLogInterceptor,
useValue: {
intercept: jest.fn().mockImplementation((context, next) => next.handle())
}
},
{
provide: 'UsersService',
useValue: mockUsersService
},
{
provide: 'IUserProfilesService',
useValue: mockUserProfilesService
},
{
provide: 'ZulipAccountsService',
useValue: mockZulipAccountsService
}
]
})
.overrideGuard(AdminGuard)
.useValue({ canActivate: () => true })
.compile();
app = module.createNestApplication();
app.useGlobalFilters(new AdminDatabaseExceptionFilter());
await app.init();
controller = module.get<AdminDatabaseController>(AdminDatabaseController);
});
afterAll(async () => {
await app.close();
});
beforeEach(() => {
performanceMetrics.length = 0; // 清空性能指标
});
describe('Property 13: 性能监控准确性', () => {
it('操作执行时间应该被准确记录', async () => {
await PropertyTestRunner.runPropertyTest(
'操作执行时间记录准确性',
() => PropertyTestGenerators.generateUser(),
async (userData) => {
const startTime = Date.now();
// 执行操作
await controller.createUser({
...userData,
status: UserStatus.ACTIVE
});
const endTime = Date.now();
const totalDuration = endTime - startTime;
// 验证性能指标被记录
const createMetrics = performanceMetrics.filter(m =>
m.service === 'UsersService' && m.method === 'create'
);
expect(createMetrics.length).toBeGreaterThan(0);
const createMetric = createMetrics[0];
expect(createMetric.duration).toBeGreaterThan(0);
expect(createMetric.duration).toBeLessThan(totalDuration + 50); // 允许一些误差
expect(createMetric.timestamp).toBeDefined();
// 验证时间戳格式
const timestamp = new Date(createMetric.timestamp);
expect(timestamp.toISOString()).toBe(createMetric.timestamp);
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 20 }
);
});
it('不同操作类型的性能指标应该被正确分类', async () => {
await PropertyTestRunner.runPropertyTest(
'操作类型性能分类',
() => ({
user: PropertyTestGenerators.generateUser(),
profile: PropertyTestGenerators.generateUserProfile(),
zulipAccount: PropertyTestGenerators.generateZulipAccount()
}),
async ({ user, profile, zulipAccount }) => {
// 执行不同类型的操作
await controller.getUserList(10, 0);
await controller.createUser({ ...user, status: UserStatus.ACTIVE });
await controller.getUserProfileList(10, 0);
await controller.createUserProfile(profile);
await controller.getZulipAccountList(10, 0);
await controller.createZulipAccount(zulipAccount);
// 验证不同服务的性能指标
const userServiceMetrics = performanceMetrics.filter(m => m.service === 'UsersService');
const profileServiceMetrics = performanceMetrics.filter(m => m.service === 'UserProfilesService');
const zulipServiceMetrics = performanceMetrics.filter(m => m.service === 'ZulipAccountsService');
expect(userServiceMetrics.length).toBeGreaterThan(0);
expect(profileServiceMetrics.length).toBeGreaterThan(0);
expect(zulipServiceMetrics.length).toBeGreaterThan(0);
// 验证方法分类
const createMethods = performanceMetrics.filter(m => m.method === 'create');
const findAllMethods = performanceMetrics.filter(m => m.method === 'findAll');
const countMethods = performanceMetrics.filter(m => m.method === 'count');
expect(createMethods.length).toBe(3); // 三个create操作
expect(findAllMethods.length).toBe(3); // 三个findAll操作
expect(countMethods.length).toBe(3); // 三个count操作
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 15 }
);
});
it('复杂查询的性能应该被正确监控', async () => {
await PropertyTestRunner.runPropertyTest(
'复杂查询性能监控',
() => ({
searchTerm: PropertyTestGenerators.generateUser().username.substring(0, 3),
mapName: ['plaza', 'forest', 'beach'][Math.floor(Math.random() * 3)],
limit: Math.floor(Math.random() * 50) + 10,
offset: Math.floor(Math.random() * 100)
}),
async ({ searchTerm, mapName, limit, offset }) => {
// 执行复杂查询操作
await controller.searchUsers(searchTerm, limit);
await controller.getUserProfilesByMap(mapName, limit, offset);
await controller.getZulipAccountStatistics();
// 验证复杂查询的性能指标
const searchMetrics = performanceMetrics.filter(m => m.method === 'search');
const mapQueryMetrics = performanceMetrics.filter(m => m.method === 'findByMap');
const statsMetrics = performanceMetrics.filter(m => m.method === 'getStatusStatistics');
expect(searchMetrics.length).toBeGreaterThan(0);
expect(mapQueryMetrics.length).toBeGreaterThan(0);
expect(statsMetrics.length).toBeGreaterThan(0);
// 验证复杂查询通常耗时更长
const searchDuration = searchMetrics[0].duration;
const mapQueryDuration = mapQueryMetrics[0].duration;
const statsDuration = statsMetrics[0].duration;
expect(searchDuration).toBeGreaterThan(50); // 搜索操作基础延迟100ms
expect(mapQueryDuration).toBeGreaterThan(70); // 地图查询基础延迟120ms
expect(statsDuration).toBeGreaterThan(30); // 统计查询基础延迟60ms
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 15 }
);
});
it('批量操作的性能应该被准确监控', async () => {
await PropertyTestRunner.runPropertyTest(
'批量操作性能监控',
() => {
const accountIds = Array.from({ length: Math.floor(Math.random() * 5) + 2 },
(_, i) => `account_${i + 1}`);
const targetStatus = ['active', 'inactive', 'suspended'][Math.floor(Math.random() * 3)];
return { accountIds, targetStatus };
},
async ({ accountIds, targetStatus }) => {
const startTime = Date.now();
// 执行批量操作
await controller.batchUpdateZulipAccountStatus({
ids: accountIds,
status: targetStatus as any,
reason: '性能测试批量更新'
});
const endTime = Date.now();
const totalDuration = endTime - startTime;
// 验证批量操作的性能指标
const updateMetrics = performanceMetrics.filter(m =>
m.service === 'ZulipAccountsService' && m.method === 'update'
);
expect(updateMetrics.length).toBe(accountIds.length);
// 验证每个更新操作的性能
updateMetrics.forEach(metric => {
expect(metric.duration).toBeGreaterThan(0);
expect(metric.duration).toBeLessThan(200); // 单个操作不应超过200ms
});
// 验证总体性能合理性
const totalServiceTime = updateMetrics.reduce((sum, m) => sum + m.duration, 0);
expect(totalServiceTime).toBeLessThan(totalDuration + 100); // 允许一些并发优化
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 10 }
);
});
it('性能异常应该被正确识别', async () => {
await PropertyTestRunner.runPropertyTest(
'性能异常识别',
() => PropertyTestGenerators.generateUser(),
async (userData) => {
// 模拟慢查询(通过增加延迟)
const originalFindOne = mockUsersService.findOne;
mockUsersService.findOne = jest.fn().mockImplementation(async (...args) => {
const startTime = Date.now();
// 模拟异常慢的查询
await new Promise(resolve => setTimeout(resolve, 300));
const endTime = Date.now();
const duration = endTime - startTime;
performanceMetrics.push({
service: 'UsersService',
method: 'findOne',
duration,
timestamp: new Date().toISOString(),
args: args.length,
slow: duration > 200 // 标记为慢查询
});
return { ...PropertyTestGenerators.generateUser(), id: BigInt(1) };
});
// 执行操作
await controller.getUserById('1');
// 恢复原始mock
mockUsersService.findOne = originalFindOne;
// 验证慢查询被识别
const slowQueries = performanceMetrics.filter(m => m.slow === true);
expect(slowQueries.length).toBeGreaterThan(0);
const slowQuery = slowQueries[0];
expect(slowQuery.duration).toBeGreaterThan(200);
expect(slowQuery.service).toBe('UsersService');
expect(slowQuery.method).toBe('findOne');
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 10 }
);
});
it('并发操作的性能应该被独立监控', async () => {
await PropertyTestRunner.runPropertyTest(
'并发操作性能监控',
() => ({
concurrentCount: Math.floor(Math.random() * 3) + 2 // 2-4个并发操作
}),
async ({ concurrentCount }) => {
const promises = [];
const startTime = Date.now();
// 创建并发操作
for (let i = 0; i < concurrentCount; i++) {
const user = PropertyTestGenerators.generateUser();
promises.push(
controller.createUser({
...user,
status: UserStatus.ACTIVE,
username: `${user.username}_${i}` // 确保唯一性
})
);
}
// 等待所有操作完成
await Promise.all(promises);
const endTime = Date.now();
const totalDuration = endTime - startTime;
// 验证并发操作的性能指标
const createMetrics = performanceMetrics.filter(m =>
m.service === 'UsersService' && m.method === 'create'
);
expect(createMetrics.length).toBe(concurrentCount);
// 验证每个操作都有独立的性能记录
createMetrics.forEach((metric, index) => {
expect(metric.duration).toBeGreaterThan(0);
expect(metric.timestamp).toBeDefined();
// 验证时间戳在合理范围内
const metricTime = new Date(metric.timestamp).getTime();
expect(metricTime).toBeGreaterThanOrEqual(startTime);
expect(metricTime).toBeLessThanOrEqual(endTime);
});
// 验证并发执行的效率
const avgDuration = createMetrics.reduce((sum, m) => sum + m.duration, 0) / concurrentCount;
expect(totalDuration).toBeLessThan(avgDuration * concurrentCount * 1.2); // 并发应该有一定效率提升
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 10 }
);
});
it('性能统计数据应该准确计算', async () => {
await PropertyTestRunner.runPropertyTest(
'性能统计准确性',
() => ({
operationCount: Math.floor(Math.random() * 8) + 3 // 3-10个操作
}),
async ({ operationCount }) => {
// 执行多个操作
for (let i = 0; i < operationCount; i++) {
await controller.getUserList(10, i * 10);
}
// 计算性能统计
const findAllMetrics = performanceMetrics.filter(m =>
m.service === 'UsersService' && m.method === 'findAll'
);
expect(findAllMetrics.length).toBe(operationCount);
// 计算统计数据
const durations = findAllMetrics.map(m => m.duration);
const totalDuration = durations.reduce((sum, d) => sum + d, 0);
const avgDuration = totalDuration / durations.length;
const minDuration = Math.min(...durations);
const maxDuration = Math.max(...durations);
// 验证统计数据合理性
expect(totalDuration).toBeGreaterThan(0);
expect(avgDuration).toBeGreaterThan(0);
expect(avgDuration).toBeGreaterThanOrEqual(minDuration);
expect(avgDuration).toBeLessThanOrEqual(maxDuration);
expect(minDuration).toBeLessThanOrEqual(maxDuration);
// 验证平均值在合理范围内基础延迟30ms + 随机100ms
expect(avgDuration).toBeGreaterThan(20);
expect(avgDuration).toBeLessThan(200);
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 15 }
);
});
it('性能监控不应该显著影响操作性能', async () => {
await PropertyTestRunner.runPropertyTest(
'性能监控开销验证',
() => PropertyTestGenerators.generateUser(),
async (userData) => {
const iterations = 5;
const durations = [];
// 执行多次相同操作
for (let i = 0; i < iterations; i++) {
const startTime = Date.now();
await controller.createUser({
...userData,
status: UserStatus.ACTIVE,
username: `${userData.username}_${i}`
});
const endTime = Date.now();
durations.push(endTime - startTime);
}
// 验证性能一致性
const avgDuration = durations.reduce((sum, d) => sum + d, 0) / durations.length;
const maxVariation = Math.max(...durations) - Math.min(...durations);
// 性能变化不应该太大(监控开销应该很小)
expect(maxVariation).toBeLessThan(avgDuration * 0.5); // 变化不超过平均值的50%
// 验证所有操作都被监控
const createMetrics = performanceMetrics.filter(m =>
m.service === 'UsersService' && m.method === 'create'
);
expect(createMetrics.length).toBe(iterations);
},
{ ...DEFAULT_PROPERTY_CONFIG, iterations: 10 }
);
});
});
});