Files
suaveplan/packages/foundation/data/bytes/src/encoding/encoding.test.ts
2026-05-27 23:47:53 +01:00

111 lines
3.5 KiB
TypeScript

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);
})
);
});
});