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

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

542 lines
21 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.
/**
* 性能监控属性测试
*
* 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 './admin_database.controller';
import { DatabaseManagementService } from './database_management.service';
import { AdminOperationLogService } from './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 '../user_mgmt/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),
batchUpdateStatus: createPerformanceAwareMock('ZulipAccountsService', 'batchUpdateStatus', 120),
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 }
);
});
});
});