import { describe, expect, test } from "bun:test"; import { PURPOSE_MAX, isProjectPurpose, projectPurpose, projectRelPath, sanitizePurpose, } from "../src/core/purpose.ts"; describe("sanitizePurpose (must match the fork)", () => { test("keeps the fork's allowed set", () => { expect(sanitizePurpose("a-b_c.d:e/f")).toBe("a-b_c.d:e/f"); }); // The fork's own doc uses this exact example. test("strips shell metacharacters", () => { expect(sanitizePurpose('rm -rf /; echo "x"')).toBe("rm-rf/echox"); }); test("strips non-ascii", () => { expect(sanitizePurpose("café–π")).toBe("caf"); }); test("truncates to the fork's limit", () => { expect(sanitizePurpose("x".repeat(200))).toHaveLength(PURPOSE_MAX); }); }); describe("projectPurpose", () => { test("is the plain relative path for ordinary names", () => { expect(projectPurpose("sl0ppi")).toBe("project:sl0ppi"); expect(projectPurpose("group/nested")).toBe("project:group/nested"); }); test("never exceeds what the fork will store", () => { for (const name of ["ok", "a".repeat(200), "wéîrd name!", "x/".repeat(50)]) { expect(projectPurpose(name).length).toBeLessThanOrEqual(PURPOSE_MAX); } }); test("survives the fork's sanitiser unchanged (round-trip through the terminal)", () => { for (const name of ["ok", "a".repeat(200), "wéîrd name!", "dots...", "x/".repeat(50)]) { const purpose = projectPurpose(name); expect(sanitizePurpose(purpose)).toBe(purpose); } }); test("distinct projects get distinct purposes, even when truncated", () => { const a = projectPurpose(`${"a".repeat(80)}/one`); const b = projectPurpose(`${"a".repeat(80)}/two`); expect(a).not.toBe(b); }); test("is stable across calls", () => { expect(projectPurpose("foo")).toBe(projectPurpose("foo")); expect(projectPurpose("wéîrd name!")).toBe(projectPurpose("wéîrd name!")); }); }); describe("projectRelPath", () => { test("reconstructs an ordinary path", () => { expect(projectRelPath("project:sl0ppi")).toBe("sl0ppi"); expect(projectRelPath("project:group/nested")).toBe("group/nested"); }); test("refuses to reconstruct a truncated/hashed purpose", () => { expect(projectRelPath(projectPurpose("a".repeat(200)))).toBeNull(); }); test("refuses non-project purposes", () => { expect(projectRelPath("agent:main")).toBeNull(); expect(projectRelPath("project:")).toBeNull(); }); test("round-trips every reconstructable name", () => { for (const name of ["foo", "group/nested", "dots.in.name", "a-b_c"]) { expect(projectRelPath(projectPurpose(name))).toBe(name); } }); }); // The safety invariant, fuzzed. Closing a tab is destructive, so the one thing // projectRelPath must never do is return a path that is not the original. // Wrong answers close the wrong tab; refusing (null) only leaves a stale one. describe("projectRelPath never lies (fuzz)", () => { const alphabet = "abzAZ09_-./: !@#\u00e9\u4e2d\"';"; function randomName(rng: () => number): string { const len = 1 + Math.floor(rng() * 90); let out = ""; for (let i = 0; i < len; i++) { out += alphabet[Math.floor(rng() * alphabet.length)]; } return out; } // Deterministic PRNG so a failure is reproducible. function mulberry32(seed: number): () => number { return () => { seed = (seed + 0x6d2b79f5) | 0; let t = Math.imul(seed ^ (seed >>> 15), 1 | seed); t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t; return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } test("reconstructs exactly, or refuses — never something else", () => { const rng = mulberry32(0x510991); for (let i = 0; i < 5000; i++) { const name = randomName(rng); const purpose = projectPurpose(name); expect(purpose.length).toBeLessThanOrEqual(PURPOSE_MAX); expect(sanitizePurpose(purpose)).toBe(purpose); const back = projectRelPath(purpose); if (back !== null) expect(back).toBe(name); } }); test("distinct names never collide on a purpose", () => { const rng = mulberry32(12345); const seen = new Map(); for (let i = 0; i < 5000; i++) { const name = randomName(rng); const purpose = projectPurpose(name); const prior = seen.get(purpose); if (prior !== undefined) expect(prior).toBe(name); seen.set(purpose, name); } }); }); describe("isProjectPurpose", () => { test("classifies", () => { expect(isProjectPurpose("project:foo")).toBe(true); expect(isProjectPurpose("agent:main")).toBe(false); expect(isProjectPurpose(undefined)).toBe(false); }); });