peachScript.test.ts

6.5 KB · raw

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  // Off-by-one boundary — U+E100 is one above the valid ceiling U+E0FF66  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  // Surrogate code points — below the valid PUA range U+E000..U+E0FF79  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  // Real Mayan numerals are U+1D2E0..U+1D2F3 — outside PUA codec range92  test("rejects U+1D2E0 (real Mayan numeral — outside codec range)", () => {93    expect(() => decodePeachScript("\u{1D2E0}")).toThrow(94      "INVALID_PEACHSCRIPT_CHARACTER"95    );96  });9798  // BOM and Unicode direction/zero-width marks mid-stream99  test("rejects BOM (U+FEFF) mid-stream", () => {100    const encoded = encodePeachScript(new Uint8Array([0x41, 0x42]));101    // U+FEFF = BOM, inject between two valid runes102    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    // U+200F = RIGHT-TO-LEFT MARK109    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    // U+200C = ZERO WIDTH NON-JOINER117    expect(() => decodePeachScript(encoded + String.fromCodePoint(0x200c))).toThrow(118      "INVALID_PEACHSCRIPT_CHARACTER"119    );120  });121122  // Whitespace — the raw decoder is strict; normalisation is a separate step123  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  // Boundary bytes must round-trip correctly136  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  // 1 KiB pseudo-random round-trip152  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  // DoS resistance — the decoder must fail on the first invalid character,159  // not after scanning the whole input. U+E100 > 0xE0FF fails on char 0.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  // Copy-paste hostility — smart quote prefix (U+201C = LEFT DOUBLE QUOTATION MARK)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});