import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { writeStore } from "../../auth/store.js"; import { setupTempAuthEnv, type EnvFixture } from "../../auth/_test-utils.js"; import { consumeCommandResult } from "../../utils/commandResult.js"; // Mock only AuthClient so the live /v3/users/me probe is controllable; // keep the real store/resolver/user helpers so the test exercises the // actual on-disk persisted-user-block surfacing. // - apiReject: throw ErrApi on getCurrentUser (simulates an API blip) // - user: the live identity returned on success const probeState = vi.hoisted( () => ({ apiReject: false, user: { email: "live@example.com" } }) as { apiReject: boolean; user: Record; }, ); vi.mock("../../auth/index.js", async (orig) => { const actual = await orig(); class MockAuthClient { async getCurrentUser(): Promise> { if (probeState.apiReject) { const { ErrApi } = await import("../../auth/errors.js"); throw ErrApi(503, "service unavailable"); } return probeState.user; } } return { ...actual, AuthClient: MockAuthClient }; }); describe("auth status — persisted user block surface", () => { let envFixture: EnvFixture; let stdout: string[]; beforeEach(async () => { envFixture = await setupTempAuthEnv("hf-status-"); probeState.apiReject = false; probeState.user = { email: "live@example.com" }; stdout = []; consumeCommandResult(); vi.spyOn(console, "log").mockImplementation((...args: unknown[]) => { stdout.push(args.join(" ")); }); vi.spyOn(console, "error").mockImplementation(() => {}); }); afterEach(async () => { consumeCommandResult(); vi.restoreAllMocks(); await envFixture.restore(); }); async function runStatus(asJson: boolean): Promise { const cmd = (await import("./status.js")).default; await (cmd.run as (ctx: { args: Record }) => Promise)({ args: { json: asJson }, }); return consumeCommandResult().exitCode; } function lastJson(): Record { return JSON.parse(stdout[stdout.length - 1] ?? "{}"); } it("surfaces the persisted user block (with resolved display_name) for a file credential", async () => { await writeStore({ api_key: "hg_x", user: { email: "jane@example.com", first_name: "Jane", last_name: "Doe", username: "jdoe" }, }); const code = await runStatus(true); expect(code).toBe(0); const payload = lastJson(); expect(payload["source"]).toBe("file_json"); expect(payload["persisted_user"]).toEqual({ email: "jane@example.com", first_name: "Jane", last_name: "Doe", username: "jdoe", display_name: "jane@example.com", }); }); it("backwards-compat: a file credential with no user block reports persisted_user: null", async () => { await writeStore({ api_key: "hg_legacy" }); const code = await runStatus(true); expect(code).toBe(0); const payload = lastJson(); expect(payload["persisted_user"]).toBeNull(); // The live `user` field (from the API probe) is unchanged / additive. expect(payload["user"]).toEqual({ email: "live@example.com" }); }); it("skips the persisted block for an env-sourced credential (could be a different key)", async () => { // Seed a file-side user block, then resolve via the env key — the // active credential is env, so the file block must NOT be surfaced. await writeStore({ api_key: "hg_file", user: { email: "file-user@example.com" } }); process.env["HEYGEN_API_KEY"] = "hg_env_key"; const code = await runStatus(true); expect(code).toBe(0); const payload = lastJson(); expect(payload["source"]).toBe("env"); expect(payload["persisted_user"]).toBeNull(); }); it("falls back to the cached identity in human output when the live probe fails", async () => { await writeStore({ api_key: "hg_x", user: { email: "cached@example.com" } }); probeState.apiReject = true; const code = await runStatus(false); expect(code).toBe(1); // API failure → non-zero exit const text = stdout.join("\n"); expect(text).toContain("API check failed"); expect(text).toContain("cached@example.com"); expect(text).toContain("cached"); }); });