test: inject provider auth command fixtures

This commit is contained in:
Peter Steinberger
2026-04-10 16:41:10 +01:00
parent d6ece7fb89
commit 8a5b7cf573
3 changed files with 135 additions and 99 deletions

View File

@@ -1,5 +1,7 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { resolveAgentModelPrimaryValue } from "../config/model-input.js";
import { __testing as providerAuthChoiceTesting } from "../plugins/provider-auth-choice.js";
import type { ProviderAuthContext, ProviderPlugin } from "../plugins/types.js";
import type { WizardPrompter } from "../wizard/prompts.js";
import { applyAuthChoice } from "./auth-choice.js";
import {
@@ -27,6 +29,75 @@ describe("applyAuthChoice (moonshot)", () => {
const env = await setupAuthTestEnv("openclaw-auth-");
lifecycle.setStateDir(env.stateDir);
delete process.env.MOONSHOT_API_KEY;
providerAuthChoiceTesting.setDepsForTest({
loadPluginProviderRuntime: async () =>
({
resolvePluginProviders: () =>
[
{
id: "moonshot",
label: "Moonshot",
auth: [
{
id: "api-key-cn",
label: "Moonshot API key (.cn)",
kind: "api_key",
run: async ({ prompter }: ProviderAuthContext) => {
const key = await prompter.text({
message: "Enter Moonshot API key (.cn)",
});
return {
profiles: [
{
profileId: "moonshot:default",
credential: {
type: "api_key",
provider: "moonshot",
key,
},
},
],
configPatch: {
models: {
providers: {
moonshot: {
api: "openai-completions",
baseUrl: "https://api.moonshot.cn/v1",
models: [
{
id: "kimi-k2.5",
name: "kimi-k2.5",
input: ["text", "image"],
reasoning: true,
cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
contextWindow: 128_000,
maxTokens: 8192,
},
],
},
},
},
},
defaultModel: "moonshot/kimi-k2.5",
};
},
},
],
},
] as ProviderPlugin[],
resolveProviderPluginChoice: ({
choice,
providers,
}: {
choice: string;
providers: ProviderPlugin[];
}) =>
choice === "moonshot-api-key-cn"
? { provider: providers[0], method: providers[0]?.auth[0] }
: null,
runProviderModelSelectedHook: async () => {},
}) as never,
});
}
async function readAuthProfiles() {
@@ -53,6 +124,7 @@ describe("applyAuthChoice (moonshot)", () => {
}
afterEach(async () => {
providerAuthChoiceTesting.resetDepsForTest();
await lifecycle.cleanup();
});

View File

@@ -1,122 +1,65 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import { note } from "../terminal/note.js";
import { withEnvAsync } from "../test-utils/env.js";
import { runDoctorConfigWithInput } from "./doctor-config-flow.test-utils.js";
vi.mock("../terminal/note.js", () => ({
note: vi.fn(),
}));
vi.mock("./doctor-legacy-config.js", async (importOriginal) => {
const actual = await importOriginal<typeof import("./doctor-legacy-config.js")>();
return {
...actual,
normalizeCompatibilityConfigValues: (cfg: unknown) => ({
config: cfg,
changes: [],
}),
};
});
import { loadAndMaybeMigrateDoctorConfig } from "./doctor-config-flow.js";
const noteSpy = vi.mocked(note);
import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import {
collectMissingDefaultAccountBindingWarnings,
collectMissingExplicitDefaultAccountWarnings,
} from "./doctor/shared/default-account-warnings.js";
describe("doctor missing default account binding warning", () => {
beforeEach(() => {
noteSpy.mockClear();
});
it("emits a doctor warning when named accounts have no valid account-scoped bindings", async () => {
await withEnvAsync(
{
TELEGRAM_BOT_TOKEN: undefined,
TELEGRAM_BOT_TOKEN_FILE: undefined,
},
async () => {
await runDoctorConfigWithInput({
config: {
channels: {
telegram: {
accounts: {
alerts: {},
work: {},
},
},
},
bindings: [{ agentId: "ops", match: { channel: "telegram" } }],
it("warns when named accounts have no valid account-scoped bindings", () => {
const warnings = collectMissingDefaultAccountBindingWarnings({
channels: {
telegram: {
accounts: {
alerts: {},
work: {},
},
run: loadAndMaybeMigrateDoctorConfig,
});
},
},
);
bindings: [{ agentId: "ops", match: { channel: "telegram" } }],
} as OpenClawConfig);
expect(noteSpy).toHaveBeenCalledWith(
expect(warnings).toEqual([
expect.stringContaining("channels.telegram: accounts.default is missing"),
"Doctor warnings",
);
]);
});
it("emits a warning when multiple accounts have no explicit default", async () => {
await withEnvAsync(
{
TELEGRAM_BOT_TOKEN: undefined,
TELEGRAM_BOT_TOKEN_FILE: undefined,
},
async () => {
await runDoctorConfigWithInput({
config: {
channels: {
telegram: {
accounts: {
alerts: {},
work: {},
},
},
},
it("warns when multiple accounts have no explicit default", () => {
const warnings = collectMissingExplicitDefaultAccountWarnings({
channels: {
telegram: {
accounts: {
alerts: {},
work: {},
},
run: loadAndMaybeMigrateDoctorConfig,
});
},
},
);
} as OpenClawConfig);
expect(noteSpy).toHaveBeenCalledWith(
expect(warnings).toEqual([
expect.stringContaining(
"channels.telegram: multiple accounts are configured but no explicit default is set",
),
"Doctor warnings",
);
]);
});
it("emits a warning when defaultAccount does not match configured accounts", async () => {
await withEnvAsync(
{
TELEGRAM_BOT_TOKEN: undefined,
TELEGRAM_BOT_TOKEN_FILE: undefined,
},
async () => {
await runDoctorConfigWithInput({
config: {
channels: {
telegram: {
defaultAccount: "missing",
accounts: {
alerts: {},
work: {},
},
},
},
it("warns when defaultAccount does not match configured accounts", () => {
const warnings = collectMissingExplicitDefaultAccountWarnings({
channels: {
telegram: {
defaultAccount: "missing",
accounts: {
alerts: {},
work: {},
},
run: loadAndMaybeMigrateDoctorConfig,
});
},
},
);
} as OpenClawConfig);
expect(noteSpy).toHaveBeenCalledWith(
expect(warnings).toEqual([
expect.stringContaining(
'channels.telegram: defaultAccount is set to "missing" but does not match configured accounts',
),
"Doctor warnings",
);
]);
});
});

View File

@@ -78,10 +78,31 @@ function restoreConfiguredPrimaryModel(
};
}
type ProviderAuthChoiceRuntime = typeof import("./provider-auth-choice.runtime.js");
const defaultProviderAuthChoiceDeps = {
loadPluginProviderRuntime: async (): Promise<ProviderAuthChoiceRuntime> =>
import("./provider-auth-choice.runtime.js"),
};
let providerAuthChoiceDeps = defaultProviderAuthChoiceDeps;
async function loadPluginProviderRuntime() {
return import("./provider-auth-choice.runtime.js");
return await providerAuthChoiceDeps.loadPluginProviderRuntime();
}
export const __testing = {
resetDepsForTest(): void {
providerAuthChoiceDeps = defaultProviderAuthChoiceDeps;
},
setDepsForTest(deps: Partial<typeof defaultProviderAuthChoiceDeps>): void {
providerAuthChoiceDeps = {
...defaultProviderAuthChoiceDeps,
...deps,
};
},
} as const;
export async function runProviderPluginAuthMethod(params: {
config: OpenClawConfig;
env?: NodeJS.ProcessEnv;