196 lines
6.4 KiB
TypeScript
196 lines
6.4 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { base64ToBytes, hexToBytes, BufferReader, BufferWriter } from '../src/parser';
|
|
|
|
describe('base64ToBytes', () => {
|
|
it('decodes a simple base64 string', () => {
|
|
const bytes = base64ToBytes('aGVsbG8=', 5); // "hello"
|
|
expect(Array.from(bytes)).toEqual([104, 101, 108, 108, 111]);
|
|
});
|
|
|
|
it('handles empty string', () => {
|
|
const bytes = base64ToBytes('', 0);
|
|
expect(bytes).toBeInstanceOf(Uint8Array);
|
|
expect(bytes.length).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('BufferReader', () => {
|
|
it('readByte and peekByte advance/inspect correctly', () => {
|
|
const buf = new Uint8Array([1, 2, 3]);
|
|
const r = new BufferReader(buf);
|
|
expect(r.peekByte()).toBe(1);
|
|
expect(r.readByte()).toBe(1);
|
|
expect(r.peekByte()).toBe(2);
|
|
});
|
|
|
|
it('readBytes with and without length', () => {
|
|
const buf = new Uint8Array([10, 11, 12, 13]);
|
|
const r = new BufferReader(buf);
|
|
const a = r.readBytes(2);
|
|
expect(Array.from(a)).toEqual([10, 11]);
|
|
const b = r.readBytes();
|
|
expect(Array.from(b)).toEqual([12, 13]);
|
|
});
|
|
|
|
it('hasMore and remainingBytes reflect position', () => {
|
|
const buf = new Uint8Array([5, 6]);
|
|
const r = new BufferReader(buf);
|
|
expect(r.hasMore()).toBe(true);
|
|
expect(r.remainingBytes()).toBe(2);
|
|
r.readByte();
|
|
expect(r.remainingBytes()).toBe(1);
|
|
r.readByte();
|
|
expect(r.hasMore()).toBe(false);
|
|
});
|
|
|
|
it('reads little-endian unsigned ints', () => {
|
|
const r16 = new BufferReader(new Uint8Array([0x34, 0x12]));
|
|
expect(r16.readUint16LE()).toBe(0x1234);
|
|
|
|
const r32 = new BufferReader(new Uint8Array([0x78, 0x56, 0x34, 0x12]));
|
|
expect(r32.readUint32LE()).toBe(0x12345678);
|
|
});
|
|
|
|
it('reads signed ints with two/four bytes (negative)', () => {
|
|
const r16 = new BufferReader(new Uint8Array([0xff, 0xff]));
|
|
expect(r16.readInt16LE()).toBe(-1);
|
|
|
|
const r32 = new BufferReader(new Uint8Array([0xff, 0xff, 0xff, 0xff]));
|
|
expect(r32.readInt32LE()).toBe(-1);
|
|
});
|
|
|
|
it('readTimestamp returns Date with seconds->ms conversion', () => {
|
|
const r = new BufferReader(new Uint8Array([0x01, 0x00, 0x00, 0x00]));
|
|
const d = r.readTimestamp();
|
|
expect(d.getTime()).toBe(1000);
|
|
});
|
|
});
|
|
describe('sizedStringToBytes', () => {
|
|
it('decodes hex string of correct length', () => {
|
|
// 4 bytes = 8 hex chars
|
|
const hex = 'deadbeef';
|
|
const result = hexToBytes(hex, 4);
|
|
expect(Array.from(result)).toEqual([0xde, 0xad, 0xbe, 0xef]);
|
|
});
|
|
|
|
it('decodes base64 string of correct length', () => {
|
|
// 4 bytes = 8 hex chars, base64 for [0xde, 0xad, 0xbe, 0xef] is '3q2+7w=='
|
|
const b64 = '3q2+7w==';
|
|
const result = base64ToBytes(b64, 4);
|
|
expect(Array.from(result)).toEqual([0xde, 0xad, 0xbe, 0xef]);
|
|
});
|
|
|
|
it('throws on invalid string length', () => {
|
|
expect(() => hexToBytes('abc', 4)).toThrow();
|
|
expect(() => hexToBytes('deadbeef00', 4)).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('BufferWriter', () => {
|
|
it('writeByte and toBytes', () => {
|
|
const w = new BufferWriter();
|
|
w.writeByte(0x12);
|
|
w.writeByte(0x34);
|
|
expect(Array.from(w.toBytes())).toEqual([0x12, 0x34]);
|
|
});
|
|
|
|
it('writeBytes appends bytes', () => {
|
|
const w = new BufferWriter();
|
|
w.writeBytes(new Uint8Array([1, 2, 3]));
|
|
expect(Array.from(w.toBytes())).toEqual([1, 2, 3]);
|
|
});
|
|
|
|
it('writeUint16LE writes little-endian', () => {
|
|
const w = new BufferWriter();
|
|
w.writeUint16LE(0x1234);
|
|
expect(Array.from(w.toBytes())).toEqual([0x34, 0x12]);
|
|
});
|
|
|
|
it('writeUint32LE writes little-endian', () => {
|
|
const w = new BufferWriter();
|
|
w.writeUint32LE(0x12345678);
|
|
expect(Array.from(w.toBytes())).toEqual([0x78, 0x56, 0x34, 0x12]);
|
|
});
|
|
|
|
it('writeInt16LE writes signed values', () => {
|
|
const w = new BufferWriter();
|
|
w.writeInt16LE(-1);
|
|
expect(Array.from(w.toBytes())).toEqual([0xff, 0xff]);
|
|
const w2 = new BufferWriter();
|
|
w2.writeInt16LE(0x1234);
|
|
expect(Array.from(w2.toBytes())).toEqual([0x34, 0x12]);
|
|
});
|
|
|
|
it('writeInt32LE writes signed values', () => {
|
|
const w = new BufferWriter();
|
|
w.writeInt32LE(-1);
|
|
expect(Array.from(w.toBytes())).toEqual([0xff, 0xff, 0xff, 0xff]);
|
|
const w2 = new BufferWriter();
|
|
w2.writeInt32LE(0x12345678);
|
|
expect(Array.from(w2.toBytes())).toEqual([0x78, 0x56, 0x34, 0x12]);
|
|
});
|
|
|
|
it('writeTimestamp writes seconds as uint32le', () => {
|
|
const w = new BufferWriter();
|
|
const date = new Date(1000); // 1 second
|
|
w.writeTimestamp(date);
|
|
expect(Array.from(w.toBytes())).toEqual([0x01, 0x00, 0x00, 0x00]);
|
|
});
|
|
|
|
it('BufferWriter output can be read back by BufferReader', () => {
|
|
const w = new BufferWriter();
|
|
w.writeByte(0x42);
|
|
w.writeUint16LE(0x1234);
|
|
w.writeInt16LE(-2);
|
|
w.writeUint32LE(0xdeadbeef);
|
|
w.writeInt32LE(-123456);
|
|
w.writeBytes(new Uint8Array([0x01, 0x02]));
|
|
const date = new Date(5000); // 5 seconds
|
|
w.writeTimestamp(date);
|
|
|
|
const bytes = w.toBytes();
|
|
const r = new BufferReader(bytes);
|
|
|
|
expect(r.readByte()).toBe(0x42);
|
|
expect(r.readUint16LE()).toBe(0x1234);
|
|
expect(r.readInt16LE()).toBe(-2);
|
|
expect(r.readUint32LE()).toBe(0xdeadbeef);
|
|
expect(r.readInt32LE()).toBe(-123456);
|
|
expect(Array.from(r.readBytes(2))).toEqual([0x01, 0x02]);
|
|
const readDate = r.readTimestamp();
|
|
expect(readDate.getTime()).toBe(5000);
|
|
expect(r.hasMore()).toBe(false);
|
|
});
|
|
|
|
it('BufferReader throws or returns undefined if reading past end', () => {
|
|
const r = new BufferReader(new Uint8Array([1, 2]));
|
|
r.readByte();
|
|
r.readByte();
|
|
expect(() => r.readByte()).toThrow();
|
|
});
|
|
|
|
it('BufferWriter handles multiple writeBytes calls', () => {
|
|
const w = new BufferWriter();
|
|
w.writeBytes(new Uint8Array([1, 2]));
|
|
w.writeBytes(new Uint8Array([3, 4]));
|
|
expect(Array.from(w.toBytes())).toEqual([1, 2, 3, 4]);
|
|
});
|
|
|
|
it('encodedStringToBytes decodes raw string', () => {
|
|
const str = String.fromCharCode(0xde, 0xad, 0xbe, 0xef);
|
|
const bytes = new Uint8Array(4);
|
|
for (let i = 0; i < 4; i++) bytes[i] = str.charCodeAt(i) & 0xff;
|
|
expect(Array.from(bytes)).toEqual([0xde, 0xad, 0xbe, 0xef]);
|
|
});
|
|
|
|
it('hexToBytes returns different length for wrong-size hex', () => {
|
|
expect(() => hexToBytes('deadbe', 4)).toThrow();
|
|
});
|
|
|
|
it('base64ToBytes handles URL-safe base64', () => {
|
|
// [0xde, 0xad, 0xbe, 0xef] in URL-safe base64: '3q2-7w=='
|
|
const bytes = base64ToBytes('3q2-7w==', 4);
|
|
expect(Array.from(bytes)).toEqual([0xde, 0xad, 0xbe, 0xef]);
|
|
});
|
|
});
|