refactor(test): share auth test env/profile helpers

This commit is contained in:
Peter Steinberger
2026-02-16 15:25:23 +00:00
parent 1d37389490
commit 94f455c693
4 changed files with 79 additions and 69 deletions

View File

@@ -1,13 +1,14 @@
import fs from "node:fs/promises"; 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 { afterEach, describe, expect, it, vi } from "vitest";
import type { WizardPrompter } from "../wizard/prompts.js"; import type { WizardPrompter } from "../wizard/prompts.js";
import { captureEnv } from "../test-utils/env.js"; import { captureEnv } from "../test-utils/env.js";
import { applyAuthChoiceHuggingface } from "./auth-choice.apply.huggingface.js"; import { applyAuthChoiceHuggingface } from "./auth-choice.apply.huggingface.js";
import { createExitThrowingRuntime, createWizardPrompter } from "./test-wizard-helpers.js"; import {
createExitThrowingRuntime,
const authProfilePathFor = (agentDir: string) => path.join(agentDir, "auth-profiles.json"); createWizardPrompter,
readAuthProfilesForAgent,
setupAuthTestEnv,
} from "./test-wizard-helpers.js";
function createHuggingfacePrompter(params: { function createHuggingfacePrompter(params: {
text: WizardPrompter["text"]; text: WizardPrompter["text"];
@@ -25,9 +26,27 @@ function createHuggingfacePrompter(params: {
} }
describe("applyAuthChoiceHuggingface", () => { 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; 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<string, { key?: string }>;
}>(agentDir);
}
afterEach(async () => { afterEach(async () => {
if (tempStateDir) { if (tempStateDir) {
await fs.rm(tempStateDir, { recursive: true, force: true }); await fs.rm(tempStateDir, { recursive: true, force: true });
@@ -41,17 +60,14 @@ describe("applyAuthChoiceHuggingface", () => {
authChoice: "openrouter-api-key", authChoice: "openrouter-api-key",
config: {}, config: {},
prompter: {} as WizardPrompter, prompter: {} as WizardPrompter,
runtime: {} as RuntimeEnv, runtime: createExitThrowingRuntime(),
setDefaultModel: false, setDefaultModel: false,
}); });
expect(result).toBeNull(); expect(result).toBeNull();
}); });
it("prompts for key and model, then writes config and auth profile", async () => { 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 = await setupTempState();
const agentDir = path.join(tempStateDir, "agent");
process.env.OPENCLAW_AGENT_DIR = agentDir;
await fs.mkdir(agentDir, { recursive: true });
const text = vi.fn().mockResolvedValue("hf-test-token"); const text = vi.fn().mockResolvedValue("hf-test-token");
const select: WizardPrompter["select"] = vi.fn( const select: WizardPrompter["select"] = vi.fn(
@@ -81,21 +97,14 @@ describe("applyAuthChoiceHuggingface", () => {
expect.objectContaining({ message: "Default Hugging Face model" }), expect.objectContaining({ message: "Default Hugging Face model" }),
); );
const authProfilePath = authProfilePathFor(agentDir); const parsed = await readAuthProfiles(agentDir);
const raw = await fs.readFile(authProfilePath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, { key?: string }>;
};
expect(parsed.profiles?.["huggingface:default"]?.key).toBe("hf-test-token"); expect(parsed.profiles?.["huggingface:default"]?.key).toBe("hf-test-token");
}); });
it("does not prompt to reuse env token when opts.token already provided", async () => { 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 = await setupTempState();
const agentDir = path.join(tempStateDir, "agent");
process.env.OPENCLAW_AGENT_DIR = agentDir;
process.env.HF_TOKEN = "hf-env-token"; process.env.HF_TOKEN = "hf-env-token";
delete process.env.HUGGINGFACE_HUB_TOKEN; delete process.env.HUGGINGFACE_HUB_TOKEN;
await fs.mkdir(agentDir, { recursive: true });
const text = vi.fn().mockResolvedValue("hf-text-token"); const text = vi.fn().mockResolvedValue("hf-text-token");
const select: WizardPrompter["select"] = vi.fn( const select: WizardPrompter["select"] = vi.fn(
@@ -121,11 +130,7 @@ describe("applyAuthChoiceHuggingface", () => {
expect(confirm).not.toHaveBeenCalled(); expect(confirm).not.toHaveBeenCalled();
expect(text).not.toHaveBeenCalled(); expect(text).not.toHaveBeenCalled();
const authProfilePath = authProfilePathFor(agentDir); const parsed = await readAuthProfiles(agentDir);
const raw = await fs.readFile(authProfilePath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, { key?: string }>;
};
expect(parsed.profiles?.["huggingface:default"]?.key).toBe("hf-opts-token"); expect(parsed.profiles?.["huggingface:default"]?.key).toBe("hf-opts-token");
}); });
}); });

View File

@@ -1,13 +1,15 @@
import fs from "node:fs/promises"; 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 { afterEach, describe, expect, it, vi } from "vitest";
import type { WizardPrompter } from "../wizard/prompts.js"; import type { WizardPrompter } from "../wizard/prompts.js";
import { captureEnv } from "../test-utils/env.js"; import { captureEnv } from "../test-utils/env.js";
import { applyAuthChoice } from "./auth-choice.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 requireAgentDir = () => {
const agentDir = process.env.OPENCLAW_AGENT_DIR; const agentDir = process.env.OPENCLAW_AGENT_DIR;
if (!agentDir) { if (!agentDir) {
@@ -30,13 +32,17 @@ describe("applyAuthChoice (moonshot)", () => {
let tempStateDir: string | null = null; let tempStateDir: string | null = null;
async function setupTempState() { async function setupTempState() {
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-auth-")); const env = await setupAuthTestEnv("openclaw-auth-");
process.env.OPENCLAW_STATE_DIR = tempStateDir; tempStateDir = env.stateDir;
process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "agent");
process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_AGENT_DIR;
delete process.env.MOONSHOT_API_KEY; delete process.env.MOONSHOT_API_KEY;
} }
async function readAuthProfiles() {
return await readAuthProfilesForAgent<{
profiles?: Record<string, { key?: string }>;
}>(requireAgentDir());
}
afterEach(async () => { afterEach(async () => {
if (tempStateDir) { if (tempStateDir) {
await fs.rm(tempStateDir, { recursive: true, force: true }); 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.config.models?.providers?.moonshot?.baseUrl).toBe("https://api.moonshot.cn/v1");
expect(result.agentModelOverride).toBe("moonshot/kimi-k2.5"); expect(result.agentModelOverride).toBe("moonshot/kimi-k2.5");
const authProfilePath = authProfilePathFor(requireAgentDir()); const parsed = await readAuthProfiles();
const raw = await fs.readFile(authProfilePath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, { key?: string }>;
};
expect(parsed.profiles?.["moonshot:default"]?.key).toBe("sk-moonshot-cn-test"); 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.config.models?.providers?.moonshot?.baseUrl).toBe("https://api.moonshot.cn/v1");
expect(result.agentModelOverride).toBeUndefined(); expect(result.agentModelOverride).toBeUndefined();
const authProfilePath = authProfilePathFor(requireAgentDir()); const parsed = await readAuthProfiles();
const raw = await fs.readFile(authProfilePath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, { key?: string }>;
};
expect(parsed.profiles?.["moonshot:default"]?.key).toBe("sk-moonshot-cn-test"); expect(parsed.profiles?.["moonshot:default"]?.key).toBe("sk-moonshot-cn-test");
}); });
}); });

View File

@@ -1,6 +1,5 @@
import type { OAuthCredentials } from "@mariozechner/pi-ai"; import type { OAuthCredentials } from "@mariozechner/pi-ai";
import fs from "node:fs/promises"; import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path"; import path from "node:path";
import { afterEach, describe, expect, it } from "vitest"; import { afterEach, describe, expect, it } from "vitest";
import { captureEnv } from "../test-utils/env.js"; import { captureEnv } from "../test-utils/env.js";
@@ -30,15 +29,7 @@ import {
ZAI_CODING_CN_BASE_URL, ZAI_CODING_CN_BASE_URL,
ZAI_GLOBAL_BASE_URL, ZAI_GLOBAL_BASE_URL,
} from "./onboard-auth.js"; } from "./onboard-auth.js";
import { 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) {
throw new Error("OPENCLAW_AGENT_DIR not set");
}
return agentDir;
};
function createLegacyProviderConfig(params: { function createLegacyProviderConfig(params: {
providerId: string; providerId: string;
@@ -88,10 +79,8 @@ describe("writeOAuthCredentials", () => {
}); });
it("writes auth-profiles.json under OPENCLAW_AGENT_DIR when set", async () => { it("writes auth-profiles.json under OPENCLAW_AGENT_DIR when set", async () => {
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-oauth-")); const env = await setupAuthTestEnv("openclaw-oauth-");
process.env.OPENCLAW_STATE_DIR = tempStateDir; tempStateDir = env.stateDir;
process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "agent");
process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_AGENT_DIR;
const creds = { const creds = {
refresh: "refresh-token", refresh: "refresh-token",
@@ -101,11 +90,9 @@ describe("writeOAuthCredentials", () => {
await writeOAuthCredentials("openai-codex", creds); await writeOAuthCredentials("openai-codex", creds);
const authProfilePath = authProfilePathFor(requireAgentDir()); const parsed = await readAuthProfilesForAgent<{
const raw = await fs.readFile(authProfilePath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, OAuthCredentials & { type?: string }>; profiles?: Record<string, OAuthCredentials & { type?: string }>;
}; }>(env.agentDir);
expect(parsed.profiles?.["openai-codex:default"]).toMatchObject({ expect(parsed.profiles?.["openai-codex:default"]).toMatchObject({
refresh: "refresh-token", refresh: "refresh-token",
access: "access-token", access: "access-token",
@@ -135,18 +122,14 @@ describe("setMinimaxApiKey", () => {
}); });
it("writes to OPENCLAW_AGENT_DIR when set", async () => { it("writes to OPENCLAW_AGENT_DIR when set", async () => {
tempStateDir = await fs.mkdtemp(path.join(os.tmpdir(), "openclaw-minimax-")); const env = await setupAuthTestEnv("openclaw-minimax-", { agentSubdir: "custom-agent" });
process.env.OPENCLAW_STATE_DIR = tempStateDir; tempStateDir = env.stateDir;
process.env.OPENCLAW_AGENT_DIR = path.join(tempStateDir, "custom-agent");
process.env.PI_CODING_AGENT_DIR = process.env.OPENCLAW_AGENT_DIR;
await setMinimaxApiKey("sk-minimax-test"); await setMinimaxApiKey("sk-minimax-test");
const customAuthPath = authProfilePathFor(requireAgentDir()); const parsed = await readAuthProfilesForAgent<{
const raw = await fs.readFile(customAuthPath, "utf8");
const parsed = JSON.parse(raw) as {
profiles?: Record<string, { type?: string; provider?: string; key?: string }>; profiles?: Record<string, { type?: string; provider?: string; key?: string }>;
}; }>(env.agentDir);
expect(parsed.profiles?.["minimax:default"]).toMatchObject({ expect(parsed.profiles?.["minimax:default"]).toMatchObject({
type: "api_key", type: "api_key",
provider: "minimax", provider: "minimax",

View File

@@ -1,6 +1,9 @@
import fs from "node:fs/promises";
import path from "node:path";
import { vi } from "vitest"; import { vi } from "vitest";
import type { RuntimeEnv } from "../runtime.js"; import type { RuntimeEnv } from "../runtime.js";
import type { WizardPrompter } from "../wizard/prompts.js"; import type { WizardPrompter } from "../wizard/prompts.js";
import { makeTempWorkspace } from "../test-helpers/workspace.js";
export const noopAsync = async () => {}; export const noopAsync = async () => {};
export const noop = () => {}; export const noop = () => {};
@@ -31,3 +34,24 @@ export function createWizardPrompter(
...overrides, ...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<T>(agentDir: string): Promise<T> {
const raw = await fs.readFile(path.join(agentDir, "auth-profiles.json"), "utf8");
return JSON.parse(raw) as T;
}