mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 06:40:44 +00:00
test: merge repeated onboarding auth cases
This commit is contained in:
@@ -1092,21 +1092,50 @@ describe("applyAuthChoice", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("maps apiKey tokenProvider aliases to provider flow", async () => {
|
||||
it("uses provided tokens without prompting across alias and direct provider choices", async () => {
|
||||
const scenarios: Array<{
|
||||
authChoice: "apiKey" | "opencode-zen" | "gemini-api-key";
|
||||
config?: OpenClawConfig;
|
||||
setDefaultModel: boolean;
|
||||
tokenProvider: string;
|
||||
token: string;
|
||||
profileId: string;
|
||||
provider: string;
|
||||
expectedModel: string;
|
||||
expectedModel?: string;
|
||||
expectedModelPrefix?: string;
|
||||
expectedAgentModelOverride?: string;
|
||||
extraProfiles?: string[];
|
||||
}> = [
|
||||
{
|
||||
authChoice: "apiKey",
|
||||
setDefaultModel: true,
|
||||
tokenProvider: " GOOGLE ",
|
||||
token: "sk-gemini-token-provider-test",
|
||||
profileId: "google:default",
|
||||
provider: "google",
|
||||
expectedModel: GOOGLE_GEMINI_DEFAULT_MODEL,
|
||||
},
|
||||
{
|
||||
authChoice: "opencode-zen",
|
||||
setDefaultModel: true,
|
||||
tokenProvider: "opencode",
|
||||
token: "sk-opencode-test",
|
||||
profileId: "opencode:default",
|
||||
provider: "opencode",
|
||||
expectedModelPrefix: "opencode/",
|
||||
extraProfiles: ["opencode-go:default"],
|
||||
},
|
||||
{
|
||||
authChoice: "gemini-api-key",
|
||||
config: { agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } } },
|
||||
setDefaultModel: false,
|
||||
tokenProvider: "google",
|
||||
token: "sk-gemini-test",
|
||||
profileId: "google:default",
|
||||
provider: "google",
|
||||
expectedModel: "openai/gpt-4o-mini",
|
||||
expectedAgentModelOverride: GOOGLE_GEMINI_DEFAULT_MODEL,
|
||||
},
|
||||
];
|
||||
await setupTempState();
|
||||
for (const scenario of scenarios) {
|
||||
@@ -1118,124 +1147,40 @@ describe("applyAuthChoice", () => {
|
||||
const { prompter, runtime } = createApiKeyPromptHarness({ text, confirm });
|
||||
|
||||
const result = await applyAuthChoice({
|
||||
authChoice: "apiKey",
|
||||
config: {},
|
||||
authChoice: scenario.authChoice,
|
||||
config: scenario.config ?? {},
|
||||
prompter,
|
||||
runtime,
|
||||
setDefaultModel: true,
|
||||
setDefaultModel: scenario.setDefaultModel,
|
||||
opts: {
|
||||
tokenProvider: scenario.tokenProvider,
|
||||
token: scenario.token,
|
||||
},
|
||||
});
|
||||
|
||||
expect(text).not.toHaveBeenCalled();
|
||||
expect(confirm).not.toHaveBeenCalled();
|
||||
expect(result.config.auth?.profiles?.[scenario.profileId]).toMatchObject({
|
||||
provider: scenario.provider,
|
||||
mode: "api_key",
|
||||
});
|
||||
expect(resolveAgentModelPrimaryValue(result.config.agents?.defaults?.model)).toBe(
|
||||
scenario.expectedModel,
|
||||
);
|
||||
expect(text).not.toHaveBeenCalled();
|
||||
expect(confirm).not.toHaveBeenCalled();
|
||||
const selectedModel = resolveAgentModelPrimaryValue(result.config.agents?.defaults?.model);
|
||||
if (scenario.expectedModel) {
|
||||
expect(selectedModel).toBe(scenario.expectedModel);
|
||||
}
|
||||
if (scenario.expectedModelPrefix) {
|
||||
expect(selectedModel?.startsWith(scenario.expectedModelPrefix)).toBe(true);
|
||||
}
|
||||
if (scenario.expectedAgentModelOverride) {
|
||||
expect(result.agentModelOverride).toBe(scenario.expectedAgentModelOverride);
|
||||
}
|
||||
expect((await readAuthProfile(scenario.profileId))?.key).toBe(scenario.token);
|
||||
}
|
||||
});
|
||||
|
||||
it("uses opts token for direct provider choices without prompting", async () => {
|
||||
await setupTempState();
|
||||
const scenarios: Array<{
|
||||
authChoice: "opencode-zen";
|
||||
tokenProvider: "opencode";
|
||||
profileId: "opencode:default";
|
||||
provider: "opencode";
|
||||
modelPrefix: "opencode/";
|
||||
extraProfiles: string[];
|
||||
}> = [
|
||||
{
|
||||
authChoice: "opencode-zen",
|
||||
tokenProvider: "opencode",
|
||||
profileId: "opencode:default",
|
||||
provider: "opencode",
|
||||
modelPrefix: "opencode/",
|
||||
extraProfiles: ["opencode-go:default"],
|
||||
},
|
||||
];
|
||||
for (const {
|
||||
authChoice,
|
||||
tokenProvider,
|
||||
profileId,
|
||||
provider,
|
||||
modelPrefix,
|
||||
extraProfiles,
|
||||
} of scenarios) {
|
||||
const text = vi.fn();
|
||||
const confirm = vi.fn(async () => false);
|
||||
const { prompter, runtime } = createApiKeyPromptHarness({ text, confirm });
|
||||
const token = `sk-${tokenProvider}-test`;
|
||||
|
||||
const result = await applyAuthChoice({
|
||||
authChoice,
|
||||
config: {},
|
||||
prompter,
|
||||
runtime,
|
||||
setDefaultModel: true,
|
||||
opts: {
|
||||
tokenProvider,
|
||||
token,
|
||||
},
|
||||
});
|
||||
|
||||
expect(text).not.toHaveBeenCalled();
|
||||
expect(confirm).not.toHaveBeenCalled();
|
||||
expect(result.config.auth?.profiles?.[profileId]).toMatchObject({
|
||||
provider,
|
||||
mode: "api_key",
|
||||
});
|
||||
expect(
|
||||
resolveAgentModelPrimaryValue(result.config.agents?.defaults?.model)?.startsWith(
|
||||
modelPrefix,
|
||||
),
|
||||
).toBe(true);
|
||||
expect((await readAuthProfile(profileId))?.key).toBe(token);
|
||||
for (const extraProfile of extraProfiles ?? []) {
|
||||
expect((await readAuthProfile(extraProfile))?.key).toBe(token);
|
||||
for (const extraProfile of scenario.extraProfiles ?? []) {
|
||||
expect((await readAuthProfile(extraProfile))?.key).toBe(scenario.token);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it("uses opts token for Gemini and keeps global default model when setDefaultModel=false", async () => {
|
||||
await setupTempState();
|
||||
|
||||
const text = vi.fn();
|
||||
const confirm = vi.fn(async () => false);
|
||||
const { prompter, runtime } = createApiKeyPromptHarness({ text, confirm });
|
||||
|
||||
const result = await applyAuthChoice({
|
||||
authChoice: "gemini-api-key",
|
||||
config: { agents: { defaults: { model: { primary: "openai/gpt-4o-mini" } } } },
|
||||
prompter,
|
||||
runtime,
|
||||
setDefaultModel: false,
|
||||
opts: {
|
||||
tokenProvider: "google",
|
||||
token: "sk-gemini-test",
|
||||
},
|
||||
});
|
||||
|
||||
expect(text).not.toHaveBeenCalled();
|
||||
expect(confirm).not.toHaveBeenCalled();
|
||||
expect(result.config.auth?.profiles?.["google:default"]).toMatchObject({
|
||||
provider: "google",
|
||||
mode: "api_key",
|
||||
});
|
||||
expect(resolveAgentModelPrimaryValue(result.config.agents?.defaults?.model)).toBe(
|
||||
"openai/gpt-4o-mini",
|
||||
);
|
||||
expect(result.agentModelOverride).toBe(GOOGLE_GEMINI_DEFAULT_MODEL);
|
||||
expect((await readAuthProfile("google:default"))?.key).toBe("sk-gemini-test");
|
||||
});
|
||||
|
||||
it("prompts for Venice API key and shows the Venice note when no token is provided", async () => {
|
||||
await setupTempState();
|
||||
process.env.VENICE_API_KEY = "";
|
||||
|
||||
@@ -635,7 +635,7 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
|
||||
});
|
||||
}, 60_000);
|
||||
|
||||
it("uses a longer Windows health deadline when daemon install was requested", async () => {
|
||||
it("uses longer Windows health timeouts when daemon install was requested", async () => {
|
||||
await withStateDir("state-local-daemon-health-win-", async (stateDir) => {
|
||||
const captured = mockGatewayReachableWithCapturedTimeouts();
|
||||
|
||||
@@ -646,17 +646,6 @@ describe("onboard (non-interactive): gateway and remote auth", () => {
|
||||
expect(installGatewayDaemonNonInteractiveMock).toHaveBeenCalledTimes(1);
|
||||
expect(captured.deadlineMs).toBe(90_000);
|
||||
expect(captured.probeTimeoutMs).toBe(15_000);
|
||||
});
|
||||
}, 60_000);
|
||||
|
||||
it("uses a longer Windows health command timeout when daemon install was requested", async () => {
|
||||
await withStateDir("state-local-daemon-health-command-win-", async (stateDir) => {
|
||||
waitForGatewayReachableMock = vi.fn(async () => ({ ok: true }));
|
||||
|
||||
await withMockedPlatform("win32", async () => {
|
||||
await runLocalDaemonSetup(stateDir);
|
||||
});
|
||||
|
||||
expect(healthCommandMock).toHaveBeenCalledTimes(1);
|
||||
expect(healthCommandMock).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
|
||||
@@ -1178,49 +1178,51 @@ describe("onboard (non-interactive): provider auth", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("configures a custom provider from non-interactive flags", async () => {
|
||||
await withOnboardEnv("openclaw-onboard-custom-provider-", async ({ configPath, runtime }) => {
|
||||
await runNonInteractiveSetupWithDefaults(runtime, {
|
||||
authChoice: "custom-api-key",
|
||||
customBaseUrl: "https://llm.example.com/v1",
|
||||
customApiKey: "custom-test-key", // pragma: allowlist secret
|
||||
customModelId: "foo-large",
|
||||
customCompatibility: "anthropic",
|
||||
skipSkills: true,
|
||||
});
|
||||
|
||||
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
|
||||
|
||||
const provider = cfg.models?.providers?.["custom-llm-example-com"];
|
||||
expect(provider?.baseUrl).toBe("https://llm.example.com/v1");
|
||||
expect(provider?.api).toBe("anthropic-messages");
|
||||
expect(provider?.apiKey).toBe("custom-test-key");
|
||||
expect(provider?.models?.some((model) => model.id === "foo-large")).toBe(true);
|
||||
expect(cfg.agents?.defaults?.model?.primary).toBe("custom-llm-example-com/foo-large");
|
||||
});
|
||||
});
|
||||
|
||||
it("infers custom provider auth choice from custom flags", async () => {
|
||||
await withOnboardEnv(
|
||||
"openclaw-onboard-custom-provider-infer-",
|
||||
async ({ configPath, runtime }) => {
|
||||
await runNonInteractiveSetupWithDefaults(runtime, {
|
||||
it("configures custom providers from explicit or inferred non-interactive flags", async () => {
|
||||
const scenarios = [
|
||||
{
|
||||
options: {
|
||||
authChoice: "custom-api-key",
|
||||
customBaseUrl: "https://llm.example.com/v1",
|
||||
customApiKey: "custom-test-key", // pragma: allowlist secret
|
||||
customModelId: "foo-large",
|
||||
customCompatibility: "anthropic",
|
||||
skipSkills: true,
|
||||
},
|
||||
providerId: "custom-llm-example-com",
|
||||
expectedBaseUrl: "https://llm.example.com/v1",
|
||||
expectedApi: "anthropic-messages",
|
||||
expectedModel: "custom-llm-example-com/foo-large",
|
||||
modelId: "foo-large",
|
||||
},
|
||||
{
|
||||
options: {
|
||||
customBaseUrl: "https://models.custom.local/v1",
|
||||
customModelId: "local-large",
|
||||
customApiKey: "custom-test-key", // pragma: allowlist secret
|
||||
skipSkills: true,
|
||||
});
|
||||
|
||||
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
|
||||
|
||||
expect(cfg.models?.providers?.["custom-models-custom-local"]?.baseUrl).toBe(
|
||||
"https://models.custom.local/v1",
|
||||
);
|
||||
expect(cfg.models?.providers?.["custom-models-custom-local"]?.api).toBe(
|
||||
"openai-completions",
|
||||
);
|
||||
expect(cfg.agents?.defaults?.model?.primary).toBe("custom-models-custom-local/local-large");
|
||||
},
|
||||
providerId: "custom-models-custom-local",
|
||||
expectedBaseUrl: "https://models.custom.local/v1",
|
||||
expectedApi: "openai-completions",
|
||||
expectedModel: "custom-models-custom-local/local-large",
|
||||
modelId: "local-large",
|
||||
},
|
||||
);
|
||||
] as const;
|
||||
|
||||
await withOnboardEnv("openclaw-onboard-custom-provider-", async ({ configPath, runtime }) => {
|
||||
for (const scenario of scenarios) {
|
||||
await fs.rm(configPath, { force: true });
|
||||
resetProviderAuthTestState();
|
||||
await runNonInteractiveSetupWithDefaults(runtime, scenario.options);
|
||||
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
|
||||
const provider = cfg.models?.providers?.[scenario.providerId];
|
||||
expect(provider?.baseUrl).toBe(scenario.expectedBaseUrl);
|
||||
expect(provider?.api).toBe(scenario.expectedApi);
|
||||
expect(provider?.apiKey).toBe("custom-test-key");
|
||||
expect(provider?.models?.some((model) => model.id === scenario.modelId)).toBe(true);
|
||||
expect(cfg.agents?.defaults?.model?.primary).toBe(scenario.expectedModel);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user