diff --git a/src/commands/auth-choice.apply.huggingface.test.ts b/src/commands/auth-choice.apply.huggingface.test.ts index 04e7b296d5a..fd881e86bdd 100644 --- a/src/commands/auth-choice.apply.huggingface.test.ts +++ b/src/commands/auth-choice.apply.huggingface.test.ts @@ -1,13 +1,14 @@ import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { WizardPrompter } from "../wizard/prompts.js"; import { captureEnv } from "../test-utils/env.js"; import { applyAuthChoiceHuggingface } from "./auth-choice.apply.huggingface.js"; -import { createExitThrowingRuntime, createWizardPrompter } from "./test-wizard-helpers.js"; - -const authProfilePathFor = (agentDir: string) => path.join(agentDir, "auth-profiles.json"); +import { + createExitThrowingRuntime, + createWizardPrompter, + readAuthProfilesForAgent, + setupAuthTestEnv, +} from "./test-wizard-helpers.js"; function createHuggingfacePrompter(params: { text: WizardPrompter["text"]; @@ -25,9 +26,27 @@ function createHuggingfacePrompter(params: { } describe("applyAuthChoiceHuggingface", () => { - const envSnapshot = captureEnv(["OPENCLAW_AGENT_DIR", "HF_TOKEN", "HUGGINGFACE_HUB_TOKEN"]); + const envSnapshot = captureEnv([ + "OPENCLAW_STATE_DIR", + "OPENCLAW_AGENT_DIR", + "PI_CODING_AGENT_DIR", + "HF_TOKEN", + "HUGGINGFACE_HUB_TOKEN", + ]); let tempStateDir: string | null = null; + async function setupTempState() { + const env = await setupAuthTestEnv("openclaw-hf-"); + tempStateDir = env.stateDir; + return env.agentDir; + } + + async function readAuthProfiles(agentDir: string) { + return await readAuthProfilesForAgent<{ + profiles?: Record; + }>(agentDir); + } + afterEach(async () => { if (tempStateDir) { await fs.rm(tempStateDir, { recursive: true, force: true }); @@ -41,17 +60,14 @@ describe("applyAuthChoiceHuggingface", () => { authChoice: "openrouter-api-key", config: {}, prompter: {} as WizardPrompter, - runtime: {} as RuntimeEnv, + runtime: createExitThrowingRuntime(), setDefaultModel: false, }); expect(result).toBeNull(); }); it("prompts for key and model, then writes config and auth profile", async () => { - tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-hf-")); - const agentDir = path.join(tempStateDir, "agent"); - process.env.OPENCLAW_AGENT_DIR = agentDir; - await fs.mkdir(agentDir, { recursive: true }); + const agentDir = await setupTempState(); const text = vi.fn().mockResolvedValue("hf-test-token"); const select: WizardPrompter["select"] = vi.fn( @@ -81,21 +97,14 @@ describe("applyAuthChoiceHuggingface", () => { expect.objectContaining({ message: "Default Hugging Face model" }), ); - const authProfilePath = authProfilePathFor(agentDir); - const raw = await fs.readFile(authProfilePath, "utf8"); - const parsed = JSON.parse(raw) as { - profiles?: Record; - }; + const parsed = await readAuthProfiles(agentDir); expect(parsed.profiles?.["huggingface:default"]?.key).toBe("hf-test-token"); }); it("does not prompt to reuse env token when opts.token already provided", async () => { - tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-hf-")); - const agentDir = path.join(tempStateDir, "agent"); - process.env.OPENCLAW_AGENT_DIR = agentDir; + const agentDir = await setupTempState(); process.env.HF_TOKEN = "hf-env-token"; delete process.env.HUGGINGFACE_HUB_TOKEN; - await fs.mkdir(agentDir, { recursive: true }); const text = vi.fn().mockResolvedValue("hf-text-token"); const select: WizardPrompter["select"] = vi.fn( @@ -121,11 +130,7 @@ describe("applyAuthChoiceHuggingface", () => { expect(confirm).not.toHaveBeenCalled(); expect(text).not.toHaveBeenCalled(); - const authProfilePath = authProfilePathFor(agentDir); - const raw = await fs.readFile(authProfilePath, "utf8"); - const parsed = JSON.parse(raw) as { - profiles?: Record; - }; + const parsed = await readAuthProfiles(agentDir); expect(parsed.profiles?.["huggingface:default"]?.key).toBe("hf-opts-token"); }); }); diff --git a/src/commands/auth-choice.moonshot.e2e.test.ts b/src/commands/auth-choice.moonshot.e2e.test.ts index e997aed3ca9..1dfc392a7ba 100644 --- a/src/commands/auth-choice.moonshot.e2e.test.ts +++ b/src/commands/auth-choice.moonshot.e2e.test.ts @@ -1,13 +1,15 @@ import fs from "node:fs/promises"; -import os from "node:os"; -import path from "node:path"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { WizardPrompter } from "../wizard/prompts.js"; import { captureEnv } from "../test-utils/env.js"; import { applyAuthChoice } from "./auth-choice.js"; -import { createExitThrowingRuntime, createWizardPrompter } from "./test-wizard-helpers.js"; +import { + createExitThrowingRuntime, + createWizardPrompter, + readAuthProfilesForAgent, + setupAuthTestEnv, +} from "./test-wizard-helpers.js"; -const authProfilePathFor = (agentDir: string) => path.join(agentDir, "auth-profiles.json"); const requireAgentDir = () => { const agentDir = process.env.OPENCLAW_AGENT_DIR; if (!agentDir) { @@ -30,13 +32,17 @@ describe("applyAuthChoice (moonshot)", () => { let tempStateDir: string | null = null; async function setupTempState() { - tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-")); - process.env.OPENCLAW_STATE_DIR = tempStateDir; - process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "agent"); - process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_AGENT_DIR; + const env = await setupAuthTestEnv("openclaw-auth-"); + tempStateDir = env.stateDir; delete process.env.MOONSHOT_API_KEY; } + async function readAuthProfiles() { + return await readAuthProfilesForAgent<{ + profiles?: Record; + }>(requireAgentDir()); + } + afterEach(async () => { if (tempStateDir) { await fs.rm(tempStateDir, { recursive: true, force: true }); @@ -73,11 +79,7 @@ describe("applyAuthChoice (moonshot)", () => { expect(result.config.models?.providers?.moonshot?.baseUrl).toBe("https://api.moonshot.cn/v1"); expect(result.agentModelOverride).toBe("moonshot/kimi-k2.5"); - const authProfilePath = authProfilePathFor(requireAgentDir()); - const raw = await fs.readFile(authProfilePath, "utf8"); - const parsed = JSON.parse(raw) as { - profiles?: Record; - }; + const parsed = await readAuthProfiles(); expect(parsed.profiles?.["moonshot:default"]?.key).toBe("sk-moonshot-cn-test"); }); @@ -100,11 +102,7 @@ describe("applyAuthChoice (moonshot)", () => { expect(result.config.models?.providers?.moonshot?.baseUrl).toBe("https://api.moonshot.cn/v1"); expect(result.agentModelOverride).toBeUndefined(); - const authProfilePath = authProfilePathFor(requireAgentDir()); - const raw = await fs.readFile(authProfilePath, "utf8"); - const parsed = JSON.parse(raw) as { - profiles?: Record; - }; + const parsed = await readAuthProfiles(); expect(parsed.profiles?.["moonshot:default"]?.key).toBe("sk-moonshot-cn-test"); }); }); diff --git a/src/commands/onboard-auth.e2e.test.ts b/src/commands/onboard-auth.e2e.test.ts index 7a9c931ceef..b9ede009468 100644 --- a/src/commands/onboard-auth.e2e.test.ts +++ b/src/commands/onboard-auth.e2e.test.ts @@ -1,6 +1,5 @@ import type { OAuthCredentials } from "@mariozechner/pi-ai"; import fs from "node:fs/promises"; -import os from "node:os"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; import { captureEnv } from "../test-utils/env.js"; @@ -30,15 +29,7 @@ import { ZAI_CODING_CN_BASE_URL, ZAI_GLOBAL_BASE_URL, } from "./onboard-auth.js"; - -const authProfilePathFor = (agentDir: string) => path.join(agentDir, "auth-profiles.json"); -const requireAgentDir = () => { - const agentDir = process.env.OPENCLAW_AGENT_DIR; - if (!agentDir) { - throw new Error("OPENCLAW_AGENT_DIR not set"); - } - return agentDir; -}; +import { readAuthProfilesForAgent, setupAuthTestEnv } from "./test-wizard-helpers.js"; function createLegacyProviderConfig(params: { providerId: string; @@ -88,10 +79,8 @@ describe("writeOAuthCredentials", () => { }); it("writes auth-profiles.json under OPENCLAW_AGENT_DIR when set", async () => { - tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-oauth-")); - process.env.OPENCLAW_STATE_DIR = tempStateDir; - process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "agent"); - process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_AGENT_DIR; + const env = await setupAuthTestEnv("openclaw-oauth-"); + tempStateDir = env.stateDir; const creds = { refresh: "refresh-token", @@ -101,11 +90,9 @@ describe("writeOAuthCredentials", () => { await writeOAuthCredentials("openai-codex", creds); - const authProfilePath = authProfilePathFor(requireAgentDir()); - const raw = await fs.readFile(authProfilePath, "utf8"); - const parsed = JSON.parse(raw) as { + const parsed = await readAuthProfilesForAgent<{ profiles?: Record; - }; + }>(env.agentDir); expect(parsed.profiles?.["openai-codex:default"]).toMatchObject({ refresh: "refresh-token", access: "access-token", @@ -135,18 +122,14 @@ describe("setMinimaxApiKey", () => { }); it("writes to OPENCLAW_AGENT_DIR when set", async () => { - tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-minimax-")); - process.env.OPENCLAW_STATE_DIR = tempStateDir; - process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "custom-agent"); - process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_AGENT_DIR; + const env = await setupAuthTestEnv("openclaw-minimax-", { agentSubdir: "custom-agent" }); + tempStateDir = env.stateDir; await setMinimaxApiKey("sk-minimax-test"); - const customAuthPath = authProfilePathFor(requireAgentDir()); - const raw = await fs.readFile(customAuthPath, "utf8"); - const parsed = JSON.parse(raw) as { + const parsed = await readAuthProfilesForAgent<{ profiles?: Record; - }; + }>(env.agentDir); expect(parsed.profiles?.["minimax:default"]).toMatchObject({ type: "api_key", provider: "minimax", diff --git a/src/commands/test-wizard-helpers.ts b/src/commands/test-wizard-helpers.ts index 71f85759e28..6394ec99898 100644 --- a/src/commands/test-wizard-helpers.ts +++ b/src/commands/test-wizard-helpers.ts @@ -1,6 +1,9 @@ +import fs from "node:fs/promises"; +import path from "node:path"; import { vi } from "vitest"; import type { RuntimeEnv } from "../runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; +import { makeTempWorkspace } from "../test-helpers/workspace.js"; export const noopAsync = async () => {}; export const noop = () => {}; @@ -31,3 +34,24 @@ export function createWizardPrompter( ...overrides, }; } + +export async function setupAuthTestEnv( + prefix = "openclaw-auth-", + options?: { agentSubdir?: string }, +): Promise<{ + stateDir: string; + agentDir: string; +}> { + const stateDir = await makeTempWorkspace(prefix); + const agentDir = path.join(stateDir, options?.agentSubdir ?? "agent"); + process.env.OPENCLAW_STATE_DIR = stateDir; + process.env.OPENCLAW_AGENT_DIR = agentDir; + process.env.PI_CODING_AGENT_DIR = agentDir; + await fs.mkdir(agentDir, { recursive: true }); + return { stateDir, agentDir }; +} + +export async function readAuthProfilesForAgent(agentDir: string): Promise { + const raw = await fs.readFile(path.join(agentDir, "auth-profiles.json"), "utf8"); + return JSON.parse(raw) as T; +}