1import { describe, expect, test } from "vitest";2import {3 decodePeachScript,4 encodePeachScript,5 isPeachScript,6 normalisePeachScript,7} from "./peachScript";89describe("peachScript", () => {10 test("byte 0 maps to U+E000", () => {11 const out = encodePeachScript(new Uint8Array([0]));12 expect(out.codePointAt(0)).toBe(0xe000);13 });1415 test("byte 255 maps to U+E0FF", () => {16 const out = encodePeachScript(new Uint8Array([255]));17 expect(out.codePointAt(0)).toBe(0xe0ff);18 });1920 test("round-trip identity for all 256 byte values", () => {21 const bytes = new Uint8Array(256);22 for (let i = 0; i < 256; i++) bytes[i] = i;23 const encoded = encodePeachScript(bytes);24 const decoded = decodePeachScript(encoded);25 expect(decoded).toEqual(bytes);26 });2728 test("empty bytes round-trip to empty string and back", () => {29 expect(encodePeachScript(new Uint8Array())).toBe("");30 expect(decodePeachScript("")).toEqual(new Uint8Array());31 });3233 test("decode throws on character outside PUA range", () => {34 expect(() => decodePeachScript("hello")).toThrow("INVALID_PEACHSCRIPT_CHARACTER");35 expect(() => decodePeachScript(String.fromCodePoint(0xe100))).toThrow(36 "INVALID_PEACHSCRIPT_CHARACTER"37 );38 expect(() => decodePeachScript(String.fromCodePoint(0xdfff))).toThrow(39 "INVALID_PEACHSCRIPT_CHARACTER"40 );41 });4243 test("random-payload round-trip preserves bytes", () => {44 const payload = crypto.getRandomValues(new Uint8Array(2048));45 const decoded = decodePeachScript(encodePeachScript(payload));46 expect(decoded).toEqual(payload);47 });4849 test("normalisePeachScript strips whitespace", () => {50 const encoded = encodePeachScript(new Uint8Array([1, 2, 3, 4]));51 const wrapped = encoded.slice(0, 2) + "\n " + encoded.slice(2);52 expect(normalisePeachScript(wrapped)).toBe(encoded);53 });5455 test("isPeachScript accepts only PUA strings", () => {56 expect(isPeachScript(encodePeachScript(new Uint8Array([1, 2, 3])))).toBe(true);57 expect(isPeachScript("")).toBe(false);58 expect(isPeachScript("hello")).toBe(false);59 const mixed = encodePeachScript(new Uint8Array([1])) + "x";60 expect(isPeachScript(mixed)).toBe(false);61 });62});6364describe("peachScript fuzz corpus", () => {65 66 test("rejects U+E100 (off-by-one out of range)", () => {67 expect(() => decodePeachScript(String.fromCodePoint(0xe100))).toThrow(68 "INVALID_PEACHSCRIPT_CHARACTER"69 );70 });7172 test("rejects U+E200 (far out of range)", () => {73 expect(() => decodePeachScript(String.fromCodePoint(0xe200))).toThrow(74 "INVALID_PEACHSCRIPT_CHARACTER"75 );76 });7778 79 test("rejects lone low surrogate U+DFFF", () => {80 expect(() => decodePeachScript(String.fromCodePoint(0xdfff))).toThrow(81 "INVALID_PEACHSCRIPT_CHARACTER"82 );83 });8485 test("rejects lone high surrogate U+D800", () => {86 expect(() => decodePeachScript(String.fromCodePoint(0xd800))).toThrow(87 "INVALID_PEACHSCRIPT_CHARACTER"88 );89 });9091 92 test("rejects U+1D2E0 (real Mayan numeral — outside codec range)", () => {93 expect(() => decodePeachScript("\u{1D2E0}")).toThrow(94 "INVALID_PEACHSCRIPT_CHARACTER"95 );96 });9798 99 test("rejects BOM (U+FEFF) mid-stream", () => {100 const encoded = encodePeachScript(new Uint8Array([0x41, 0x42]));101 102 const withBOM = encoded[0] + String.fromCodePoint(0xfeff) + encoded[1];103 expect(() => decodePeachScript(withBOM)).toThrow("INVALID_PEACHSCRIPT_CHARACTER");104 });105106 test("rejects RTL mark (U+200F) mid-stream", () => {107 const encoded = encodePeachScript(new Uint8Array([0x41]));108 109 expect(() => decodePeachScript(encoded + String.fromCodePoint(0x200f))).toThrow(110 "INVALID_PEACHSCRIPT_CHARACTER"111 );112 });113114 test("rejects zero-width non-joiner (U+200C)", () => {115 const encoded = encodePeachScript(new Uint8Array([0x41]));116 117 expect(() => decodePeachScript(encoded + String.fromCodePoint(0x200c))).toThrow(118 "INVALID_PEACHSCRIPT_CHARACTER"119 );120 });121122 123 test("rejects space between valid rune characters", () => {124 const encoded = encodePeachScript(new Uint8Array([0x41, 0x42]));125 const withSpace = encoded[0] + " " + encoded[1];126 expect(() => decodePeachScript(withSpace)).toThrow("INVALID_PEACHSCRIPT_CHARACTER");127 });128129 test("rejects newline between valid rune characters", () => {130 const encoded = encodePeachScript(new Uint8Array([0x41, 0x42]));131 const withNewline = encoded[0] + "\n" + encoded[1];132 expect(() => decodePeachScript(withNewline)).toThrow("INVALID_PEACHSCRIPT_CHARACTER");133 });134135 136 test("byte 0x00 round-trips correctly", () => {137 const data = new Uint8Array([0x00]);138 expect(decodePeachScript(encodePeachScript(data))).toEqual(data);139 });140141 test("byte 0xFE round-trips correctly", () => {142 const data = new Uint8Array([0xfe]);143 expect(decodePeachScript(encodePeachScript(data))).toEqual(data);144 });145146 test("byte 0xFF round-trips correctly", () => {147 const data = new Uint8Array([0xff]);148 expect(decodePeachScript(encodePeachScript(data))).toEqual(data);149 });150151 152 test("1 KiB deterministic payload round-trips", () => {153 const data = new Uint8Array(1024);154 for (let i = 0; i < 1024; i++) data[i] = (i * 37 + 13) % 256;155 expect(decodePeachScript(encodePeachScript(data))).toEqual(data);156 });157158 159 160 test("rejects large all-invalid input in well under 50 ms", () => {161 const invalid = String.fromCodePoint(0xe100).repeat(10_000);162 const start = Date.now();163 expect(() => decodePeachScript(invalid)).toThrow("INVALID_PEACHSCRIPT_CHARACTER");164 expect(Date.now() - start).toBeLessThan(50);165 });166167 168 test("rejects left double quotation mark (U+201C) prefix", () => {169 const encoded = encodePeachScript(new Uint8Array([0x41]));170 expect(() => decodePeachScript(String.fromCodePoint(0x201c) + encoded)).toThrow(171 "INVALID_PEACHSCRIPT_CHARACTER"172 );173 });174});