import { afterEach, describe, expect, it } from "vitest" import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs" import { tmpdir } from "node:os" import { join } from "node:path" import { credentialSearchDirs, resolveGreenptKey } from "../src/credential" const created: string[] = [] function tempDir(): string { const dir = mkdtempSync(join(tmpdir(), "greenpt-cred-")) created.push(dir) return dir } function writeAuth(dir: string, content: unknown): void { writeFileSync(join(dir, "auth.json"), JSON.stringify(content)) } afterEach(() => { while (created.length > 0) { const dir = created.pop() if (dir) rmSync(dir, { recursive: true, force: true }) } }) describe("resolveGreenptKey", () => { it("prefers the explicit apiKey option", () => { const dir = tempDir() writeAuth(dir, { greenpt: { type: "api", key: "from-auth" } }) const result = resolveGreenptKey({ apiKey: "from-option" }, dir, { GREENPT_API_KEY: "from-env" }) expect(result).toEqual({ ok: true, key: "from-option" }) }) it("prefers the environment variable over the auth store", () => { const dir = tempDir() writeAuth(dir, { greenpt: { type: "api", key: "from-auth" } }) const result = resolveGreenptKey({}, dir, { GREENPT_API_KEY: "from-env" }) expect(result).toEqual({ ok: true, key: "from-env" }) }) it("reads the active auth store when nothing else is set", () => { const dir = tempDir() writeAuth(dir, { greenpt: { type: "api", key: "from-auth" } }) const result = resolveGreenptKey({}, dir, {}) expect(result).toEqual({ ok: true, key: "from-auth" }) }) it("ignores OAuth entries", () => { const dir = tempDir() writeAuth(dir, { greenpt: { type: "oauth", access: "token" } }) const result = resolveGreenptKey({}, dir, {}) expect(result).toEqual({ ok: false, reason: "missing" }) }) it("ignores empty keys", () => { const dir = tempDir() writeAuth(dir, { greenpt: { type: "api", key: "" } }) const result = resolveGreenptKey({}, dir, {}) expect(result).toEqual({ ok: false, reason: "missing" }) }) it("returns missing when no credential is available", () => { const dir = tempDir() const result = resolveGreenptKey({}, dir, {}) expect(result).toEqual({ ok: false, reason: "missing" }) }) it("falls back to XDG data home", () => { const xdg = tempDir() const stateDir = join(xdg, "opencode") mkdirSync(stateDir) writeAuth(stateDir, { greenpt: { type: "api", key: "from-xdg" } }) const result = resolveGreenptKey({}, undefined, { XDG_DATA_HOME: xdg }) expect(result).toEqual({ ok: true, key: "from-xdg" }) }) }) describe("credentialSearchDirs", () => { it("puts the state dir first and de-duplicates", () => { const dirs = credentialSearchDirs("/state", { XDG_DATA_HOME: "/xdg", OPENCODE_DATA_DIR: "/state", }) expect(dirs[0]).toBe("/state") expect(dirs).toContain(join("/xdg", "opencode")) expect(new Set(dirs).size).toBe(dirs.length) }) it("falls back to ~/.local/share/opencode", () => { const dirs = credentialSearchDirs(undefined, { HOME: "/home/tester" }) expect(dirs).toContain(join("/home/tester", ".local", "share", "opencode")) }) })