import { CONNECTION_TRANSPORT_STATE_CLOSED, CONNECTION_TRANSPORT_STATE_CLOSING, CONNECTION_TRANSPORT_STATE_IDLE, CONNECTION_TRANSPORT_STATE_OPEN, CONNECTION_TRANSPORT_STATE_OPENING, MOCK_TRANSPORT_DEFAULT_CLOSE_CLEAN, MOCK_TRANSPORT_DEFAULT_CLOSE_CODE, TRANSPORT_KIND_MOCK } from '../consts.ts'; import { createTransportEmitter } from '../transport.ts'; import type { ConnectionCloseEvent, ConnectionTransportState, MockConnectionTransport, MockTransportOptions } from '../types.ts'; export function createMockTransport(options: MockTransportOptions = {}): MockConnectionTransport { const autoOpen = options.autoOpen ?? true; const openEmitter = createTransportEmitter<() => void>(); const messageEmitter = createTransportEmitter<(message: string | ArrayBuffer) => void>(); const closeEmitter = createTransportEmitter<(event: ConnectionCloseEvent) => void>(); const errorEmitter = createTransportEmitter<(error: unknown) => void>(); const sent: (string | ArrayBuffer)[] = []; let state: ConnectionTransportState = CONNECTION_TRANSPORT_STATE_IDLE; const openResolvers = new Set<() => void>(); function resolveOpenWaiters(): void { for (const resolve of openResolvers) resolve(); openResolvers.clear(); } function emitOpen(): void { state = CONNECTION_TRANSPORT_STATE_OPEN; resolveOpenWaiters(); openEmitter.emit(); } function emitClose(event: Partial = {}): void { state = CONNECTION_TRANSPORT_STATE_CLOSED; resolveOpenWaiters(); closeEmitter.emit({ code: event.code ?? MOCK_TRANSPORT_DEFAULT_CLOSE_CODE, reason: event.reason, clean: event.clean ?? MOCK_TRANSPORT_DEFAULT_CLOSE_CLEAN }); } return { kind: TRANSPORT_KIND_MOCK, get state() { return state; }, get bufferedAmount() { return sent.length; }, get canSend() { return state === CONNECTION_TRANSPORT_STATE_OPEN; }, open() { if (state === CONNECTION_TRANSPORT_STATE_OPEN) return Promise.resolve(); state = CONNECTION_TRANSPORT_STATE_OPENING; if (autoOpen) { emitOpen(); return Promise.resolve(); } return new Promise((resolve) => { openResolvers.add(resolve); }); }, send(data) { sent.push(data); }, close(code, reason) { if (state === CONNECTION_TRANSPORT_STATE_CLOSED) return; state = CONNECTION_TRANSPORT_STATE_CLOSING; emitClose({ code, reason }); }, onOpen: (listener) => openEmitter.on(listener), onMessage: (listener) => messageEmitter.on(listener), onClose: (listener) => closeEmitter.on(listener), onError: (listener) => errorEmitter.on(listener), emitOpen, emitMessage(message) { messageEmitter.emit(message); }, emitClose, emitError(error) { errorEmitter.emit(error); }, sentMessages() { return [...sent]; }, clearSent() { sent.length = 0; } }; }