import { beforeEach, describe, expect, it, vi } from "vitest"; import type { RuntimeEnv } from "../runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; const mocks = vi.hoisted(() => ({ loginOpenAICodex: vi.fn(), createVpsAwareOAuthHandlers: vi.fn(), })); vi.mock("@mariozechner/pi-ai", () => ({ loginOpenAICodex: mocks.loginOpenAICodex, })); vi.mock("./oauth-flow.js", () => ({ createVpsAwareOAuthHandlers: mocks.createVpsAwareOAuthHandlers, })); import { loginOpenAICodexOAuth } from "./openai-codex-oauth.js"; function createPrompter() { const spin = { update: vi.fn(), stop: vi.fn() }; const prompter: Pick = { note: vi.fn(async () => {}), progress: vi.fn(() => spin), }; return { prompter: prompter as unknown as WizardPrompter, spin }; } function createRuntime(): RuntimeEnv { return { log: vi.fn(), error: vi.fn(), exit: vi.fn((code: number) => { throw new Error(`exit:${code}`); }), }; } describe("loginOpenAICodexOAuth", () => { beforeEach(() => { vi.clearAllMocks(); }); it("returns credentials on successful oauth login", async () => { const creds = { provider: "openai-codex" as const, access: "access-token", refresh: "refresh-token", expires: Date.now() + 60_000, email: "user@example.com", }; mocks.createVpsAwareOAuthHandlers.mockReturnValue({ onAuth: vi.fn(), onPrompt: vi.fn(), }); mocks.loginOpenAICodex.mockResolvedValue(creds); const { prompter, spin } = createPrompter(); const runtime = createRuntime(); const result = await loginOpenAICodexOAuth({ prompter, runtime, isRemote: false, openUrl: async () => {}, }); expect(result).toEqual(creds); expect(mocks.loginOpenAICodex).toHaveBeenCalledOnce(); expect(spin.stop).toHaveBeenCalledWith("OpenAI OAuth complete"); expect(runtime.error).not.toHaveBeenCalled(); }); it("reports oauth errors and rethrows", async () => { mocks.createVpsAwareOAuthHandlers.mockReturnValue({ onAuth: vi.fn(), onPrompt: vi.fn(), }); mocks.loginOpenAICodex.mockRejectedValue(new Error("oauth failed")); const { prompter, spin } = createPrompter(); const runtime = createRuntime(); await expect( loginOpenAICodexOAuth({ prompter, runtime, isRemote: true, openUrl: async () => {}, }), ).rejects.toThrow("oauth failed"); expect(spin.stop).toHaveBeenCalledWith("OpenAI OAuth failed"); expect(runtime.error).toHaveBeenCalledWith(expect.stringContaining("oauth failed")); expect(prompter.note).toHaveBeenCalledWith( "Trouble with OAuth? See https://docs.openclaw.ai/start/faq", "OAuth help", ); }); });