12345import { describe, expect, it } from "vitest";67import {8 ALPHABET_END,9 ALPHABET_SIZE,10 ALPHABET_START,11 cjkDecode,12 cjkEncode,13 isValidPhraseCharset,14 remainingBits,15} from "./cjk";1617describe("cjkEncode", () => {18 it("encodes 0x00 to U+4E00 (first character of the alphabet)", () => {19 20 expect(cjkEncode(new Uint8Array([0x00]))).toBe("\u4E00");21 });2223 it("encodes 0xFF to U+8DC0 (verified programmatically)", () => {24 25 26 27 expect(cjkEncode(new Uint8Array([0xFF]))).toBe("\u8DC0");28 });2930 it("encodes 0xAB 0xCD to [U+78F3, U+5E00]", () => {31 32 33 34 expect(cjkEncode(new Uint8Array([0xAB, 0xCD]))).toBe("\u78F3\u5E00");35 });3637 it("produces only characters within the 16384-entry alphabet", () => {38 const bytes = new Uint8Array(256);39 for (let i = 0; i < 256; i++) bytes[i] = i;40 const encoded = cjkEncode(bytes);41 for (const ch of encoded) {42 const cp = ch.codePointAt(0)!;43 expect(cp).toBeGreaterThanOrEqual(ALPHABET_START);44 expect(cp).toBeLessThanOrEqual(ALPHABET_END);45 }46 });47});4849describe("cjkEncode / cjkDecode round-trip", () => {50 it("round-trips every single-byte value", () => {51 for (let b = 0; b <= 255; b++) {52 const original = new Uint8Array([b]);53 const encoded = cjkEncode(original);54 const decoded = cjkDecode(encoded, remainingBits(1));55 expect(decoded).toEqual(original);56 }57 });5859 it("round-trips random buffers of length 1..200", () => {60 for (let len = 1; len <= 200; len++) {61 const original = new Uint8Array(len);62 crypto.getRandomValues(original);63 const encoded = cjkEncode(original);64 const rb = remainingBits(len);65 const decoded = cjkDecode(encoded, rb);66 expect(decoded).toEqual(original);67 }68 });69});7071describe("remainingBits", () => {72 it("returns zero when bytes * 8 is a multiple of 14", () => {73 74 expect(remainingBits(7)).toBe(0);75 76 expect(remainingBits(14)).toBe(0);77 });7879 it("returns the non-zero remainder otherwise", () => {80 expect(remainingBits(1)).toBe(8);81 expect(remainingBits(2)).toBe(2);82 expect(remainingBits(3)).toBe(10);83 });84});8586describe("isValidPhraseCharset", () => {87 it("accepts the alphabet bounds", () => {88 expect(isValidPhraseCharset("\u4E00")).toBe(true);89 expect(isValidPhraseCharset("\u8DFF")).toBe(true);90 });9192 it("rejects characters just outside the alphabet", () => {93 expect(isValidPhraseCharset("\u4DFF")).toBe(false); 94 expect(isValidPhraseCharset("\u8E00")).toBe(false); 95 });9697 it("rejects non-CJK characters", () => {98 expect(isValidPhraseCharset("a")).toBe(false);99 expect(isValidPhraseCharset("一二 三")).toBe(false); 100 });101});102103describe("alphabet constants", () => {104 it("has 16,384 entries so each character packs exactly 14 bits", () => {105 expect(ALPHABET_SIZE).toBe(2 ** 14);106 expect(ALPHABET_END - ALPHABET_START + 1).toBe(ALPHABET_SIZE);107 });108});