/** * ClientTransport 基类测试 * 测试客户端传输层基类的构造函数和依赖问题 */ import { ClientTransport, ClientTransportConfig, ConnectionState } from '../../src/transport/ClientTransport'; // Mock Emitter 和 Core jest.mock('@esengine/ecs-framework', () => ({ Emitter: jest.fn().mockImplementation(() => ({ emit: jest.fn(), on: jest.fn(), off: jest.fn(), removeAllListeners: jest.fn() })), Core: { schedule: { scheduleRepeating: jest.fn((callback: Function, interval: number) => ({ stop: jest.fn() })) } } })); // Mock network-shared jest.mock('@esengine/ecs-framework-network-shared', () => ({ NetworkValue: {} })); // 创建测试用的具体实现类 class TestClientTransport extends ClientTransport { async connect(): Promise { return Promise.resolve(); } async disconnect(): Promise { return Promise.resolve(); } async sendMessage(message: any): Promise { return Promise.resolve(); } } describe('ClientTransport', () => { let transport: TestClientTransport; const defaultConfig: ClientTransportConfig = { host: 'localhost', port: 8080 }; beforeEach(() => { jest.clearAllMocks(); }); afterEach(() => { if (transport) { transport = null as any; } }); describe('构造函数测试', () => { it('应该能够成功创建ClientTransport实例', () => { expect(() => { transport = new TestClientTransport(defaultConfig); }).not.toThrow(); expect(transport).toBeInstanceOf(ClientTransport); }); it('应该正确设置默认配置', () => { transport = new TestClientTransport(defaultConfig); const config = (transport as any).config; expect(config.host).toBe('localhost'); expect(config.port).toBe(8080); expect(config.secure).toBe(false); expect(config.connectionTimeout).toBe(10000); expect(config.reconnectInterval).toBe(3000); expect(config.maxReconnectAttempts).toBe(10); expect(config.heartbeatInterval).toBe(30000); expect(config.maxQueueSize).toBe(1000); }); it('应该允许自定义配置覆盖默认值', () => { const customConfig: ClientTransportConfig = { host: 'example.com', port: 9090, secure: true, connectionTimeout: 15000, reconnectInterval: 5000, maxReconnectAttempts: 5, heartbeatInterval: 60000, maxQueueSize: 500 }; transport = new TestClientTransport(customConfig); const config = (transport as any).config; expect(config.host).toBe('example.com'); expect(config.port).toBe(9090); expect(config.secure).toBe(true); expect(config.connectionTimeout).toBe(15000); expect(config.reconnectInterval).toBe(5000); expect(config.maxReconnectAttempts).toBe(5); expect(config.heartbeatInterval).toBe(60000); expect(config.maxQueueSize).toBe(500); }); it('应该正确初始化内部状态', () => { transport = new TestClientTransport(defaultConfig); expect((transport as any).state).toBe(ConnectionState.DISCONNECTED); expect((transport as any).messageQueue).toEqual([]); expect((transport as any).reconnectAttempts).toBe(0); expect((transport as any).reconnectTimer).toBeNull(); expect((transport as any).heartbeatTimer).toBeNull(); expect((transport as any).latencyMeasurements).toEqual([]); }); it('应该正确初始化统计信息', () => { transport = new TestClientTransport(defaultConfig); const stats = transport.getStats(); expect(stats.connectedAt).toBeNull(); expect(stats.connectionDuration).toBe(0); expect(stats.messagesSent).toBe(0); expect(stats.messagesReceived).toBe(0); expect(stats.bytesSent).toBe(0); expect(stats.bytesReceived).toBe(0); expect(stats.averageLatency).toBe(0); expect(stats.averageLatency).toBe(0); expect(stats.reconnectCount).toBe(0); }); }); describe('依赖注入测试', () => { it('应该正确处理@esengine/ecs-framework中的Emitter', () => { const { Emitter } = require('@esengine/ecs-framework'); expect(() => { transport = new TestClientTransport(defaultConfig); }).not.toThrow(); expect(Emitter).toHaveBeenCalled(); }); it('构造函数中Emitter初始化失败应该抛出异常', () => { // Mock Emitter构造函数抛出异常 const { Emitter } = require('@esengine/ecs-framework'); Emitter.mockImplementation(() => { throw new Error('Emitter初始化失败'); }); expect(() => { transport = new TestClientTransport(defaultConfig); }).toThrow('Emitter初始化失败'); }); it('应该正确处理@esengine/ecs-framework-network-shared依赖', () => { const networkShared = require('@esengine/ecs-framework-network-shared'); expect(() => { transport = new TestClientTransport(defaultConfig); }).not.toThrow(); expect(networkShared).toBeDefined(); expect(networkShared.NetworkValue).toBeDefined(); }); }); describe('事件系统测试', () => { beforeEach(() => { transport = new TestClientTransport(defaultConfig); }); it('应该能够注册事件监听器', () => { const mockCallback = jest.fn(); const { Emitter } = require('@esengine/ecs-framework'); const emitterInstance = Emitter.mock.results[0].value; transport.on('connected', mockCallback); expect(emitterInstance.on).toHaveBeenCalledWith('connected', mockCallback); }); it('应该能够移除事件监听器', () => { const mockCallback = jest.fn(); const { Emitter } = require('@esengine/ecs-framework'); const emitterInstance = Emitter.mock.results[0].value; transport.off('connected', mockCallback); expect(emitterInstance.off).toHaveBeenCalledWith('connected', mockCallback); }); it('应该能够发出事件', () => { const { Emitter } = require('@esengine/ecs-framework'); const emitterInstance = Emitter.mock.results[0].value; (transport as any).emit('connected'); expect(emitterInstance.emit).toHaveBeenCalledWith('connected'); }); }); describe('消息队列测试', () => { beforeEach(() => { transport = new TestClientTransport(defaultConfig); }); it('应该能够将消息加入队列', async () => { const message = { type: 'custom' as const, data: { test: 'data' }, reliable: true, timestamp: Date.now() }; await transport.sendMessage(message); const messageQueue = (transport as any).messageQueue; expect(messageQueue).toHaveLength(1); expect(messageQueue[0]).toEqual(message); }); it('消息队列达到最大大小时应该移除旧消息', async () => { // 设置较小的队列大小 const smallQueueConfig = { ...defaultConfig, maxQueueSize: 2 }; transport = new TestClientTransport(smallQueueConfig); const message1 = { type: 'custom' as const, data: { id: 1 }, reliable: true, timestamp: Date.now() }; const message2 = { type: 'custom' as const, data: { id: 2 }, reliable: true, timestamp: Date.now() }; const message3 = { type: 'custom' as const, data: { id: 3 }, reliable: true, timestamp: Date.now() }; await transport.sendMessage(message1); await transport.sendMessage(message2); await transport.sendMessage(message3); const messageQueue = (transport as any).messageQueue; expect(messageQueue).toHaveLength(2); expect(messageQueue[0]).toEqual(message2); expect(messageQueue[1]).toEqual(message3); }); }); describe('连接状态测试', () => { beforeEach(() => { transport = new TestClientTransport(defaultConfig); }); it('应该正确获取连接状态', () => { expect(transport.getState()).toBe(ConnectionState.DISCONNECTED); }); it('应该正确检查连接状态', () => { expect(transport.isConnected()).toBe(false); (transport as any).state = ConnectionState.CONNECTED; expect(transport.isConnected()).toBe(true); (transport as any).state = ConnectionState.AUTHENTICATED; expect(transport.isConnected()).toBe(true); }); it('状态变化时应该发出事件', () => { const { Emitter } = require('@esengine/ecs-framework'); const emitterInstance = Emitter.mock.results[0].value; (transport as any).setState(ConnectionState.CONNECTING); expect(emitterInstance.emit).toHaveBeenCalledWith( 'state-changed', ConnectionState.DISCONNECTED, ConnectionState.CONNECTING ); }); }); describe('延迟测量测试', () => { beforeEach(() => { transport = new TestClientTransport(defaultConfig); }); it('应该能够更新延迟测量', () => { (transport as any).updateLatency(100); (transport as any).updateLatency(200); (transport as any).updateLatency(150); const stats = transport.getStats(); expect(stats.averageLatency).toBe(150); }); it('应该限制延迟测量样本数量', () => { // 添加超过最大样本数的测量 for (let i = 0; i < 150; i++) { (transport as any).updateLatency(i * 10); } const latencyMeasurements = (transport as any).latencyMeasurements; expect(latencyMeasurements.length).toBeLessThanOrEqual(100); }); }); describe('配置验证测试', () => { it('应该拒绝无效的主机名', () => { expect(() => { transport = new TestClientTransport({ host: '', port: 8080 }); }).toThrow(); }); it('应该拒绝无效的端口号', () => { expect(() => { transport = new TestClientTransport({ host: 'localhost', port: 0 }); }).toThrow(); expect(() => { transport = new TestClientTransport({ host: 'localhost', port: 65536 }); }).toThrow(); }); it('应该拒绝负数的超时配置', () => { expect(() => { transport = new TestClientTransport({ host: 'localhost', port: 8080, connectionTimeout: -1000 }); }).toThrow(); }); }); describe('资源清理测试', () => { beforeEach(() => { transport = new TestClientTransport(defaultConfig); }); it('应该能够清理所有定时器', () => { const { Core } = require('@esengine/ecs-framework'); const mockTimer = { stop: jest.fn() }; Core.schedule.scheduleRepeating.mockReturnValue(mockTimer); // 设置一些定时器 (transport as any).reconnectTimer = mockTimer; (transport as any).heartbeatTimer = mockTimer; // 调用清理方法 (transport as any).cleanup(); expect(mockTimer.stop).toHaveBeenCalledTimes(2); expect((transport as any).reconnectTimer).toBeNull(); expect((transport as any).heartbeatTimer).toBeNull(); }); it('应该能够清理消息队列', () => { (transport as any).messageQueue = [ { type: 'custom', data: {}, reliable: true, timestamp: Date.now() } ]; (transport as any).cleanup(); expect((transport as any).messageQueue).toHaveLength(0); }); it('应该能够移除所有事件监听器', () => { const { Emitter } = require('@esengine/ecs-framework'); const emitterInstance = Emitter.mock.results[0].value; (transport as any).cleanup(); expect(emitterInstance.removeAllListeners).toHaveBeenCalled(); }); }); });