111 lines
3.5 KiB
TypeScript
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);
|
|
})
|
|
);
|
|
});
|
|
});
|