import { spawnSync } from "node:child_process"; import { describe, expect, test } from "bun:test"; import { repoRoot } from "./config"; import { sshHelp, sshHelpScope } from "./help"; function runTransHelp(...args: string[]): string { const result = spawnSync("bun", ["scripts/ssh-cli.ts", ...args], { cwd: repoRoot, env: { ...process.env, UNIDESK_SSH_ENTRYPOINT: "trans", UNIDESK_TRANS_REPO_ROOT: repoRoot, }, encoding: "utf8", }); expect(result.status).toBe(0); expect(result.stderr).toBe(""); return result.stdout; } describe("ssh bounded progressive help", () => { test("selects exact help-first operation scopes", () => { expect(sshHelpScope(["ssh", "--help", "download"])).toBe("download"); expect(sshHelpScope(["ssh", "help", "apply-patch"])).toBe("apply-patch"); expect(sshHelpScope(["ssh", "--help", "ps"])).toBe("ps"); expect(sshHelpScope(["ssh", "--help"])).toBeUndefined(); expect(sshHelpScope(["ssh", "--help", "unknown"])).toBeUndefined(); expect(sshHelpScope(["ssh", "NC01:k3s", "--help"])).toBeUndefined(); expect(sshHelpScope(["ssh", "--help", "download", "extra"])).toBeUndefined(); }); test("keeps the top-level model compact while indexing routes and every scoped operation", () => { const top = sshHelp() as { routeSyntax: Record; operationGroups: Record; scopedHelp: { command: string; availableOperationCount: number }; outputTruncated?: boolean; }; const serialized = JSON.stringify(top); const grouped = Object.values(top.operationGroups).flatMap((value) => value.split(" | ")); expect(Buffer.byteLength(serialized, "utf8")).toBeLessThan(6_144); expect(top.outputTruncated).toBeUndefined(); expect(top.routeSyntax.general).toContain(" "); expect(top.routeSyntax.k3sWorkload).toContain("::"); expect(top.routeSyntax.windowsWorkspace).toContain(":win"); expect(top.operationGroups.patch).toBe("apply-patch | apply-patch-v1"); expect(top.operationGroups.transfer).toBe("upload | download"); expect(new Set(grouped).size).toBe(top.scopedHelp.availableOperationCount); expect(top.scopedHelp.command).toContain("--help "); for (const operation of grouped) { const scope = sshHelpScope(["ssh", "--help", operation]); expect(scope).toBe(operation); expect(Buffer.byteLength(JSON.stringify(sshHelp(scope)), "utf8")).toBeLessThan(6_144); } }); test("keeps download help bounded and discloses the executing source root", () => { const scoped = sshHelp("download") as { scope: string; runtime: { repoRoot: string; cliPath: string; cwdSelectsRepoRoot: boolean; rootSelection: { environmentVariable: string } }; usage: string[]; examples: Record; contract: { pathOrder: string[]; transport: string; verification: { automatic: boolean } }; }; const serialized = JSON.stringify(scoped); expect(scoped.scope).toBe("download"); expect(scoped.usage.length).toBeLessThanOrEqual(4); expect(Buffer.byteLength(serialized, "utf8")).toBeLessThan(6_144); expect(serialized).not.toContain("git exact-commit"); expect(scoped.runtime.repoRoot).toBe(repoRoot); expect(scoped.runtime.cliPath).toBe(`${repoRoot}/scripts/ssh-cli.ts`); expect(scoped.runtime.cwdSelectsRepoRoot).toBe(false); expect(scoped.runtime.rootSelection.environmentVariable).toBe("UNIDESK_TRANS_REPO_ROOT"); expect(scoped.contract.pathOrder).toEqual(["remote-file", "local-file"]); expect(scoped.contract.transport).toBe("host.ssh.tcp-pool"); expect(scoped.contract.verification.automatic).toBe(true); expect(scoped.usage[0]).toBe("trans cat "); expect(serialized).toContain("text-transfer-discouraged"); expect(serialized).toContain("--allow-text-transfer"); expect(scoped.examples.host).toContain("download /tmp/firmware.bin ./firmware.bin"); expect(scoped.examples.k3sWorkload).toBe( "trans NC01:k3s:hwlab-v03:hwlab-cloud-api:hwlab-cloud-api download /tmp/runtime-bundle.tar.gz ./runtime-bundle.tar.gz", ); expect(scoped.examples.windowsDrive).toContain("D:\\tmp\\capture.zip"); expect(scoped.examples.generatedTextOverride).toBe( "trans D601:/tmp download --allow-text-transfer /tmp/trace.json ./trace.json", ); expect(scoped.examples.host).not.toMatch(/\.(?:json|md|txt|ya?ml)\b/); expect(scoped.examples.k3sWorkload).not.toMatch(/\.(?:json|md|txt|ya?ml)\b/); expect(scoped.examples.windowsDrive).not.toMatch(/\.(?:json|md|txt|ya?ml)\b/); }); test("renders compact top-level and scoped help without dump fallback", () => { const top = runTransHelp("--help"); expect(Buffer.byteLength(top, "utf8")).toBeLessThan(2_048); expect(top.trim().split("\n")).toHaveLength(11); expect(top).toContain("usage: trans "); expect(top).toContain("transfer: upload | download"); expect(top).toContain("scoped help: trans --help "); expect(top).toContain("cwd: host/workspace uses :/absolute/workspace; k3s exec uses --cwd"); expect(top).not.toContain("outputTruncated"); expect(top).not.toContain("dump"); const routeTop = runTransHelp("NC01:k3s", "--help"); expect(routeTop).toBe(top); const applyPatch = runTransHelp("--help", "apply-patch"); expect(applyPatch.trim().split("\n").length).toBeLessThanOrEqual(8); expect(applyPatch).toContain("TRANS HELP apply-patch"); expect(applyPatch).toContain("usage: trans apply-patch <<'PATCH'"); expect(applyPatch).not.toContain("patch.diff"); expect(applyPatch).not.toContain("outputTruncated"); const exec = runTransHelp("--help", "exec"); expect(exec).toContain("trans :/absolute/workspace exec "); expect(exec).toContain("k3s::"); const download = runTransHelp("--help", "download"); expect(download.trim().split("\n").length).toBeLessThanOrEqual(8); expect(download).toContain("TRANS HELP download"); expect(download.indexOf("cat ")).toBeLessThan(download.indexOf("download ")); expect(download).toContain("download "); expect(download).not.toContain("outputTruncated"); const upload = runTransHelp("--help", "upload"); expect(upload.trim().split("\n").length).toBeLessThanOrEqual(8); expect(upload.indexOf("apply-patch <<'PATCH'")).toBeLessThan(upload.indexOf("upload ")); expect(upload).toContain("text-transfer-discouraged"); }); test("returns the existing bounded JSON model only for explicit machine output", () => { const top = JSON.parse(runTransHelp("--help", "--json")) as { ok: boolean; command: string; data: { command: string; output: string; routeSyntax: Record; dump?: unknown }; }; expect(top.ok).toBe(true); expect(top.command).toBe("trans --help --json"); expect(top.data.command).toBe("trans --help"); expect(top.data.output).toBe("json"); expect(top.data.routeSyntax.general).toContain(" "); expect(top.data.dump).toBeUndefined(); const prefixed = JSON.parse(runTransHelp("--json", "--help")) as typeof top; expect(prefixed.ok).toBe(true); expect(prefixed.data.command).toBe("trans --help"); const topOutput = JSON.parse(runTransHelp("--help", "-o", "json")) as typeof top; expect(topOutput.ok).toBe(true); expect(topOutput.data.output).toBe("json"); const scoped = JSON.parse(runTransHelp("--help", "download", "-o", "json")) as { ok: boolean; data: { command: string; output: string; scope: string; runtime: { repoRoot: string }; dump?: unknown }; }; expect(scoped.ok).toBe(true); expect(scoped.data.command).toBe("trans --help download"); expect(scoped.data.output).toBe("json"); expect(scoped.data.scope).toBe("download"); expect(scoped.data.runtime.repoRoot).toBe(repoRoot); expect(scoped.data.dump).toBeUndefined(); const routeTop = JSON.parse(runTransHelp("NC01:k3s", "--help", "--json")) as typeof top; expect(routeTop.ok).toBe(true); expect(routeTop.data.command).toBe("trans --help"); }); test("returns compact no-stack input errors for unknown help scopes", () => { const result = spawnSync("bun", ["scripts/ssh-cli.ts", "--help", "unknown-operation"], { cwd: repoRoot, env: { ...process.env, UNIDESK_SSH_ENTRYPOINT: "trans", UNIDESK_TRANS_REPO_ROOT: repoRoot }, encoding: "utf8", }); expect(result.status).toBe(1); expect(result.stderr).toBe(""); expect(result.stdout.trim().split("\n").length).toBeLessThanOrEqual(8); expect(result.stdout).toContain("ERROR cli-input/unknown-help-scope"); expect(result.stdout).not.toContain("stack"); expect(result.stdout).not.toContain(" at "); }); test("rejects the moved root entrypoint locally with one exact replacement", () => { const result = spawnSync("bun", ["scripts/cli.ts", "trans", "NC01:/root/unidesk", "git", "status", "--short"], { cwd: repoRoot, env: { ...process.env, UNIDESK_TRANS_REPO_ROOT: repoRoot }, encoding: "utf8", }); expect(result.status).toBe(1); expect(result.stderr).toBe(""); expect(result.stdout).toContain("ERROR cli-input/trans-root-cli-entrypoint-moved"); expect(result.stdout).toContain("usage: trans NC01:/root/unidesk git status --short"); expect(result.stdout).not.toContain("stack"); }); });