diff --git a/src/commands/auth-choice.apply.huggingface.test.ts b/src/commands/auth-choice.apply.huggingface.test.ts index fd881e86bd..7cf1ebc96d 100644 --- a/src/commands/auth-choice.apply.huggingface.test.ts +++ b/src/commands/auth-choice.apply.huggingface.test.ts @@ -1,9 +1,8 @@ -import fs from "node:fs/promises"; 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 { + createAuthTestLifecycle, createExitThrowingRuntime, createWizardPrompter, readAuthProfilesForAgent, @@ -26,18 +25,17 @@ function createHuggingfacePrompter(params: { } describe("applyAuthChoiceHuggingface", () => { - const envSnapshot = captureEnv([ + const lifecycle = createAuthTestLifecycle([ "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; + lifecycle.setStateDir(env.stateDir); return env.agentDir; } @@ -48,11 +46,7 @@ describe("applyAuthChoiceHuggingface", () => { } afterEach(async () => { - if (tempStateDir) { - await fs.rm(tempStateDir, { recursive: true, force: true }); - tempStateDir = null; - } - envSnapshot.restore(); + await lifecycle.cleanup(); }); it("returns null when authChoice is not huggingface-api-key", async () => { diff --git a/src/commands/auth-choice.e2e.test.ts b/src/commands/auth-choice.e2e.test.ts index 3591e2c6bd..1e5843b6c4 100644 --- a/src/commands/auth-choice.e2e.test.ts +++ b/src/commands/auth-choice.e2e.test.ts @@ -2,7 +2,6 @@ import fs from "node:fs/promises"; import { afterEach, describe, expect, it, vi } from "vitest"; import type { WizardPrompter } from "../wizard/prompts.js"; import type { AuthChoice } from "./onboard-types.js"; -import { captureEnv } from "../test-utils/env.js"; import { applyAuthChoice, resolvePreferredProviderForAuthChoice } from "./auth-choice.js"; import { MINIMAX_CN_API_BASE_URL, @@ -11,6 +10,7 @@ import { } from "./onboard-auth.js"; import { authProfilePathForAgent, + createAuthTestLifecycle, createExitThrowingRuntime, createWizardPrompter, readAuthProfilesForAgent, @@ -43,7 +43,7 @@ type StoredAuthProfile = { }; describe("applyAuthChoice", () => { - const envSnapshot = captureEnv([ + const lifecycle = createAuthTestLifecycle([ "OPENCLAW_STATE_DIR", "OPENCLAW_AGENT_DIR", "PI_CODING_AGENT_DIR", @@ -57,10 +57,9 @@ describe("applyAuthChoice", () => { "SSH_TTY", "CHUTES_CLIENT_ID", ]); - let tempStateDir: string | null = null; async function setupTempState() { const env = await setupAuthTestEnv("openclaw-auth-"); - tempStateDir = env.stateDir; + lifecycle.setStateDir(env.stateDir); } function createPrompter(overrides: Partial): WizardPrompter { return createWizardPrompter(overrides, { defaultSelect: "" }); @@ -102,11 +101,7 @@ describe("applyAuthChoice", () => { resolvePluginProviders.mockReset(); loginOpenAICodexOAuth.mockReset(); loginOpenAICodexOAuth.mockResolvedValue(null); - if (tempStateDir) { - await fs.rm(tempStateDir, { recursive: true, force: true }); - tempStateDir = null; - } - envSnapshot.restore(); + await lifecycle.cleanup(); }); it("does not throw when openai-codex oauth fails", async () => { diff --git a/src/commands/auth-choice.moonshot.e2e.test.ts b/src/commands/auth-choice.moonshot.e2e.test.ts index d28148838a..64b1aa860f 100644 --- a/src/commands/auth-choice.moonshot.e2e.test.ts +++ b/src/commands/auth-choice.moonshot.e2e.test.ts @@ -1,9 +1,8 @@ -import fs from "node:fs/promises"; 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 { + createAuthTestLifecycle, createExitThrowingRuntime, createWizardPrompter, readAuthProfilesForAgent, @@ -16,17 +15,16 @@ function createPrompter(overrides: Partial): WizardPrompter { } describe("applyAuthChoice (moonshot)", () => { - const envSnapshot = captureEnv([ + const lifecycle = createAuthTestLifecycle([ "OPENCLAW_STATE_DIR", "OPENCLAW_AGENT_DIR", "PI_CODING_AGENT_DIR", "MOONSHOT_API_KEY", ]); - let tempStateDir: string | null = null; async function setupTempState() { const env = await setupAuthTestEnv("openclaw-auth-"); - tempStateDir = env.stateDir; + lifecycle.setStateDir(env.stateDir); delete process.env.MOONSHOT_API_KEY; } @@ -37,11 +35,7 @@ describe("applyAuthChoice (moonshot)", () => { } afterEach(async () => { - if (tempStateDir) { - await fs.rm(tempStateDir, { recursive: true, force: true }); - tempStateDir = null; - } - envSnapshot.restore(); + await lifecycle.cleanup(); }); it("keeps the .cn baseUrl when setDefaultModel is false", async () => { diff --git a/src/commands/onboard-auth.e2e.test.ts b/src/commands/onboard-auth.e2e.test.ts index b9ede00946..023546bb04 100644 --- a/src/commands/onboard-auth.e2e.test.ts +++ b/src/commands/onboard-auth.e2e.test.ts @@ -2,7 +2,6 @@ import type { OAuthCredentials } from "@mariozechner/pi-ai"; import fs from "node:fs/promises"; import path from "node:path"; import { afterEach, describe, expect, it } from "vitest"; -import { captureEnv } from "../test-utils/env.js"; import { applyAuthProfileConfig, applyLitellmProviderConfig, @@ -29,7 +28,11 @@ import { ZAI_CODING_CN_BASE_URL, ZAI_GLOBAL_BASE_URL, } from "./onboard-auth.js"; -import { readAuthProfilesForAgent, setupAuthTestEnv } from "./test-wizard-helpers.js"; +import { + createAuthTestLifecycle, + readAuthProfilesForAgent, + setupAuthTestEnv, +} from "./test-wizard-helpers.js"; function createLegacyProviderConfig(params: { providerId: string; @@ -62,25 +65,20 @@ function createLegacyProviderConfig(params: { } describe("writeOAuthCredentials", () => { - const envSnapshot = captureEnv([ + const lifecycle = createAuthTestLifecycle([ "OPENCLAW_STATE_DIR", "OPENCLAW_AGENT_DIR", "PI_CODING_AGENT_DIR", "OPENCLAW_OAUTH_DIR", ]); - let tempStateDir: string | null = null; afterEach(async () => { - if (tempStateDir) { - await fs.rm(tempStateDir, { recursive: true, force: true }); - tempStateDir = null; - } - envSnapshot.restore(); + await lifecycle.cleanup(); }); it("writes auth-profiles.json under OPENCLAW_AGENT_DIR when set", async () => { const env = await setupAuthTestEnv("openclaw-oauth-"); - tempStateDir = env.stateDir; + lifecycle.setStateDir(env.stateDir); const creds = { refresh: "refresh-token", @@ -100,30 +98,25 @@ describe("writeOAuthCredentials", () => { }); await expect( - fs.readFile(path.join(tempStateDir, "agents", "main", "agent", "auth-profiles.json"), "utf8"), + fs.readFile(path.join(env.stateDir, "agents", "main", "agent", "auth-profiles.json"), "utf8"), ).rejects.toThrow(); }); }); describe("setMinimaxApiKey", () => { - const envSnapshot = captureEnv([ + const lifecycle = createAuthTestLifecycle([ "OPENCLAW_STATE_DIR", "OPENCLAW_AGENT_DIR", "PI_CODING_AGENT_DIR", ]); - let tempStateDir: string | null = null; afterEach(async () => { - if (tempStateDir) { - await fs.rm(tempStateDir, { recursive: true, force: true }); - tempStateDir = null; - } - envSnapshot.restore(); + await lifecycle.cleanup(); }); it("writes to OPENCLAW_AGENT_DIR when set", async () => { const env = await setupAuthTestEnv("openclaw-minimax-", { agentSubdir: "custom-agent" }); - tempStateDir = env.stateDir; + lifecycle.setStateDir(env.stateDir); await setMinimaxApiKey("sk-minimax-test"); @@ -137,7 +130,7 @@ describe("setMinimaxApiKey", () => { }); await expect( - fs.readFile(path.join(tempStateDir, "agents", "main", "agent", "auth-profiles.json"), "utf8"), + fs.readFile(path.join(env.stateDir, "agents", "main", "agent", "auth-profiles.json"), "utf8"), ).rejects.toThrow(); }); }); diff --git a/src/commands/test-wizard-helpers.ts b/src/commands/test-wizard-helpers.ts index 1fc1f09cbc..bf12645a13 100644 --- a/src/commands/test-wizard-helpers.ts +++ b/src/commands/test-wizard-helpers.ts @@ -4,6 +4,7 @@ import { vi } from "vitest"; import type { RuntimeEnv } from "../runtime.js"; import type { WizardPrompter } from "../wizard/prompts.js"; import { makeTempWorkspace } from "../test-helpers/workspace.js"; +import { captureEnv } from "../test-utils/env.js"; export const noopAsync = async () => {}; export const noop = () => {}; @@ -51,6 +52,28 @@ export async function setupAuthTestEnv( return { stateDir, agentDir }; } +export type AuthTestLifecycle = { + setStateDir: (stateDir: string) => void; + cleanup: () => Promise; +}; + +export function createAuthTestLifecycle(envKeys: string[]): AuthTestLifecycle { + const envSnapshot = captureEnv(envKeys); + let stateDir: string | null = null; + return { + setStateDir(nextStateDir: string) { + stateDir = nextStateDir; + }, + async cleanup() { + if (stateDir) { + await fs.rm(stateDir, { recursive: true, force: true }); + stateDir = null; + } + envSnapshot.restore(); + }, + }; +} + export function requireOpenClawAgentDir(): string { const agentDir = process.env.OPENCLAW_AGENT_DIR; if (!agentDir) {