import { existsSync, readFileSync, readdirSync, rmSync } from "node:fs"; import { spawnSync } from "node:child_process"; import { describe, expect, test } from "bun:test"; import { repoRoot } from "./config"; const dumpDir = "/tmp/unidesk-cli-output"; function runOversizedIssueOutput(token: string, explicit = false): { status: number | null; stdout: string; stderr: string } { const command = `gh issue view 1890 --repo pikasTech/unidesk --json body ${token}`; const script = [ "import { emitJson } from './scripts/src/output.ts';", `emitJson(${JSON.stringify(command)}, {`, " command: 'issue view',", " repo: 'pikasTech/unidesk',", " issue: { number: 1890, title: 'large body', state: 'open', url: 'https://example.invalid/1890', body: 'x'.repeat(30000) },", "});", ].join("\n"); const args = ["-e", script, "output-test", ...(explicit ? ["--full"] : [])]; const result = spawnSync("bun", args, { cwd: repoRoot, encoding: "utf8" }); return { status: result.status, stdout: result.stdout, stderr: result.stderr }; } function matchingDumps(token: string): string[] { if (!existsSync(dumpDir)) return []; return readdirSync(dumpDir).filter((name) => name.includes(token)); } describe("CLI oversized output disclosure", () => { test("default oversized output creates no dump and gives the semantic gh text route", () => { const token = `nodump-${process.pid}-${Date.now()}`; expect(matchingDumps(token)).toEqual([]); const result = runOversizedIssueOutput(token); expect(result.status).toBe(0); expect(result.stderr).toBe(""); const payload = JSON.parse(result.stdout) as { data: { outputTruncated: boolean; dump?: unknown; next: string[]; disclosurePolicy: Record } }; expect(payload.data.outputTruncated).toBe(true); expect(payload.data.dump).toBeUndefined(); expect(payload.data.disclosurePolicy).toMatchObject({ defaultCreatesDump: false, dumpCreated: false }); expect(payload.data.next[0]).toBe("trans gh:/pikasTech/unidesk/issue/1890 cat"); expect(matchingDumps(token)).toEqual([]); }); test("explicit full disclosure keeps a protected complete dump", () => { const token = `fulldump-${process.pid}-${Date.now()}`; expect(matchingDumps(token)).toEqual([]); const result = runOversizedIssueOutput(token, true); expect(result.status).toBe(0); expect(result.stderr).toBe(""); const payload = JSON.parse(result.stdout) as { data: { dump: { path: string }; disclosurePolicy: Record } }; expect(payload.data.disclosurePolicy).toMatchObject({ defaultCreatesDump: false, dumpCreated: true }); expect(existsSync(payload.data.dump.path)).toBe(true); expect(readFileSync(payload.data.dump.path, "utf8")).toContain(token); rmSync(payload.data.dump.path, { force: true }); expect(matchingDumps(token)).toEqual([]); }); });