import { describe, test, expect } from 'bun:test'; import { encodeStreamOpen, encodeStreamOpenAck, decodeStreamHandshake, encodeStreamFrame, decodeStreamFrame, inspectEnvelopeType, } from '../src/index.js'; import type { RatchetMessage } from '@shade/core'; function randBytes(n: number): Uint8Array { const buf = new Uint8Array(n); crypto.getRandomValues(buf); return buf; } function makeRatchetMessage(): RatchetMessage { return { dhPublicKey: randBytes(32), previousCounter: 3, counter: 9001, ciphertext: randBytes(128), nonce: randBytes(12), }; } describe('Stream sub-session wire (V4.11)', () => { test('STREAM_OPEN round-trips and inspects', () => { const sid = randBytes(16); const eph = randBytes(32); const bytes = encodeStreamOpen(sid, eph); expect(inspectEnvelopeType(bytes)).toBe('stream-open'); const hs = decodeStreamHandshake(bytes); expect(hs.kind).toBe('open'); expect(hs.streamId).toEqual(sid); expect(hs.ephemeralPub).toEqual(eph); }); test('STREAM_OPEN_ACK round-trips and inspects', () => { const sid = randBytes(16); const eph = randBytes(32); const bytes = encodeStreamOpenAck(sid, eph); expect(inspectEnvelopeType(bytes)).toBe('stream-open-ack'); const hs = decodeStreamHandshake(bytes); expect(hs.kind).toBe('open-ack'); expect(hs.streamId).toEqual(sid); expect(hs.ephemeralPub).toEqual(eph); }); test('STREAM_FRAME carries a full ratchet message verbatim', () => { const sid = randBytes(16); const msg = makeRatchetMessage(); const bytes = encodeStreamFrame(sid, msg); expect(inspectEnvelopeType(bytes)).toBe('stream-frame'); const decoded = decodeStreamFrame(bytes); expect(decoded.streamId).toEqual(sid); expect(decoded.message.dhPublicKey).toEqual(msg.dhPublicKey); expect(decoded.message.previousCounter).toBe(msg.previousCounter); expect(decoded.message.counter).toBe(msg.counter); expect(decoded.message.ciphertext).toEqual(msg.ciphertext); expect(decoded.message.nonce).toEqual(msg.nonce); }); test('rejects wrong sizes and wrong type tags', () => { expect(() => encodeStreamOpen(randBytes(15), randBytes(32))).toThrow(); expect(() => encodeStreamOpen(randBytes(16), randBytes(31))).toThrow(); expect(() => decodeStreamHandshake(encodeStreamFrame(randBytes(16), makeRatchetMessage()))).toThrow(); expect(() => decodeStreamFrame(encodeStreamOpen(randBytes(16), randBytes(32)))).toThrow(); }); });