import { describe, expect, it } from 'bun:test'; import { CHUNK_HEADER_LEN, bytesEqual, decodeFrame, encodeChunkAckFrame, encodeChunkFrame, encodeErrorFrame, encodePingFrame, encodePongFrame, encodeResumeQueryFrame, encodeResumeStateFrame, randomRequestId, REQUEST_ID_LEN, STREAM_ID_LEN, streamIdBytesToString, streamIdStringToBytes, WIRE_CHUNK, WIRE_CHUNK_ACK, WIRE_ERROR, WIRE_PING, WIRE_PONG, WIRE_RESUME_QUERY, WIRE_RESUME_STATE, } from '../src/wire.js'; describe('wire format', () => { it('roundtrips a chunk frame', () => { const requestId = randomRequestId(); const streamId = new Uint8Array(STREAM_ID_LEN).fill(0xab); const envelope = new Uint8Array(1024); for (let i = 0; i < envelope.length; i++) envelope[i] = i & 0xff; const buf = encodeChunkFrame({ type: WIRE_CHUNK, requestId, streamId, laneId: 7, seq: 12345n, envelope, }); expect(buf.length).toBe(CHUNK_HEADER_LEN + envelope.length); const decoded = decodeFrame(buf); expect(decoded.type).toBe(WIRE_CHUNK); if (decoded.type !== WIRE_CHUNK) throw new Error('type narrow failed'); expect(bytesEqual(decoded.requestId, requestId)).toBe(true); expect(bytesEqual(decoded.streamId, streamId)).toBe(true); expect(decoded.laneId).toBe(7); expect(decoded.seq).toBe(12345n); expect(bytesEqual(decoded.envelope, envelope)).toBe(true); }); it('roundtrips a resume-query frame', () => { const requestId = randomRequestId(); const streamId = new Uint8Array(STREAM_ID_LEN).fill(0x33); const buf = encodeResumeQueryFrame({ type: WIRE_RESUME_QUERY, requestId, streamId, }); const decoded = decodeFrame(buf); if (decoded.type !== WIRE_RESUME_QUERY) throw new Error('type narrow failed'); expect(bytesEqual(decoded.requestId, requestId)).toBe(true); expect(bytesEqual(decoded.streamId, streamId)).toBe(true); }); it('roundtrips chunk-ack', () => { const requestId = randomRequestId(); const buf = encodeChunkAckFrame({ type: WIRE_CHUNK_ACK, requestId, lastSeq: 42, bytesReceived: 1024, }); const decoded = decodeFrame(buf); if (decoded.type !== WIRE_CHUNK_ACK) throw new Error('type narrow failed'); expect(decoded.lastSeq).toBe(42); expect(decoded.bytesReceived).toBe(1024); }); it('roundtrips resume-state frame', () => { const json = JSON.stringify({ streamId: 'abc', lanes: [{ laneId: 0, lastSeqAcked: 5 }], }); const buf = encodeResumeStateFrame({ type: WIRE_RESUME_STATE, requestId: randomRequestId(), json, }); const decoded = decodeFrame(buf); if (decoded.type !== WIRE_RESUME_STATE) throw new Error('type narrow failed'); expect(decoded.json).toBe(json); }); it('roundtrips ping/pong frames', () => { const requestId = randomRequestId(); const ping = encodePingFrame({ type: WIRE_PING, requestId, nonce: 99n }); const decodedPing = decodeFrame(ping); if (decodedPing.type !== WIRE_PING) throw new Error('type narrow failed'); expect(decodedPing.nonce).toBe(99n); const pong = encodePongFrame({ type: WIRE_PONG, requestId, nonce: 99n }); const decodedPong = decodeFrame(pong); if (decodedPong.type !== WIRE_PONG) throw new Error('type narrow failed'); expect(decodedPong.nonce).toBe(99n); }); it('roundtrips error frame', () => { const buf = encodeErrorFrame({ type: WIRE_ERROR, requestId: randomRequestId(), json: '{"error":"oh no"}', }); const decoded = decodeFrame(buf); if (decoded.type !== WIRE_ERROR) throw new Error('type narrow failed'); expect(JSON.parse(decoded.json)).toEqual({ error: 'oh no' }); }); it('rejects truncated frames', () => { const tiny = new Uint8Array(8); expect(() => decodeFrame(tiny)).toThrow(); }); it('rejects unknown type', () => { const bad = new Uint8Array(REQUEST_ID_LEN + 1); bad[0] = 0x77; expect(() => decodeFrame(bad)).toThrow(); }); }); describe('streamId base64url codec', () => { it('roundtrips arbitrary 16-byte ids', () => { const original = new Uint8Array(STREAM_ID_LEN); globalThis.crypto.getRandomValues(original); const s = streamIdBytesToString(original); const back = streamIdStringToBytes(s); expect(bytesEqual(back, original)).toBe(true); }); });