import { afterEach, describe, expect, it } from 'vitest'; import { WebSocket as WsClient } from 'ws'; import { createTransport } from '../../src/background/transport/index.js'; import type { WebSocketCtor } from '../../src/background/transport/websocket.js'; import type { VeloxTransport } from '../../src/background/transport/types.js'; import { FakeDaemon, FakeNativePort, nextPort, tick } from './helpers.js'; const CTOR = WsClient as unknown as WebSocketCtor; let daemon: FakeDaemon | undefined; let transport: VeloxTransport | undefined; afterEach(async () => { transport?.disconnect(); transport = undefined; await daemon?.stop(); daemon = undefined; }); describe('createTransport (runtime picker)', () => { it('override "ws" uses the WebSocket transport and never touches native messaging', async () => { const port = nextPort(); daemon = await FakeDaemon.start({ port, acceptToken: null }); let nativeTried = false; transport = await createTransport({ override: 'ws', webSocketCtor: CTOR, portRange: { start: port, end: port }, connectNative: () => { nativeTried = true; return new FakeNativePort(); }, backoff: { baseMs: 20, jitter: 0 }, }); expect(transport.kind).toBe('ws'); expect(transport.state).toBe('connected'); expect(nativeTried).toBe(false); }); it('"auto" prefers native messaging when its handshake succeeds', async () => { transport = await createTransport({ override: 'auto', connectNative: () => new FakeNativePort(), webSocketCtor: CTOR, portRange: { start: nextPort(), end: nextPort() }, }); expect(transport.kind).toBe('uds'); expect(transport.state).toBe('connected'); }); it('"auto" falls back to WebSocket when the native host is not installed', async () => { const port = nextPort(); daemon = await FakeDaemon.start({ port, acceptToken: null }); transport = await createTransport({ override: 'auto', webSocketCtor: CTOR, portRange: { start: port, end: port }, connectNative: () => { const fp = new FakeNativePort({ autoHello: false }); queueMicrotask(() => fp.fireDisconnect('No such native application com.velox.host')); return fp; }, backoff: { baseMs: 20, jitter: 0 }, }); expect(transport.kind).toBe('ws'); expect(transport.state).toBe('connected'); }); it('reads the override from storage when none is passed', async () => { await browser.storage.local.set({ 'velox.transportOverride': 'ws' }); const port = nextPort(); daemon = await FakeDaemon.start({ port, acceptToken: null }); let nativeTried = false; transport = await createTransport({ webSocketCtor: CTOR, portRange: { start: port, end: port }, connectNative: () => { nativeTried = true; return new FakeNativePort(); }, backoff: { baseMs: 20, jitter: 0 }, }); expect(transport.kind).toBe('ws'); expect(nativeTried).toBe(false); }); it('override "uds" surfaces a native-host failure to the caller', async () => { await expect( createTransport({ override: 'uds', connectNative: () => { const fp = new FakeNativePort({ autoHello: false }); queueMicrotask(() => fp.fireDisconnect('No such native application com.velox.host')); return fp; }, backoff: { baseMs: 10 }, }), ).rejects.toThrow(/not installed/); await tick(30); }); });