Files
whale-town-end/test/zulip_integration/performance/chat_performance.spec.ts
moyin d04ab7f75f CRITICAL ISSUES: Database management service with major problems
WARNING: This commit contains code with significant issues that need immediate attention:

1. Type Safety Issues:
   - Unused import ZulipAccountsService causing compilation warnings
   - Implicit 'any' type in formatZulipAccount method parameter
   - Type inconsistencies in service injections

2. Service Integration Problems:
   - Inconsistent service interface usage
   - Missing proper type definitions for injected services
   - Potential runtime errors due to type mismatches

3. Code Quality Issues:
   - Violation of TypeScript strict mode requirements
   - Inconsistent error handling patterns
   - Missing proper interface implementations

 Files affected:
   - src/business/admin/database_management.service.ts (main issue)
   - Multiple test files and service implementations
   - Configuration and documentation updates

 Next steps required:
   1. Fix TypeScript compilation errors
   2. Implement proper type safety
   3. Resolve service injection inconsistencies
   4. Add comprehensive error handling
   5. Update tests to match new implementations

 Impact: High - affects admin functionality and system stability
 Priority: Urgent - requires immediate review and fixes

Author: moyin
Date: 2026-01-10
2026-01-10 19:27:28 +08:00

358 lines
11 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.
/**
* Zulip聊天性能测试
*
* 功能描述:
* - 测试优化后聊天架构的性能表现
* - 验证游戏内实时广播 + Zulip异步同步的效果
* - 测试高并发场景下的系统稳定性
*
* 测试场景:
* - 单用户消息发送性能
* - 多用户并发聊天性能
* - 大量消息批量处理性能
* - 内存使用和资源清理
*
* @author moyin
* @version 1.0.0
* @since 2026-01-10
*/
import { Test, TestingModule } from '@nestjs/testing';
import { ZulipService } from '../../../src/business/zulip/zulip.service';
import { ZulipClientPoolService } from '../../../src/core/zulip_core/services/zulip_client_pool.service';
import { SessionManagerService } from '../../../src/business/zulip/services/session_manager.service';
import { MessageFilterService } from '../../../src/business/zulip/services/message_filter.service';
// 模拟WebSocket网关
class MockWebSocketGateway {
private sentMessages: Array<{ socketId: string; data: any }> = [];
private broadcastMessages: Array<{ mapId: string; data: any }> = [];
sendToPlayer(socketId: string, data: any): void {
this.sentMessages.push({ socketId, data });
}
broadcastToMap(mapId: string, data: any, excludeId?: string): void {
this.broadcastMessages.push({ mapId, data });
}
getSentMessages() { return this.sentMessages; }
getBroadcastMessages() { return this.broadcastMessages; }
clearMessages() {
this.sentMessages = [];
this.broadcastMessages = [];
}
}
describe('Zulip聊天性能测试', () => {
let zulipService: ZulipService;
let sessionManager: SessionManagerService;
let mockWebSocketGateway: MockWebSocketGateway;
let mockZulipClientPool: any;
beforeAll(async () => {
// 创建模拟服务
mockZulipClientPool = {
sendMessage: jest.fn().mockResolvedValue({
success: true,
messageId: 'zulip-msg-123',
}),
createUserClient: jest.fn(),
destroyUserClient: jest.fn(),
};
const mockSessionManager = {
getSession: jest.fn().mockResolvedValue({
sessionId: 'test-session',
userId: 'user-123',
username: 'TestPlayer',
currentMap: 'whale_port',
position: { x: 100, y: 200 },
}),
injectContext: jest.fn().mockResolvedValue({
stream: 'Whale Port',
topic: 'Town Square Chat',
}),
getSocketsInMap: jest.fn().mockResolvedValue(['socket-1', 'socket-2', 'socket-3']),
createSession: jest.fn(),
destroySession: jest.fn(),
updatePlayerPosition: jest.fn().mockResolvedValue(true),
};
const mockMessageFilter = {
validateMessage: jest.fn().mockResolvedValue({
allowed: true,
filteredContent: null,
}),
};
const module: TestingModule = await Test.createTestingModule({
providers: [
ZulipService,
{
provide: 'ZULIP_CLIENT_POOL_SERVICE',
useValue: mockZulipClientPool,
},
{
provide: SessionManagerService,
useValue: mockSessionManager,
},
{
provide: MessageFilterService,
useValue: mockMessageFilter,
},
{
provide: 'API_KEY_SECURITY_SERVICE',
useValue: {
getApiKey: jest.fn().mockResolvedValue({
success: true,
apiKey: 'test-api-key',
}),
},
},
{
provide: 'LoginCoreService',
useValue: {
verifyToken: jest.fn().mockResolvedValue({
sub: 'user-123',
username: 'TestPlayer',
email: 'test@example.com',
}),
},
},
],
}).compile();
zulipService = module.get<ZulipService>(ZulipService);
sessionManager = module.get<SessionManagerService>(SessionManagerService);
// 设置WebSocket网关
mockWebSocketGateway = new MockWebSocketGateway();
zulipService.setWebSocketGateway(mockWebSocketGateway as any);
});
beforeEach(() => {
jest.clearAllMocks();
mockWebSocketGateway.clearMessages();
});
describe('单用户消息发送性能', () => {
it('应该在50ms内完成游戏内广播', async () => {
const startTime = Date.now();
const result = await zulipService.sendChatMessage({
socketId: 'test-socket',
content: 'Performance test message',
scope: 'local',
});
const duration = Date.now() - startTime;
expect(result.success).toBe(true);
expect(duration).toBeLessThan(50); // 游戏内广播应该在50ms内完成
console.log(`游戏内广播耗时: ${duration}ms`);
});
it('应该异步处理Zulip同步不阻塞游戏聊天', async () => {
// 模拟Zulip同步延迟
mockZulipClientPool.sendMessage.mockImplementation(() =>
new Promise(resolve => setTimeout(() => resolve({
success: true,
messageId: 'delayed-msg',
}), 200))
);
const startTime = Date.now();
const result = await zulipService.sendChatMessage({
socketId: 'test-socket',
content: 'Async test message',
scope: 'local',
});
const duration = Date.now() - startTime;
expect(result.success).toBe(true);
expect(duration).toBeLessThan(100); // 不应该等待Zulip同步完成
console.log(`异步处理耗时: ${duration}ms`);
});
});
describe('多用户并发聊天性能', () => {
it('应该处理50个并发消息', async () => {
const messageCount = 50;
const startTime = Date.now();
const promises = Array.from({ length: messageCount }, (_, i) =>
zulipService.sendChatMessage({
socketId: `socket-${i}`,
content: `Concurrent message ${i}`,
scope: 'local',
})
);
const results = await Promise.all(promises);
const duration = Date.now() - startTime;
// 验证所有消息都成功处理
expect(results).toHaveLength(messageCount);
results.forEach(result => {
expect(result.success).toBe(true);
});
const avgTimePerMessage = duration / messageCount;
console.log(`处理${messageCount}条并发消息耗时: ${duration}ms, 平均每条: ${avgTimePerMessage.toFixed(2)}ms`);
// 期望平均每条消息处理时间不超过20ms
expect(avgTimePerMessage).toBeLessThan(20);
}, 10000);
it('应该正确广播给地图内的所有玩家', async () => {
await zulipService.sendChatMessage({
socketId: 'sender-socket',
content: 'Broadcast test message',
scope: 'local',
});
// 验证广播消息
const broadcastMessages = mockWebSocketGateway.getBroadcastMessages();
expect(broadcastMessages).toHaveLength(1);
const broadcastMessage = broadcastMessages[0];
expect(broadcastMessage.mapId).toBe('whale_port');
expect(broadcastMessage.data.t).toBe('chat_render');
expect(broadcastMessage.data.txt).toBe('Broadcast test message');
});
});
describe('批量消息处理性能', () => {
it('应该高效处理大量消息', async () => {
const batchSize = 100;
const startTime = Date.now();
// 创建批量消息
const batchPromises = Array.from({ length: batchSize }, (_, i) =>
zulipService.sendChatMessage({
socketId: 'batch-socket',
content: `Batch message ${i}`,
scope: 'local',
})
);
const results = await Promise.all(batchPromises);
const duration = Date.now() - startTime;
// 验证处理结果
expect(results).toHaveLength(batchSize);
results.forEach((result, index) => {
expect(result.success).toBe(true);
expect(result.messageId).toBeDefined();
});
const throughput = (batchSize / duration) * 1000; // 每秒处理的消息数
console.log(`批量处理${batchSize}条消息耗时: ${duration}ms, 吞吐量: ${throughput.toFixed(2)} msg/s`);
// 期望吞吐量至少达到500 msg/s
expect(throughput).toBeGreaterThan(500);
}, 15000);
});
describe('内存使用和资源清理', () => {
it('应该正确清理会话资源', async () => {
// 创建多个会话
const sessionCount = 10;
const sessionIds = Array.from({ length: sessionCount }, (_, i) => `session-${i}`);
// 模拟会话创建
for (const sessionId of sessionIds) {
await zulipService.handlePlayerLogin({
socketId: sessionId,
token: 'valid-jwt-token',
});
}
// 清理所有会话
for (const sessionId of sessionIds) {
await zulipService.handlePlayerLogout(sessionId);
}
// 验证资源清理
expect(mockZulipClientPool.destroyUserClient).toHaveBeenCalledTimes(sessionCount);
});
it('应该处理内存压力测试', async () => {
const initialMemory = process.memoryUsage();
// 创建大量临时对象
const largeDataSet = Array.from({ length: 1000 }, (_, i) => ({
id: i,
data: 'x'.repeat(1000), // 1KB per object
timestamp: new Date(),
}));
// 处理大量消息
const promises = largeDataSet.map((item, i) =>
zulipService.sendChatMessage({
socketId: `memory-test-${i}`,
content: `Memory test ${item.id}: ${item.data.substring(0, 50)}...`,
scope: 'local',
})
);
await Promise.all(promises);
// 强制垃圾回收(如果可用)
if (global.gc) {
global.gc();
}
const finalMemory = process.memoryUsage();
const memoryIncrease = finalMemory.heapUsed - initialMemory.heapUsed;
console.log(`内存使用增加: ${(memoryIncrease / 1024 / 1024).toFixed(2)} MB`);
// 期望内存增加不超过50MB
expect(memoryIncrease).toBeLessThan(50 * 1024 * 1024);
}, 20000);
});
describe('错误处理性能', () => {
it('应该快速处理无效会话', async () => {
const startTime = Date.now();
const result = await zulipService.sendChatMessage({
socketId: 'invalid-socket',
content: 'This should fail quickly',
scope: 'local',
});
const duration = Date.now() - startTime;
expect(result.success).toBe(false);
expect(result.error).toContain('会话不存在');
expect(duration).toBeLessThan(10); // 错误处理应该很快
console.log(`错误处理耗时: ${duration}ms`);
});
it('应该处理Zulip服务异常而不影响游戏聊天', async () => {
// 模拟Zulip服务异常
mockZulipClientPool.sendMessage.mockRejectedValue(new Error('Zulip service unavailable'));
const result = await zulipService.sendChatMessage({
socketId: 'test-socket',
content: 'Message during Zulip outage',
scope: 'local',
});
// 游戏内聊天应该仍然成功
expect(result.success).toBe(true);
// 验证游戏内广播仍然工作
const broadcastMessages = mockWebSocketGateway.getBroadcastMessages();
expect(broadcastMessages).toHaveLength(1);
});
});
});