import { describe, it, expect } from "vitest"; import { encodePairingCode, formatPairingCode, parsePairingCode } from "./pairing-code"; const token4 = Uint8Array.of(0xde, 0xad, 0xbe, 0xef); const token8 = Uint8Array.of(0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef); describe("pairing-code", () => { it("round-trips a 4-byte token as exactly 16 characters", () => { const code = encodePairingCode("192.168.1.42", 49142, token4); expect(code.replace(/-/g, "")).toHaveLength(16); expect(code).toMatch(/^[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}-[0-9A-Z]{4}$/); expect(parsePairingCode(code)).toEqual({ ip: "192.168.1.42", port: 49142, tokenHex: "deadbeef", }); }); it("round-trips an 8-byte token without a coordinated length change", () => { const code = encodePairingCode("10.0.0.1", 8765, token8); expect(parsePairingCode(code)).toEqual({ ip: "10.0.0.1", port: 8765, tokenHex: "0123456789abcdef", }); }); it("preserves the full port range", () => { for (const port of [1, 8765, 49142, 65535]) { const code = encodePairingCode("192.168.4.230", port, token4); expect(parsePairingCode(code)?.port).toBe(port); } }); it("folds the ambiguous glyphs I, L and O on the way in", () => { const code = encodePairingCode("192.168.1.42", 49142, token4); const ambiguous = code.replace(/0/g, "O").replace(/1/g, "I"); expect(parsePairingCode(ambiguous)).toEqual(parsePairingCode(code)); }); it("accepts a code typed without dashes, in lowercase, with stray spaces", () => { const code = encodePairingCode("192.168.1.42", 49142, token4); const sloppy = ` ${code.replace(/-/g, " ").toLowerCase()} `; expect(parsePairingCode(sloppy)).toEqual(parsePairingCode(code)); }); it("rejects codes that are too short to carry a token", () => { // 4 IP bytes + 2 port bytes and nothing else — 10 base32 characters. expect(parsePairingCode("ABCDEFGHJK")).toBeNull(); expect(parsePairingCode("")).toBeNull(); expect(parsePairingCode("---")).toBeNull(); }); it("rejects characters outside Crockford's alphabet", () => { // U is excluded by Crockford and is NOT folded, unlike I/L/O. expect(parsePairingCode("UUUU-UUUU-UUUU-UUUU")).toBeNull(); expect(parsePairingCode("A74H-48J3-KI32-OEP!")).toBeNull(); }); it("rejects a zero port", () => { // An all-zero payload is long enough but decodes to 0.0.0.0:0 — a code // that parsed would send the dialler at an unroutable endpoint. expect(parsePairingCode("0000-0000-0000-0000")).toBeNull(); }); it("refuses to encode an out-of-range address, port, or short token", () => { expect(() => encodePairingCode("192.168.1.256", 49142, token4)).toThrow(); expect(() => encodePairingCode("192.168.1", 49142, token4)).toThrow(); expect(() => encodePairingCode("192.168.1.42", 0, token4)).toThrow(); expect(() => encodePairingCode("192.168.1.42", 70000, token4)).toThrow(); expect(() => encodePairingCode("192.168.1.42", 49142, Uint8Array.of(1, 2, 3))).toThrow(); }); it("regroups partial input as the user types", () => { expect(formatPairingCode("a74h48")).toBe("A74H-48"); expect(formatPairingCode("A74H-48J3-KI32-OEP4")).toBe("A74H-48J3-KI32-OEP4"); expect(formatPairingCode("a74h 48j3")).toBe("A74H-48J3"); expect(formatPairingCode("")).toBe(""); }); });