test: trim custom provider onboarding duplicates

This commit is contained in:
Peter Steinberger
2026-04-17 07:42:00 +01:00
parent e19e94ef07
commit e00f9c7a9d
3 changed files with 101 additions and 42 deletions

View File

@@ -1132,7 +1132,6 @@ async function runOnboardingAndReadConfig(
const CUSTOM_LOCAL_BASE_URL = "https://models.custom.local/v1";
const CUSTOM_LOCAL_MODEL_ID = "local-large";
const CUSTOM_LOCAL_PROVIDER_ID = "custom-models-custom-local";
async function runCustomLocalNonInteractive(
runtime: NonInteractiveRuntime,
@@ -1147,19 +1146,6 @@ async function runCustomLocalNonInteractive(
});
}
async function readCustomLocalProviderApiKey(configPath: string): Promise<string | undefined> {
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
const apiKey = cfg.models?.providers?.[CUSTOM_LOCAL_PROVIDER_ID]?.apiKey;
return typeof apiKey === "string" ? apiKey : undefined;
}
async function readCustomLocalProviderApiKeyInput(
configPath: string,
): Promise<string | { source?: string; id?: string } | undefined> {
const cfg = await readJsonFile<ProviderAuthConfigSnapshot>(configPath);
return cfg.models?.providers?.[CUSTOM_LOCAL_PROVIDER_ID]?.apiKey;
}
async function expectApiKeyProfile(params: {
profileId: string;
provider: string;
@@ -1580,34 +1566,6 @@ describe("onboard (non-interactive): provider auth", () => {
);
});
it("uses CUSTOM_API_KEY env fallback for non-interactive custom provider auth", async () => {
await withOnboardEnv(
"openclaw-onboard-custom-provider-env-fallback-",
async ({ configPath, runtime }) => {
process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret
await runCustomLocalNonInteractive(runtime);
expect(await readCustomLocalProviderApiKey(configPath)).toBe("custom-env-key");
},
);
});
it("stores CUSTOM_API_KEY env ref for non-interactive custom provider auth in ref mode", async () => {
await withOnboardEnv(
"openclaw-onboard-custom-provider-env-ref-",
async ({ configPath, runtime }) => {
process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret
await runCustomLocalNonInteractive(runtime, {
secretInputMode: "ref", // pragma: allowlist secret
});
expect(await readCustomLocalProviderApiKeyInput(configPath)).toEqual({
source: "env",
provider: "default",
id: "CUSTOM_API_KEY",
});
},
);
});
it("fails fast for custom provider ref mode when --custom-api-key is set but CUSTOM_API_KEY env is missing", async () => {
await withOnboardEnv("openclaw-onboard-custom-provider-ref-flag-", async ({ runtime }) => {
const providedSecret = "custom-inline-key-should-not-leak"; // pragma: allowlist secret

View File

@@ -92,6 +92,69 @@ describe("resolveNonInteractiveApiKey", () => {
}
});
it("returns explicit env fallback keys when provider env discovery misses", async () => {
const runtime = createRuntime();
resolveEnvApiKey.mockReturnValue(null);
const previousCustomApiKey = process.env.CUSTOM_API_KEY;
process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret
try {
const result = await resolveNonInteractiveApiKey({
provider: "custom-models-custom-local",
cfg: {},
flagName: "--custom-api-key",
envVar: "CUSTOM_API_KEY",
envVarName: "CUSTOM_API_KEY",
runtime: runtime as never,
});
expect(result).toEqual({
key: "custom-env-key",
source: "env",
envVarName: "CUSTOM_API_KEY",
});
expect(runtime.exit).not.toHaveBeenCalled();
} finally {
if (previousCustomApiKey === undefined) {
delete process.env.CUSTOM_API_KEY;
} else {
process.env.CUSTOM_API_KEY = previousCustomApiKey;
}
}
});
it("returns explicit env fallback refs in secret-ref mode", async () => {
const runtime = createRuntime();
resolveEnvApiKey.mockReturnValue(null);
const previousCustomApiKey = process.env.CUSTOM_API_KEY;
process.env.CUSTOM_API_KEY = "custom-env-key"; // pragma: allowlist secret
try {
const result = await resolveNonInteractiveApiKey({
provider: "custom-models-custom-local",
cfg: {},
flagName: "--custom-api-key",
envVar: "CUSTOM_API_KEY",
envVarName: "CUSTOM_API_KEY",
runtime: runtime as never,
secretInputMode: "ref",
});
expect(result).toEqual({
key: "custom-env-key",
source: "env",
envVarName: "CUSTOM_API_KEY",
});
expect(runtime.exit).not.toHaveBeenCalled();
} finally {
if (previousCustomApiKey === undefined) {
delete process.env.CUSTOM_API_KEY;
} else {
process.env.CUSTOM_API_KEY = previousCustomApiKey;
}
}
});
it("falls back to a matching API-key profile after flag and env are absent", async () => {
const runtime = createRuntime();
authStore.profiles["custom-models-custom-local:default"] = {

View File

@@ -72,4 +72,42 @@ describe("applyNonInteractiveAuthChoice", () => {
expect(runtime.exit).toHaveBeenCalledWith(1);
expect(applyNonInteractivePluginProviderChoice).toHaveBeenCalledOnce();
});
it("stores custom provider env refs through the local auth-choice seam", async () => {
const runtime = createRuntime();
const nextConfig = { agents: { defaults: {} } } as OpenClawConfig;
resolveNonInteractiveApiKey.mockResolvedValueOnce({
key: "custom-env-key",
source: "env",
envVarName: "CUSTOM_API_KEY",
});
const result = await applyNonInteractiveAuthChoice({
nextConfig,
authChoice: "custom-api-key",
opts: {
customBaseUrl: "https://models.custom.local/v1",
customModelId: "local-large",
secretInputMode: "ref",
} as never,
runtime: runtime as never,
baseConfig: nextConfig,
});
expect(result?.models?.providers?.["custom-models-custom-local"]?.apiKey).toEqual({
source: "env",
provider: "default",
id: "CUSTOM_API_KEY",
});
expect(result?.agents?.defaults?.model?.primary).toBe("custom-models-custom-local/local-large");
expect(resolveNonInteractiveApiKey).toHaveBeenCalledWith(
expect.objectContaining({
provider: "custom-models-custom-local",
flagName: "--custom-api-key",
envVar: "CUSTOM_API_KEY",
envVarName: "CUSTOM_API_KEY",
secretInputMode: "ref",
}),
);
});
});