Files
whale-town-end/test/utils/websocket-client.ts
moyin cbf4120ddd refactor: 更新WebSocket相关测试和location_broadcast模块
- 更新location_broadcast网关以支持原生WebSocket
- 修改WebSocket认证守卫和中间件
- 更新相关的测试文件和规范
- 添加WebSocket测试工具
- 完善Zulip服务的测试覆盖

技术改进:
- 统一WebSocket实现架构
- 优化性能监控和限流中间件
- 更新测试用例以适配新的WebSocket实现
2026-01-09 17:02:43 +08:00

118 lines
2.9 KiB
TypeScript

/**
* 原生 WebSocket 客户端测试工具
*
* 用于替代 Socket.IO 客户端进行测试
*/
import WebSocket from 'ws';
export interface WebSocketTestClient {
connect(): Promise<void>;
disconnect(): void;
send(event: string, data: any): void;
on(event: string, callback: (data: any) => void): void;
off(event: string, callback?: (data: any) => void): void;
waitForEvent(event: string, timeout?: number): Promise<any>;
isConnected(): boolean;
}
export class WebSocketTestClientImpl implements WebSocketTestClient {
private ws: WebSocket | null = null;
private eventHandlers = new Map<string, Set<(data: any) => void>>();
private connected = false;
constructor(private url: string) {}
async connect(): Promise<void> {
return new Promise((resolve, reject) => {
this.ws = new WebSocket(this.url);
this.ws.on('open', () => {
this.connected = true;
resolve();
});
this.ws.on('error', (error) => {
reject(error);
});
this.ws.on('message', (data) => {
try {
const message = JSON.parse(data.toString());
const { event, data: eventData } = message;
const handlers = this.eventHandlers.get(event);
if (handlers) {
handlers.forEach(handler => handler(eventData));
}
} catch (error) {
console.error('Failed to parse WebSocket message:', error);
}
});
this.ws.on('close', () => {
this.connected = false;
});
});
}
disconnect(): void {
if (this.ws) {
this.ws.close();
this.ws = null;
this.connected = false;
}
}
send(event: string, data: any): void {
if (this.ws && this.connected) {
const message = JSON.stringify({ event, data });
this.ws.send(message);
} else {
throw new Error('WebSocket is not connected');
}
}
on(event: string, callback: (data: any) => void): void {
if (!this.eventHandlers.has(event)) {
this.eventHandlers.set(event, new Set());
}
this.eventHandlers.get(event)!.add(callback);
}
off(event: string, callback?: (data: any) => void): void {
const handlers = this.eventHandlers.get(event);
if (handlers) {
if (callback) {
handlers.delete(callback);
} else {
handlers.clear();
}
}
}
async waitForEvent(event: string, timeout: number = 5000): Promise<any> {
return new Promise((resolve, reject) => {
const timer = setTimeout(() => {
this.off(event, handler);
reject(new Error(`Timeout waiting for event: ${event}`));
}, timeout);
const handler = (data: any) => {
clearTimeout(timer);
this.off(event, handler);
resolve(data);
};
this.on(event, handler);
});
}
isConnected(): boolean {
return this.connected;
}
}
export function createWebSocketTestClient(url: string): WebSocketTestClient {
return new WebSocketTestClientImpl(url);
}