refactor:重构Zulip模块按业务功能模块化架构
- 将技术实现服务从business层迁移到core层 - 创建src/core/zulip/核心服务模块,包含API客户端、连接池等技术服务 - 保留src/business/zulip/业务逻辑,专注游戏相关的业务规则 - 通过依赖注入实现业务层与核心层的解耦 - 更新模块导入关系,确保架构分层清晰 重构后的架构符合单一职责原则,提高了代码的可维护性和可测试性
This commit is contained in:
733
src/core/zulip/services/monitoring.service.spec.ts
Normal file
733
src/core/zulip/services/monitoring.service.spec.ts
Normal file
@@ -0,0 +1,733 @@
|
||||
/**
|
||||
* 监控服务测试
|
||||
*
|
||||
* 功能描述:
|
||||
* - 测试MonitoringService的核心功能
|
||||
* - 包含属性测试验证操作确认和日志记录
|
||||
* - 包含属性测试验证系统监控和告警
|
||||
*
|
||||
* **Feature: zulip-integration, Property 10: 操作确认和日志记录**
|
||||
* **Validates: Requirements 4.5, 8.5, 9.1, 9.2, 9.3**
|
||||
*
|
||||
* **Feature: zulip-integration, Property 11: 系统监控和告警**
|
||||
* **Validates: Requirements 9.4**
|
||||
*
|
||||
* @author angjustinl
|
||||
* @version 1.0.0
|
||||
* @since 2025-12-25
|
||||
*/
|
||||
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Logger } from '@nestjs/common';
|
||||
import * as fc from 'fast-check';
|
||||
import {
|
||||
MonitoringService,
|
||||
ConnectionEventType,
|
||||
ApiCallResult,
|
||||
AlertLevel,
|
||||
ConnectionLog,
|
||||
ApiCallLog,
|
||||
MessageForwardLog,
|
||||
OperationConfirmation,
|
||||
} from './monitoring.service';
|
||||
|
||||
describe('MonitoringService', () => {
|
||||
let service: MonitoringService;
|
||||
let mockConfigService: jest.Mocked<ConfigService>;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Mock NestJS Logger
|
||||
jest.spyOn(Logger.prototype, 'log').mockImplementation();
|
||||
jest.spyOn(Logger.prototype, 'error').mockImplementation();
|
||||
jest.spyOn(Logger.prototype, 'warn').mockImplementation();
|
||||
jest.spyOn(Logger.prototype, 'debug').mockImplementation();
|
||||
|
||||
mockConfigService = {
|
||||
get: jest.fn((key: string, defaultValue?: any) => {
|
||||
const config: Record<string, any> = {
|
||||
'MONITORING_HEALTH_CHECK_INTERVAL': 60000,
|
||||
'MONITORING_ERROR_RATE_THRESHOLD': 0.1,
|
||||
'MONITORING_RESPONSE_TIME_THRESHOLD': 5000,
|
||||
'MONITORING_MEMORY_THRESHOLD': 0.9,
|
||||
};
|
||||
return config[key] ?? defaultValue;
|
||||
}),
|
||||
} as any;
|
||||
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [
|
||||
MonitoringService,
|
||||
{
|
||||
provide: ConfigService,
|
||||
useValue: mockConfigService,
|
||||
},
|
||||
],
|
||||
}).compile();
|
||||
|
||||
service = module.get<MonitoringService>(MonitoringService);
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
service.onModuleDestroy();
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
|
||||
describe('基础功能测试', () => {
|
||||
it('应该正确初始化统计数据', () => {
|
||||
const stats = service.getStats();
|
||||
expect(stats.connections.total).toBe(0);
|
||||
expect(stats.apiCalls.total).toBe(0);
|
||||
expect(stats.messages.upstream).toBe(0);
|
||||
expect(stats.alerts.total).toBe(0);
|
||||
});
|
||||
|
||||
it('应该正确重置统计数据', () => {
|
||||
// 添加一些数据
|
||||
service.logConnection({
|
||||
socketId: 'socket1',
|
||||
eventType: ConnectionEventType.CONNECTED,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
// 重置
|
||||
service.resetStats();
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.connections.total).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('连接日志测试', () => {
|
||||
it('应该正确记录连接事件', () => {
|
||||
service.logConnection({
|
||||
socketId: 'socket1',
|
||||
userId: 'user1',
|
||||
eventType: ConnectionEventType.CONNECTED,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.connections.total).toBe(1);
|
||||
expect(stats.connections.active).toBe(1);
|
||||
});
|
||||
|
||||
it('应该正确记录断开事件', () => {
|
||||
service.logConnection({
|
||||
socketId: 'socket1',
|
||||
eventType: ConnectionEventType.CONNECTED,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
service.logConnection({
|
||||
socketId: 'socket1',
|
||||
eventType: ConnectionEventType.DISCONNECTED,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.connections.active).toBe(0);
|
||||
});
|
||||
|
||||
it('应该正确记录错误事件', () => {
|
||||
service.logConnection({
|
||||
socketId: 'socket1',
|
||||
eventType: ConnectionEventType.ERROR,
|
||||
error: 'Connection failed',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.connections.errors).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('API调用日志测试', () => {
|
||||
it('应该正确记录成功的API调用', () => {
|
||||
service.logApiCall({
|
||||
operation: 'sendMessage',
|
||||
userId: 'user1',
|
||||
result: ApiCallResult.SUCCESS,
|
||||
responseTime: 100,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.apiCalls.total).toBe(1);
|
||||
expect(stats.apiCalls.success).toBe(1);
|
||||
});
|
||||
|
||||
it('应该正确记录失败的API调用', () => {
|
||||
service.logApiCall({
|
||||
operation: 'sendMessage',
|
||||
userId: 'user1',
|
||||
result: ApiCallResult.FAILURE,
|
||||
responseTime: 100,
|
||||
error: 'API error',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.apiCalls.failures).toBe(1);
|
||||
});
|
||||
|
||||
it('应该在响应时间过长时发送告警', () => {
|
||||
const alertHandler = jest.fn();
|
||||
service.on('alert', alertHandler);
|
||||
|
||||
service.logApiCall({
|
||||
operation: 'sendMessage',
|
||||
userId: 'user1',
|
||||
result: ApiCallResult.SUCCESS,
|
||||
responseTime: 10000, // 超过阈值
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
expect(alertHandler).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('消息转发日志测试', () => {
|
||||
it('应该正确记录上行消息', () => {
|
||||
service.logMessageForward({
|
||||
fromUserId: 'user1',
|
||||
toUserIds: ['user2'],
|
||||
stream: 'test-stream',
|
||||
topic: 'test-topic',
|
||||
direction: 'upstream',
|
||||
success: true,
|
||||
latency: 50,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.messages.upstream).toBe(1);
|
||||
});
|
||||
|
||||
it('应该正确记录下行消息', () => {
|
||||
service.logMessageForward({
|
||||
fromUserId: 'user1',
|
||||
toUserIds: ['user2', 'user3'],
|
||||
stream: 'test-stream',
|
||||
topic: 'test-topic',
|
||||
direction: 'downstream',
|
||||
success: true,
|
||||
latency: 30,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const stats = service.getStats();
|
||||
expect(stats.messages.downstream).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('操作确认测试', () => {
|
||||
it('应该正确记录操作确认', () => {
|
||||
const confirmHandler = jest.fn();
|
||||
service.on('operation_confirmed', confirmHandler);
|
||||
|
||||
service.confirmOperation({
|
||||
operationId: 'op1',
|
||||
operation: 'sendMessage',
|
||||
userId: 'user1',
|
||||
success: true,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
expect(confirmHandler).toHaveBeenCalled();
|
||||
expect(Logger.prototype.log).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('告警测试', () => {
|
||||
it('应该正确发送告警', () => {
|
||||
const alertHandler = jest.fn();
|
||||
service.on('alert', alertHandler);
|
||||
|
||||
service.sendAlert({
|
||||
id: 'alert1',
|
||||
level: AlertLevel.WARNING,
|
||||
title: 'Test Alert',
|
||||
message: 'This is a test alert',
|
||||
component: 'test',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
expect(alertHandler).toHaveBeenCalled();
|
||||
const stats = service.getStats();
|
||||
expect(stats.alerts.byLevel[AlertLevel.WARNING]).toBe(1);
|
||||
});
|
||||
|
||||
it('应该正确获取最近的告警', () => {
|
||||
service.sendAlert({
|
||||
id: 'alert1',
|
||||
level: AlertLevel.INFO,
|
||||
title: 'Alert 1',
|
||||
message: 'Message 1',
|
||||
component: 'test',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
service.sendAlert({
|
||||
id: 'alert2',
|
||||
level: AlertLevel.WARNING,
|
||||
title: 'Alert 2',
|
||||
message: 'Message 2',
|
||||
component: 'test',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const recentAlerts = service.getRecentAlerts(10);
|
||||
expect(recentAlerts.length).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('健康检查测试', () => {
|
||||
it('应该返回健康状态', async () => {
|
||||
const health = await service.checkSystemHealth();
|
||||
|
||||
expect(health).toBeDefined();
|
||||
expect(health.status).toBeDefined();
|
||||
expect(health.components).toBeDefined();
|
||||
expect(health.timestamp).toBeInstanceOf(Date);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* 属性测试: 操作确认和日志记录
|
||||
*
|
||||
* **Feature: zulip-integration, Property 10: 操作确认和日志记录**
|
||||
* **Validates: Requirements 4.5, 8.5, 9.1, 9.2, 9.3**
|
||||
*
|
||||
* 对于任何重要的系统操作(连接管理、API调用、消息转发),
|
||||
* 系统应该记录相应的日志信息,并向客户端返回操作确认
|
||||
*/
|
||||
describe('Property 10: 操作确认和日志记录', () => {
|
||||
beforeEach(() => {
|
||||
service.resetStats();
|
||||
});
|
||||
|
||||
/**
|
||||
* 属性: 对于任何连接事件,系统应该正确记录并更新统计
|
||||
*/
|
||||
it('对于任何连接事件,系统应该正确记录并更新统计', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成Socket ID
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成用户ID(可选)
|
||||
fc.option(fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0)),
|
||||
// 生成事件类型
|
||||
fc.constantFrom(
|
||||
ConnectionEventType.CONNECTED,
|
||||
ConnectionEventType.DISCONNECTED,
|
||||
ConnectionEventType.ERROR,
|
||||
ConnectionEventType.TIMEOUT
|
||||
),
|
||||
async (socketId, userId, eventType) => {
|
||||
const initialStats = service.getStats();
|
||||
const initialTotal = initialStats.connections.total;
|
||||
const initialActive = initialStats.connections.active;
|
||||
const initialErrors = initialStats.connections.errors;
|
||||
|
||||
const log: ConnectionLog = {
|
||||
socketId,
|
||||
userId: userId ?? undefined,
|
||||
eventType,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
service.logConnection(log);
|
||||
|
||||
const newStats = service.getStats();
|
||||
|
||||
// 验证统计更新正确
|
||||
if (eventType === ConnectionEventType.CONNECTED) {
|
||||
expect(newStats.connections.total).toBe(initialTotal + 1);
|
||||
expect(newStats.connections.active).toBe(initialActive + 1);
|
||||
} else if (eventType === ConnectionEventType.DISCONNECTED) {
|
||||
expect(newStats.connections.active).toBe(Math.max(0, initialActive - 1));
|
||||
} else if (eventType === ConnectionEventType.ERROR || eventType === ConnectionEventType.TIMEOUT) {
|
||||
expect(newStats.connections.errors).toBe(initialErrors + 1);
|
||||
}
|
||||
|
||||
// 验证日志被调用
|
||||
expect(Logger.prototype.log).toHaveBeenCalled();
|
||||
}
|
||||
),
|
||||
{ numRuns: 100 }
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
/**
|
||||
* 属性: 对于任何API调用,系统应该正确记录响应时间和结果
|
||||
*/
|
||||
it('对于任何API调用,系统应该正确记录响应时间和结果', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成操作名称
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成用户ID
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成结果
|
||||
fc.constantFrom(
|
||||
ApiCallResult.SUCCESS,
|
||||
ApiCallResult.FAILURE,
|
||||
ApiCallResult.TIMEOUT,
|
||||
ApiCallResult.RATE_LIMITED
|
||||
),
|
||||
// 生成响应时间
|
||||
fc.integer({ min: 1, max: 10000 }),
|
||||
async (operation, userId, result, responseTime) => {
|
||||
const initialStats = service.getStats();
|
||||
const initialTotal = initialStats.apiCalls.total;
|
||||
const initialSuccess = initialStats.apiCalls.success;
|
||||
const initialFailures = initialStats.apiCalls.failures;
|
||||
|
||||
const log: ApiCallLog = {
|
||||
operation,
|
||||
userId,
|
||||
result,
|
||||
responseTime,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
service.logApiCall(log);
|
||||
|
||||
const newStats = service.getStats();
|
||||
|
||||
// 验证总数增加
|
||||
expect(newStats.apiCalls.total).toBe(initialTotal + 1);
|
||||
|
||||
// 验证成功/失败计数
|
||||
if (result === ApiCallResult.SUCCESS) {
|
||||
expect(newStats.apiCalls.success).toBe(initialSuccess + 1);
|
||||
} else {
|
||||
expect(newStats.apiCalls.failures).toBe(initialFailures + 1);
|
||||
}
|
||||
|
||||
// 验证日志被调用
|
||||
expect(Logger.prototype.log).toHaveBeenCalled();
|
||||
}
|
||||
),
|
||||
{ numRuns: 100 }
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
/**
|
||||
* 属性: 对于任何消息转发,系统应该正确记录方向和延迟
|
||||
*/
|
||||
it('对于任何消息转发,系统应该正确记录方向和延迟', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成发送者ID
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成接收者ID列表
|
||||
fc.array(
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
{ minLength: 1, maxLength: 10 }
|
||||
),
|
||||
// 生成Stream名称
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成Topic名称
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成方向
|
||||
fc.constantFrom('upstream' as const, 'downstream' as const),
|
||||
// 生成延迟
|
||||
fc.integer({ min: 1, max: 5000 }),
|
||||
async (fromUserId, toUserIds, stream, topic, direction, latency) => {
|
||||
const initialStats = service.getStats();
|
||||
const initialUpstream = initialStats.messages.upstream;
|
||||
const initialDownstream = initialStats.messages.downstream;
|
||||
|
||||
const log: MessageForwardLog = {
|
||||
fromUserId,
|
||||
toUserIds,
|
||||
stream,
|
||||
topic,
|
||||
direction,
|
||||
success: true,
|
||||
latency,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
service.logMessageForward(log);
|
||||
|
||||
const newStats = service.getStats();
|
||||
|
||||
// 验证方向计数
|
||||
if (direction === 'upstream') {
|
||||
expect(newStats.messages.upstream).toBe(initialUpstream + 1);
|
||||
} else {
|
||||
expect(newStats.messages.downstream).toBe(initialDownstream + 1);
|
||||
}
|
||||
|
||||
// 验证日志被调用
|
||||
expect(Logger.prototype.log).toHaveBeenCalled();
|
||||
}
|
||||
),
|
||||
{ numRuns: 100 }
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
/**
|
||||
* 属性: 对于任何操作确认,系统应该记录并发出事件
|
||||
*/
|
||||
it('对于任何操作确认,系统应该记录并发出事件', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成操作ID
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成操作名称
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成用户ID
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成成功状态
|
||||
fc.boolean(),
|
||||
async (operationId, operation, userId, success) => {
|
||||
const eventHandler = jest.fn();
|
||||
service.on('operation_confirmed', eventHandler);
|
||||
|
||||
const confirmation: OperationConfirmation = {
|
||||
operationId,
|
||||
operation,
|
||||
userId,
|
||||
success,
|
||||
timestamp: new Date(),
|
||||
};
|
||||
|
||||
service.confirmOperation(confirmation);
|
||||
|
||||
// 验证事件被发出
|
||||
expect(eventHandler).toHaveBeenCalledWith(confirmation);
|
||||
|
||||
// 验证日志被调用
|
||||
expect(Logger.prototype.log).toHaveBeenCalled();
|
||||
|
||||
service.removeListener('operation_confirmed', eventHandler);
|
||||
}
|
||||
),
|
||||
{ numRuns: 100 }
|
||||
);
|
||||
}, 30000);
|
||||
});
|
||||
|
||||
/**
|
||||
* 属性测试: 系统监控和告警
|
||||
*
|
||||
* **Feature: zulip-integration, Property 11: 系统监控和告警**
|
||||
* **Validates: Requirements 9.4**
|
||||
*
|
||||
* 对于任何系统资源异常或性能问题,系统应该检测异常情况并发送相应的告警通知
|
||||
*/
|
||||
describe('Property 11: 系统监控和告警', () => {
|
||||
beforeEach(() => {
|
||||
service.resetStats();
|
||||
});
|
||||
|
||||
/**
|
||||
* 属性: 对于任何告警,系统应该正确记录并更新统计
|
||||
*/
|
||||
it('对于任何告警,系统应该正确记录并更新统计', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成告警ID
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
// 生成告警级别
|
||||
fc.constantFrom(
|
||||
AlertLevel.INFO,
|
||||
AlertLevel.WARNING,
|
||||
AlertLevel.ERROR,
|
||||
AlertLevel.CRITICAL
|
||||
),
|
||||
// 生成标题
|
||||
fc.string({ minLength: 1, maxLength: 100 }).filter(s => s.trim().length > 0),
|
||||
// 生成消息
|
||||
fc.string({ minLength: 1, maxLength: 500 }).filter(s => s.trim().length > 0),
|
||||
// 生成组件名称
|
||||
fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
|
||||
async (id, level, title, message, component) => {
|
||||
const initialStats = service.getStats();
|
||||
const initialTotal = initialStats.alerts.total;
|
||||
const initialByLevel = initialStats.alerts.byLevel[level];
|
||||
|
||||
const alertHandler = jest.fn();
|
||||
service.on('alert', alertHandler);
|
||||
|
||||
service.sendAlert({
|
||||
id,
|
||||
level,
|
||||
title,
|
||||
message,
|
||||
component,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
|
||||
const newStats = service.getStats();
|
||||
|
||||
// 验证总数增加
|
||||
expect(newStats.alerts.total).toBe(initialTotal + 1);
|
||||
|
||||
// 验证级别计数增加
|
||||
expect(newStats.alerts.byLevel[level]).toBe(initialByLevel + 1);
|
||||
|
||||
// 验证事件被发出
|
||||
expect(alertHandler).toHaveBeenCalled();
|
||||
|
||||
service.removeListener('alert', alertHandler);
|
||||
}
|
||||
),
|
||||
{ numRuns: 100 }
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
/**
|
||||
* 属性: 健康检查应该返回完整的状态信息
|
||||
*/
|
||||
it('健康检查应该返回完整的状态信息', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成一些连接事件
|
||||
fc.integer({ min: 0, max: 10 }),
|
||||
// 生成一些API调用
|
||||
fc.integer({ min: 0, max: 10 }),
|
||||
async (connectionCount, apiCallCount) => {
|
||||
// 模拟一些活动
|
||||
for (let i = 0; i < connectionCount; i++) {
|
||||
service.logConnection({
|
||||
socketId: `socket-${i}`,
|
||||
eventType: ConnectionEventType.CONNECTED,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
for (let i = 0; i < apiCallCount; i++) {
|
||||
service.logApiCall({
|
||||
operation: `operation-${i}`,
|
||||
userId: `user-${i}`,
|
||||
result: ApiCallResult.SUCCESS,
|
||||
responseTime: 100,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
const health = await service.checkSystemHealth();
|
||||
|
||||
// 验证健康状态结构
|
||||
expect(health).toBeDefined();
|
||||
expect(health.status).toBeDefined();
|
||||
expect(['healthy', 'degraded', 'unhealthy']).toContain(health.status);
|
||||
expect(health.components).toBeDefined();
|
||||
expect(health.components.websocket).toBeDefined();
|
||||
expect(health.components.zulipApi).toBeDefined();
|
||||
expect(health.components.redis).toBeDefined();
|
||||
expect(health.components.memory).toBeDefined();
|
||||
expect(health.timestamp).toBeInstanceOf(Date);
|
||||
}
|
||||
),
|
||||
{ numRuns: 50 }
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
/**
|
||||
* 属性: 最近告警列表应该正确维护
|
||||
*/
|
||||
it('最近告警列表应该正确维护', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成告警数量
|
||||
fc.integer({ min: 1, max: 20 }),
|
||||
// 生成请求数量
|
||||
fc.integer({ min: 1, max: 15 }),
|
||||
async (alertCount, requestLimit) => {
|
||||
// 重置统计以确保干净的状态
|
||||
service.resetStats();
|
||||
|
||||
// 发送多个告警
|
||||
for (let i = 0; i < alertCount; i++) {
|
||||
service.sendAlert({
|
||||
id: `alert-${i}`,
|
||||
level: AlertLevel.INFO,
|
||||
title: `Alert ${i}`,
|
||||
message: `Message ${i}`,
|
||||
component: 'test',
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
const recentAlerts = service.getRecentAlerts(requestLimit);
|
||||
|
||||
// 验证返回数量不超过请求数量
|
||||
expect(recentAlerts.length).toBeLessThanOrEqual(requestLimit);
|
||||
|
||||
// 验证返回数量不超过实际告警数量(在本次测试中发送的)
|
||||
expect(recentAlerts.length).toBeLessThanOrEqual(Math.min(alertCount, requestLimit));
|
||||
|
||||
// 验证每个告警都有必要的字段
|
||||
for (const alert of recentAlerts) {
|
||||
expect(alert.id).toBeDefined();
|
||||
expect(alert.level).toBeDefined();
|
||||
expect(alert.title).toBeDefined();
|
||||
expect(alert.message).toBeDefined();
|
||||
expect(alert.component).toBeDefined();
|
||||
expect(alert.timestamp).toBeInstanceOf(Date);
|
||||
}
|
||||
}
|
||||
),
|
||||
{ numRuns: 50 }
|
||||
);
|
||||
}, 30000);
|
||||
|
||||
/**
|
||||
* 属性: 统计数据应该保持一致性
|
||||
*/
|
||||
it('统计数据应该保持一致性', async () => {
|
||||
await fc.assert(
|
||||
fc.asyncProperty(
|
||||
// 生成成功API调用数
|
||||
fc.integer({ min: 0, max: 50 }),
|
||||
// 生成失败API调用数
|
||||
fc.integer({ min: 0, max: 50 }),
|
||||
async (successCount, failureCount) => {
|
||||
// 重置统计以确保干净的状态
|
||||
service.resetStats();
|
||||
|
||||
// 记录成功的API调用
|
||||
for (let i = 0; i < successCount; i++) {
|
||||
service.logApiCall({
|
||||
operation: 'test',
|
||||
userId: 'user1',
|
||||
result: ApiCallResult.SUCCESS,
|
||||
responseTime: 100,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
// 记录失败的API调用
|
||||
for (let i = 0; i < failureCount; i++) {
|
||||
service.logApiCall({
|
||||
operation: 'test',
|
||||
userId: 'user1',
|
||||
result: ApiCallResult.FAILURE,
|
||||
responseTime: 100,
|
||||
timestamp: new Date(),
|
||||
});
|
||||
}
|
||||
|
||||
const stats = service.getStats();
|
||||
|
||||
// 验证总数等于成功数加失败数
|
||||
expect(stats.apiCalls.total).toBe(successCount + failureCount);
|
||||
expect(stats.apiCalls.success).toBe(successCount);
|
||||
expect(stats.apiCalls.failures).toBe(failureCount);
|
||||
}
|
||||
),
|
||||
{ numRuns: 50 }
|
||||
);
|
||||
}, 30000);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user