import { describe, expect, it } from "@suaveplan/testing/runner"; import fc from "fast-check"; import { base64ToBytes, bytesToBase64, bytesToHex, bytesToUtf8, hexToBytes, utf8ToBytes, } from "./encoding.js"; describe("bytesToHex / hexToBytes", () => { it("round-trips arbitrary bytes", { timeout: 5000 }, () => { const input = new Uint8Array([0x48, 0x65, 0x6c, 0x6c, 0x6f]); expect(hexToBytes(bytesToHex(input))).toEqual(input); }); it("bytesToHex produces lowercase hex", { timeout: 5000 }, () => { expect(bytesToHex(new Uint8Array([255]))).toBe("ff"); }); it("bytesToHex handles empty array", { timeout: 5000 }, () => { expect(bytesToHex(new Uint8Array())).toBe(""); }); it("hexToBytes handles empty string", { timeout: 5000 }, () => { expect(hexToBytes("")).toEqual(new Uint8Array()); }); it("hexToBytes rejects odd-length string", { timeout: 5000 }, () => { expect(() => hexToBytes("abc")).toThrow(); }); it("hexToBytes rejects non-hex chars", { timeout: 5000 }, () => { expect(() => hexToBytes("zz")).toThrow(); }); it("hexToBytes rejects uppercase letters that are not hex", { timeout: 5000, }, () => { expect(() => hexToBytes("GG")).toThrow(); }); it("hexToBytes accepts uppercase hex letters", { timeout: 5000 }, () => { expect(hexToBytes("FF")).toEqual(new Uint8Array([255])); }); it("hex round-trip for arbitrary bytes", { timeout: 30000 }, () => { fc.assert( fc.property(fc.uint8Array(), (bytes) => { expect(hexToBytes(bytesToHex(bytes))).toEqual(bytes); }) ); }); }); describe("bytesToBase64 / base64ToBytes", () => { it("round-trips arbitrary bytes", { timeout: 5000 }, () => { const input = new Uint8Array([1, 2, 3, 4, 5]); expect(base64ToBytes(bytesToBase64(input))).toEqual(input); }); it("handles empty array", { timeout: 5000 }, () => { expect(bytesToBase64(new Uint8Array())).toBe(""); expect(base64ToBytes("")).toEqual(new Uint8Array()); }); it("rejects invalid base64", { timeout: 5000 }, () => { expect(() => base64ToBytes("not!valid@base64")).toThrow(); }); it("rejects syntactically valid but malformed base64 (bad padding)", { timeout: 5000, }, () => { // "a=" passes the regex but atob rejects it (requires groups of 4 chars) expect(() => base64ToBytes("a=")).toThrow(); }); it("base64 round-trip for arbitrary bytes", { timeout: 30000 }, () => { fc.assert( fc.property(fc.uint8Array(), (bytes) => { expect(base64ToBytes(bytesToBase64(bytes))).toEqual(bytes); }) ); }); }); describe("bytesToUtf8 / utf8ToBytes", () => { it("round-trips ASCII", { timeout: 5000 }, () => { const text = "Hello, world!"; expect(bytesToUtf8(utf8ToBytes(text))).toBe(text); }); it("round-trips multibyte characters", { timeout: 5000 }, () => { const text = "こんにちは"; expect(bytesToUtf8(utf8ToBytes(text))).toBe(text); }); it("handles empty string", { timeout: 5000 }, () => { expect(bytesToUtf8(utf8ToBytes(""))).toBe(""); }); it("utf8 round-trip for arbitrary strings", { timeout: 30000 }, () => { fc.assert( fc.property(fc.string(), (str) => { expect(bytesToUtf8(utf8ToBytes(str))).toBe(str); }) ); }); });