import { describe, it, expect, spyOn, afterEach } from "bun:test" import * as gh from "./gh" describe("gh cli check", () => { describe("getGhCliInfo", () => { function createProc(opts: { stdout?: string; stderr?: string; exitCode?: number }) { const stdoutText = opts.stdout ?? "" const stderrText = opts.stderr ?? "" const exitCode = opts.exitCode ?? 0 const encoder = new TextEncoder() return { stdout: new ReadableStream({ start(controller) { if (stdoutText) controller.enqueue(encoder.encode(stdoutText)) controller.close() }, }), stderr: new ReadableStream({ start(controller) { if (stderrText) controller.enqueue(encoder.encode(stderrText)) controller.close() }, }), exited: Promise.resolve(exitCode), exitCode, } as unknown as ReturnType } it("returns gh cli info structure", async () => { const spawnSpy = spyOn(Bun, "spawn").mockImplementation((cmd) => { if (Array.isArray(cmd) && (cmd[0] === "which" || cmd[0] === "where") && cmd[1] === "gh") { return createProc({ stdout: "/usr/bin/gh\n" }) } if (Array.isArray(cmd) && cmd[0] === "gh" && cmd[1] === "--version") { return createProc({ stdout: "gh version 2.40.0\n" }) } if (Array.isArray(cmd) && cmd[0] === "gh" && cmd[1] === "auth" && cmd[2] === "status") { return createProc({ exitCode: 0, stderr: "Logged in to github.com account octocat (keyring)\nToken scopes: 'repo', 'read:org'\n", }) } throw new Error(`Unexpected Bun.spawn call: ${Array.isArray(cmd) ? cmd.join(" ") : String(cmd)}`) }) try { const info = await gh.getGhCliInfo() expect(info.installed).toBe(true) expect(info.version).toBe("2.40.0") expect(typeof info.authenticated).toBe("boolean") expect(Array.isArray(info.scopes)).toBe(true) } finally { spawnSpy.mockRestore() } }) }) describe("checkGhCli", () => { let getInfoSpy: ReturnType afterEach(() => { getInfoSpy?.mockRestore() }) it("returns warn when gh is not installed", async () => { // given gh not installed getInfoSpy = spyOn(gh, "getGhCliInfo").mockResolvedValue({ installed: false, version: null, path: null, authenticated: false, username: null, scopes: [], error: null, }) // when checking const result = await gh.checkGhCli() // then should warn (optional) expect(result.status).toBe("warn") expect(result.message).toContain("Not installed") expect(result.details).toContain("Install: https://cli.github.com/") }) it("returns warn when gh is installed but not authenticated", async () => { // given gh installed but not authenticated getInfoSpy = spyOn(gh, "getGhCliInfo").mockResolvedValue({ installed: true, version: "2.40.0", path: "/usr/local/bin/gh", authenticated: false, username: null, scopes: [], error: "not logged in", }) // when checking const result = await gh.checkGhCli() // then should warn about auth expect(result.status).toBe("warn") expect(result.message).toContain("2.40.0") expect(result.message).toContain("not authenticated") expect(result.details).toContain("Authenticate: gh auth login") }) it("returns pass when gh is installed and authenticated", async () => { // given gh installed and authenticated getInfoSpy = spyOn(gh, "getGhCliInfo").mockResolvedValue({ installed: true, version: "2.40.0", path: "/usr/local/bin/gh", authenticated: true, username: "octocat", scopes: ["repo", "read:org"], error: null, }) // when checking const result = await gh.checkGhCli() // then should pass expect(result.status).toBe("pass") expect(result.message).toContain("2.40.0") expect(result.message).toContain("octocat") expect(result.details).toContain("Account: octocat") expect(result.details).toContain("Scopes: repo, read:org") }) }) describe("getGhCliCheckDefinition", () => { it("returns correct check definition", () => { // given // when getting definition const def = gh.getGhCliCheckDefinition() // then should have correct properties expect(def.id).toBe("gh-cli") expect(def.name).toBe("GitHub CLI") expect(def.category).toBe("tools") expect(def.critical).toBe(false) expect(typeof def.check).toBe("function") }) }) })