diff --git a/src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts b/src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts index 36944d67601..036f4d00824 100644 --- a/src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts +++ b/src/agents/models-config.fills-missing-provider-apikey-from-env-var.test.ts @@ -60,13 +60,31 @@ function createMergeConfigProvider() { }; } -async function runCustomProviderMergeTest(params: { - seedProvider: { - baseUrl: string; - apiKey: string; - api: string; - models: Array<{ id: string; name: string; input: string[]; api?: string }>; +type MergeSeedProvider = { + baseUrl: string; + apiKey: string; + api: string; + models: Array<{ id: string; name: string; input: string[]; api?: string }>; +}; + +type MergeConfigApiKeyRef = { + source: "env"; + provider: "default"; + id: string; +}; + +function createAgentSeedProvider(overrides: Partial = {}): MergeSeedProvider { + return { + baseUrl: "https://agent.example/v1", + apiKey: "AGENT_KEY", // pragma: allowlist secret + api: "openai-responses", + models: [{ id: "agent-model", name: "Agent model", input: ["text"] }], + ...overrides, }; +} + +async function runCustomProviderMergeTest(params: { + seedProvider: MergeSeedProvider; existingProviderKey?: string; configProviderKey?: string; }) { @@ -86,6 +104,56 @@ async function runCustomProviderMergeTest(params: { }>(); } +async function expectCustomProviderMergeResult(params: { + seedProvider?: MergeSeedProvider; + existingProviderKey?: string; + configProviderKey?: string; + expectedApiKey: string; + expectedBaseUrl: string; +}) { + await withTempHome(async () => { + const parsed = await runCustomProviderMergeTest({ + seedProvider: params.seedProvider ?? createAgentSeedProvider(), + existingProviderKey: params.existingProviderKey, + configProviderKey: params.configProviderKey, + }); + expect(parsed.providers.custom?.apiKey).toBe(params.expectedApiKey); + expect(parsed.providers.custom?.baseUrl).toBe(params.expectedBaseUrl); + }); +} + +async function expectCustomProviderApiKeyRewrite(params: { + existingApiKey: string; + configuredApiKey: string | MergeConfigApiKeyRef; + expectedApiKey: string; +}) { + await withTempHome(async () => { + await writeAgentModelsJson({ + providers: { + custom: createAgentSeedProvider({ apiKey: params.existingApiKey }), + }, + }); + + await ensureOpenClawModelsJson({ + models: { + mode: "merge", + providers: { + custom: { + ...createMergeConfigProvider(), + apiKey: params.configuredApiKey, + }, + }, + }, + }); + + const parsed = await readGeneratedModelsJson<{ + providers: Record; + }>(); + expect(parsed.providers.custom?.apiKey).toBe(params.expectedApiKey); + expect(parsed.providers.custom?.baseUrl).toBe("https://config.example/v1"); + }); +} + function createMoonshotConfig(overrides: { contextWindow: number; maxTokens: number; @@ -301,49 +369,26 @@ describe("models-config", () => { }); it("preserves non-empty agent apiKey but lets explicit config baseUrl win in merge mode", async () => { - await withTempHome(async () => { - const parsed = await runCustomProviderMergeTest({ - seedProvider: { - baseUrl: "https://agent.example/v1", - apiKey: "AGENT_KEY", // pragma: allowlist secret - api: "openai-responses", - models: [{ id: "agent-model", name: "Agent model", input: ["text"] }], - }, - }); - expect(parsed.providers.custom?.apiKey).toBe("AGENT_KEY"); - expect(parsed.providers.custom?.baseUrl).toBe("https://config.example/v1"); + await expectCustomProviderMergeResult({ + expectedApiKey: "AGENT_KEY", + expectedBaseUrl: "https://config.example/v1", }); }); it("lets explicit config baseUrl win in merge mode when the config provider key is normalized", async () => { - await withTempHome(async () => { - const parsed = await runCustomProviderMergeTest({ - seedProvider: { - baseUrl: "https://agent.example/v1", - apiKey: "AGENT_KEY", // pragma: allowlist secret - api: "openai-responses", - models: [{ id: "agent-model", name: "Agent model", input: ["text"] }], - }, - existingProviderKey: "custom", - configProviderKey: " custom ", - }); - expect(parsed.providers.custom?.apiKey).toBe("AGENT_KEY"); - expect(parsed.providers.custom?.baseUrl).toBe("https://config.example/v1"); + await expectCustomProviderMergeResult({ + existingProviderKey: "custom", + configProviderKey: " custom ", + expectedApiKey: "AGENT_KEY", + expectedBaseUrl: "https://config.example/v1", }); }); it("replaces stale merged baseUrl when the provider api changes", async () => { - await withTempHome(async () => { - const parsed = await runCustomProviderMergeTest({ - seedProvider: { - baseUrl: "https://agent.example/v1", - apiKey: "AGENT_KEY", // pragma: allowlist secret - api: "openai-completions", - models: [{ id: "agent-model", name: "Agent model", input: ["text"] }], - }, - }); - expect(parsed.providers.custom?.apiKey).toBe("AGENT_KEY"); - expect(parsed.providers.custom?.baseUrl).toBe("https://config.example/v1"); + await expectCustomProviderMergeResult({ + seedProvider: createAgentSeedProvider({ api: "openai-completions" }), + expectedApiKey: "AGENT_KEY", + expectedBaseUrl: "https://config.example/v1", }); }); @@ -370,34 +415,14 @@ describe("models-config", () => { }); it("replaces stale merged apiKey when provider is SecretRef-managed in current config", async () => { - await withTempHome(async () => { - await writeAgentModelsJson({ - providers: { - custom: { - baseUrl: "https://agent.example/v1", - apiKey: "STALE_AGENT_KEY", // pragma: allowlist secret - api: "openai-responses", - models: [{ id: "agent-model", name: "Agent model", input: ["text"] }], - }, - }, - }); - await ensureOpenClawModelsJson({ - models: { - mode: "merge", - providers: { - custom: { - ...createMergeConfigProvider(), - apiKey: { source: "env", provider: "default", id: "CUSTOM_PROVIDER_API_KEY" }, // pragma: allowlist secret - }, - }, - }, - }); - - const parsed = await readGeneratedModelsJson<{ - providers: Record; - }>(); - expect(parsed.providers.custom?.apiKey).toBe("CUSTOM_PROVIDER_API_KEY"); // pragma: allowlist secret - expect(parsed.providers.custom?.baseUrl).toBe("https://config.example/v1"); + await expectCustomProviderApiKeyRewrite({ + existingApiKey: "STALE_AGENT_KEY", // pragma: allowlist secret + configuredApiKey: { + source: "env", + provider: "default", + id: "CUSTOM_PROVIDER_API_KEY", // pragma: allowlist secret + }, + expectedApiKey: "CUSTOM_PROVIDER_API_KEY", // pragma: allowlist secret }); }); @@ -449,34 +474,10 @@ describe("models-config", () => { }); it("replaces stale non-env marker when provider transitions back to plaintext config", async () => { - await withTempHome(async () => { - await writeAgentModelsJson({ - providers: { - custom: { - baseUrl: "https://agent.example/v1", - apiKey: NON_ENV_SECRETREF_MARKER, - api: "openai-responses", - models: [{ id: "agent-model", name: "Agent model", input: ["text"] }], - }, - }, - }); - - await ensureOpenClawModelsJson({ - models: { - mode: "merge", - providers: { - custom: { - ...createMergeConfigProvider(), - apiKey: "ALLCAPS_SAMPLE", // pragma: allowlist secret - }, - }, - }, - }); - - const parsed = await readGeneratedModelsJson<{ - providers: Record; - }>(); - expect(parsed.providers.custom?.apiKey).toBe("ALLCAPS_SAMPLE"); + await expectCustomProviderApiKeyRewrite({ + existingApiKey: NON_ENV_SECRETREF_MARKER, + configuredApiKey: "ALLCAPS_SAMPLE", // pragma: allowlist secret + expectedApiKey: "ALLCAPS_SAMPLE", // pragma: allowlist secret }); });