- 更新管理员控制器和数据库管理功能 - 完善管理员操作日志系统 - 添加全面的属性测试覆盖 - 优化用户管理和用户档案服务 - 更新代码检查规范文档 功能改进: - 增强管理员权限验证 - 完善操作日志记录 - 优化数据库管理接口 - 提升系统安全性和可维护性
542 lines
21 KiB
TypeScript
542 lines
21 KiB
TypeScript
/**
|
||
* 性能监控属性测试
|
||
*
|
||
* 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 }
|
||
);
|
||
});
|
||
});
|
||
}); |