1import { describe, expect, it } from 'vitest'; 2import { MAX_FRAME_BYTES, MAX_LOG_CHARS, MAX_LOG_LINES, parseReply, pickTab } from './tab-hub-core'; 3 4describe("a tab's reply", () => { 5 it('is rebuilt from the fields the MCP reads, each bounded', () => { 6 const logs = Array.from({ length: MAX_LOG_LINES + 10 }, (_, i) => ({ at: i, message: i < 20 ? 'x'.repeat(MAX_LOG_CHARS + 5) : 'y', kind: 'warning', count: 2, extra: 1 })); 7 const parsed = parseReply( 8 JSON.stringify({ type: 'reply', id: '1-0', ok: true, logs, status: { playing: true, cycle: 2.5, code: 'ai', evil: 1 }, html: '<b>' }), 9 )!; 10 expect(parsed.id).toBe('1-0'); 11 expect(parsed.reply.ok).toBe(true); 12 expect(parsed.reply.logs).toHaveLength(MAX_LOG_LINES); 13 expect(parsed.reply.logs![0]).toEqual({ at: 10, message: 'x'.repeat(MAX_LOG_CHARS), kind: 'warning', count: 2 }); 14 expect(parsed.reply.status).toEqual({ playing: true, cycle: 2.5, code: 'ai' }); 15 expect(parsed.reply).not.toHaveProperty('html'); 16 }); 17 18 it('drops what is not a reply, or too large', () => { 19 expect(parseReply('nope')).toBeNull(); 20 expect(parseReply(JSON.stringify({ type: 'command', id: '1' }))).toBeNull(); 21 expect(parseReply(JSON.stringify({ type: 'reply', id: 5 }))).toBeNull(); 22 expect(parseReply(JSON.stringify({ type: 'reply', id: '1', code: 'x'.repeat(MAX_FRAME_BYTES) }))).toBeNull(); 23 expect(parseReply(new ArrayBuffer(8))).toBeNull(); 24 expect(parseReply(JSON.stringify({ type: 'reply', id: '1', status: { code: 'root' } }))!.reply.status!.code).toBe('none'); 25 }); 26}); 27 28describe('which tab a command goes to', () => { 29 it('is the one named, or the only one open', () => { 30 expect(pickTab(['abc1'], undefined)).toBe('abc1'); 31 expect(pickTab(['abc1', 'def2'], 'def2')).toBe('def2'); 32 expect(() => pickTab([], undefined)).toThrow(/no jevstrudel tab open/); 33 expect(() => pickTab(['abc1', 'def2'], undefined)).toThrow(/pass session_id/); 34 expect(() => pickTab(['abc1'], 'zzz9')).toThrow(/no open tab zzz9/); 35 expect(() => pickTab(['abc1'], '../x')).toThrow(/session_id/); 36 }); 37});