import { describe, it, expect, vi } from 'vitest'; import { Router } from './router.js'; import type { GatewayRequest, OutboundMessage } from './protocol.js'; import { makeResponse, ErrorCode } from './protocol.js'; describe('Router', () => { it('dispatches to registered handler', async () => { const router = new Router(); const handler = vi.fn(async (req: GatewayRequest) => makeResponse(req.id, { ok: true })); router.register('test.method', handler); const request: GatewayRequest = { id: 1, method: 'test.method', params: {} }; const send = vi.fn(); const result = await router.dispatch(request, send); expect(handler).toHaveBeenCalledWith(request, send); expect(result).toEqual({ id: 1, result: { ok: true } }); }); it('returns MethodNotFound for unregistered method', async () => { const router = new Router(); const request: GatewayRequest = { id: 1, method: 'unknown.method' }; const send = vi.fn(); const result = await router.dispatch(request, send); expect(result).toEqual({ id: 1, error: { code: ErrorCode.MethodNotFound, message: 'Unknown method: unknown.method' }, }); }); it('lists registered methods', () => { const router = new Router(); router.register('a.method', async () => {}); router.register('b.method', async () => {}); expect(router.listMethods()).toEqual(['a.method', 'b.method']); }); it('handler can send streaming events via send function', async () => { const router = new Router(); router.register('stream.test', async (req, send) => { send({ id: req.id, event: 'content', data: { text: 'chunk1' } }); send({ id: req.id, event: 'content', data: { text: 'chunk2' } }); send({ id: req.id, event: 'done', data: { content: 'chunk1chunk2' } }); }); const request: GatewayRequest = { id: 1, method: 'stream.test' }; const sent: OutboundMessage[] = []; const send = vi.fn((msg: OutboundMessage) => sent.push(msg)); await router.dispatch(request, send); expect(sent).toHaveLength(3); expect(sent[0]).toEqual({ id: 1, event: 'content', data: { text: 'chunk1' } }); expect(sent[2]).toEqual({ id: 1, event: 'done', data: { content: 'chunk1chunk2' } }); }); });