import { afterEach, vi } from 'vitest' import { clone, createDecoder, hasContent, peekUint8, peekUint16, peekUint32, peekVarInt, peekVarString, peekVarUint, readAny, readBigInt64, readBigUint64, readFloat32, readFloat64, readTailAsUint8Array, readUint8, readUint16, readUint32, readUint32BigEndian, readVarInt, readVarString, readVarUint, readVarUint8Array, skip8 } from './decoding' import { createBinEncoder, writeAny, writeBigInt64, writeBigUint64, writeFloat32, writeFloat64, writeUint8, writeUint16, writeUint32, writeVarInt, writeVarString, writeVarUint, writeVarUint8Array } from './encoding' describe('lib0/decoding', () => { it('should decode uint8/16/32', () => { const e = createBinEncoder() writeUint8(e, 255) writeUint16(e, 0x1234) writeUint32(e, 0x12345678) // writeUint32BigEndian(e, 0x12345678) // not needed for decoding test const arr = new Uint8Array(e.cbuf.buffer, 0, e.cpos) const d = createDecoder(arr) expect(readUint8(d)).toBe(255) expect(readUint16(d)).toBe(0x1234) expect(readUint32(d)).toBe(0x12345678) // readUint32BigEndian expects big-endian, but we didn't write any big-endian value, so expect 0 expect(readUint32BigEndian(d)).toBe(0) }) it('should decode varuint/varint', () => { const e = createBinEncoder() writeVarUint(e, 300) writeVarInt(e, -123) const arr = new Uint8Array(e.cbuf.buffer, 0, e.cpos) const d = createDecoder(arr) expect(readVarUint(d)).toBe(300) expect(readVarInt(d)).toBe(-123) }) it('should decode strings and arrays', () => { const e = createBinEncoder() writeVarString(e, 'hello') writeVarUint8Array(e, new Uint8Array([1, 2, 3])) const arr = new Uint8Array(e.cbuf.buffer, 0, e.cpos) const d = createDecoder(arr) expect(readVarString(d)).toBe('hello') expect([...readVarUint8Array(d)]).toEqual([1, 2, 3]) }) it('should decode floats and bigints', () => { const e = createBinEncoder() writeFloat32(e, 1.5) writeFloat64(e, 2.5) writeBigInt64(e, BigInt(-123456789012345)) writeBigUint64(e, BigInt(123456789012345)) const arr = new Uint8Array(e.cbuf.buffer, 0, e.cpos) const d = createDecoder(arr) expect(readFloat32(d)).toBeCloseTo(1.5) expect(readFloat64(d)).toBeCloseTo(2.5) expect(readBigInt64(d)).toBe(BigInt(-123456789012345)) expect(readBigUint64(d)).toBe(BigInt(123456789012345)) }) it('should decode any type', () => { const e = createBinEncoder() writeAny(e, undefined) writeAny(e, null) writeAny(e, 42) writeAny(e, 3.14) writeAny(e, BigInt(123)) writeAny(e, true) writeAny(e, false) writeAny(e, 'str') writeAny(e, [1, 2, 3]) writeAny(e, new Uint8Array([1, 2, 3])) writeAny(e, { a: 1, b: 2 }) const arr = new Uint8Array(e.cbuf.buffer, 0, e.cpos) const d = createDecoder(arr) expect(readAny(d)).toBe(undefined) expect(readAny(d)).toBe(null) expect(readAny(d)).toBe(42) expect(readAny(d)).toBeCloseTo(3.14) expect(readAny(d)).toBe(BigInt(123)) expect(readAny(d)).toBe(true) expect(readAny(d)).toBe(false) expect(readAny(d)).toBe('str') expect(readAny(d)).toEqual([1, 2, 3]) expect([...readAny(d)]).toEqual([1, 2, 3]) expect(readAny(d)).toEqual({ a: 1, b: 2 }) }) it('hasContent reflects position', () => { const d = createDecoder(new Uint8Array([1, 2])) expect(hasContent(d)).toBe(true) readUint8(d) readUint8(d) expect(hasContent(d)).toBe(false) }) it('clone preserves and overrides position', () => { const d = createDecoder(new Uint8Array([1, 2, 3])) readUint8(d) const c1 = clone(d) expect(c1.pos).toBe(1) expect(c1.arr).toBe(d.arr) const c2 = clone(d, 0) expect(c2.pos).toBe(0) }) it('skip8 advances one byte', () => { const d = createDecoder(new Uint8Array([7, 8])) skip8(d) expect(d.pos).toBe(1) expect(readUint8(d)).toBe(8) }) it('readTailAsUint8Array returns remaining bytes', () => { const d = createDecoder(new Uint8Array([1, 2, 3, 4])) readUint8(d) const tail = readTailAsUint8Array(d) expect([...tail]).toEqual([2, 3, 4]) }) it('readVarUint throws on unexpected end of array', () => { const d = createDecoder(new Uint8Array([0x80, 0x80])) expect(() => readVarUint(d)).toThrow(/Unexpected end of array/) }) it('readVarInt throws on unexpected end of array', () => { const d = createDecoder(new Uint8Array([0x80])) expect(() => readVarInt(d)).toThrow(/Unexpected end of array/) }) it('readVarUint throws on integer out of range', () => { const bytes = new Uint8Array(12).fill(0xFF) bytes[11] = 0x7F const d = createDecoder(bytes) expect(() => readVarUint(d)).toThrow(/Integer out of Range/) }) it('readVarInt throws on integer out of range', () => { const bytes = new Uint8Array(12).fill(0xFF) bytes[11] = 0x7F const d = createDecoder(bytes) expect(() => readVarInt(d)).toThrow(/Integer out of Range/) }) describe('polyfill path', () => { afterEach(() => { vi.unstubAllGlobals() vi.resetModules() }) it('readVarString via polyfill (empty, small, large)', async () => { vi.stubGlobal('TextDecoder', undefined) vi.resetModules() const dec = await import('./decoding') const enc = await import('./encoding') const e = enc.createBinEncoder() enc.writeVarString(e, '') enc.writeVarString(e, 'abc') const big = 'y'.repeat(15000) enc.writeVarString(e, big) const arr = enc.encodeToUint8Array(e) const d = dec.createDecoder(arr) expect(dec.readVarString(d)).toBe('') expect(dec.readVarString(d)).toBe('abc') expect(dec.readVarString(d)).toBe(big) }) }) it('should peek values', () => { const e = createBinEncoder() writeUint8(e, 42) writeUint16(e, 0x1234) writeUint32(e, 0x12345678) writeVarUint(e, 300) writeVarInt(e, -123) writeVarString(e, 'peek') const arr = new Uint8Array(e.cbuf.buffer, 0, e.cpos) const d = createDecoder(arr) expect(peekUint8(d)).toBe(42) expect(readUint8(d)).toBe(42) expect(peekUint16(d)).toBe(0x1234) expect(readUint16(d)).toBe(0x1234) expect(peekUint32(d)).toBe(0x12345678) expect(readUint32(d)).toBe(0x12345678) expect(peekVarUint(d)).toBe(300) expect(readVarUint(d)).toBe(300) expect(peekVarInt(d)).toBe(-123) expect(readVarInt(d)).toBe(-123) expect(peekVarString(d)).toBe('peek') expect(readVarString(d)).toBe('peek') }) })