From 511e6c4189b309e5f451db6cc172972634e307f2 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 5 Apr 2026 16:09:19 +0100 Subject: [PATCH] test: untangle provider tests from extension internals --- ...-config.providers.anthropic-vertex.test.ts | 50 ++++--- ...ig.providers.cloudflare-ai-gateway.test.ts | 32 ++++- ...ls-config.providers.discovery-auth.test.ts | 70 +--------- .../models-config.providers.moonshot.test.ts | 39 ++++-- .../models-config.providers.nvidia.test.ts | 126 ++++++++++-------- ...dels-config.providers.openai-codex.test.ts | 58 -------- .../models-config.providers.stepfun.test.ts | 88 ++++-------- 7 files changed, 189 insertions(+), 274 deletions(-) delete mode 100644 src/agents/models-config.providers.openai-codex.test.ts diff --git a/src/agents/models-config.providers.anthropic-vertex.test.ts b/src/agents/models-config.providers.anthropic-vertex.test.ts index 7769e2ac94d..15997f80dda 100644 --- a/src/agents/models-config.providers.anthropic-vertex.test.ts +++ b/src/agents/models-config.providers.anthropic-vertex.test.ts @@ -2,19 +2,16 @@ import { mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import { - buildAnthropicVertexProvider, - mergeImplicitAnthropicVertexProvider, - resolveImplicitAnthropicVertexProvider, -} from "../../extensions/anthropic-vertex/api.js"; import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js"; describe("anthropic-vertex implicit provider", () => { - it("offers Claude models when GOOGLE_CLOUD_PROJECT_ID is set", () => { - const provider = resolveImplicitAnthropicVertexProvider({ + it("does not auto-enable from GOOGLE_CLOUD_PROJECT_ID alone", async () => { + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const providers = await resolveImplicitProvidersForTest({ + agentDir, env: { GOOGLE_CLOUD_PROJECT_ID: "vertex-project" }, }); - expect(provider).toBeNull(); + expect(providers?.["anthropic-vertex"]).toBeUndefined(); }); it("accepts ADC credentials when the file includes a project_id", async () => { @@ -127,32 +124,41 @@ describe("anthropic-vertex implicit provider", () => { } }); - it("accepts explicit metadata auth opt-in without local credential files", () => { - const provider = resolveImplicitAnthropicVertexProvider({ + it("accepts explicit metadata auth opt-in without local credential files", async () => { + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const providers = await resolveImplicitProvidersForTest({ + agentDir, env: { ANTHROPIC_VERTEX_USE_GCP_METADATA: "true", GOOGLE_CLOUD_LOCATION: "us-east5", }, }); - expect(provider?.baseUrl).toBe("https://us-east5-aiplatform.googleapis.com"); + expect(providers?.["anthropic-vertex"]?.baseUrl).toBe( + "https://us-east5-aiplatform.googleapis.com", + ); }); it("merges the bundled catalog into explicit anthropic-vertex provider overrides", async () => { - const provider = mergeImplicitAnthropicVertexProvider({ - existing: { - baseUrl: "https://europe-west4-aiplatform.googleapis.com", - headers: { "x-test-header": "1" }, + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const providers = await resolveImplicitProvidersForTest({ + agentDir, + env: { + ANTHROPIC_VERTEX_USE_GCP_METADATA: "true", + GOOGLE_CLOUD_LOCATION: "us-east5", }, - implicit: buildAnthropicVertexProvider({ - env: { - GOOGLE_CLOUD_LOCATION: "us-east5", + explicitProviders: { + "anthropic-vertex": { + baseUrl: "https://europe-west4-aiplatform.googleapis.com", + headers: { "x-test-header": "1" }, }, - }), + }, }); - expect(provider.baseUrl).toBe("https://europe-west4-aiplatform.googleapis.com"); - expect(provider.headers).toEqual({ "x-test-header": "1" }); - expect(provider.models?.map((model) => model.id)).toEqual([ + expect(providers?.["anthropic-vertex"]?.baseUrl).toBe( + "https://europe-west4-aiplatform.googleapis.com", + ); + expect(providers?.["anthropic-vertex"]?.headers).toEqual({ "x-test-header": "1" }); + expect(providers?.["anthropic-vertex"]?.models?.map((model) => model.id)).toEqual([ "claude-opus-4-6", "claude-sonnet-4-6", ]); diff --git a/src/agents/models-config.providers.cloudflare-ai-gateway.test.ts b/src/agents/models-config.providers.cloudflare-ai-gateway.test.ts index c221f4b21d6..5c69349b8f3 100644 --- a/src/agents/models-config.providers.cloudflare-ai-gateway.test.ts +++ b/src/agents/models-config.providers.cloudflare-ai-gateway.test.ts @@ -1,12 +1,42 @@ import { describe, expect, it } from "vitest"; -import { buildCloudflareAiGatewayCatalogProvider } from "../../extensions/cloudflare-ai-gateway/api.js"; import { captureEnv } from "../test-utils/env.js"; import { NON_ENV_SECRETREF_MARKER } from "./model-auth-markers.js"; +import { resolveApiKeyFromCredential } from "./models-config.providers.secrets.js"; function expectedCloudflareGatewayBaseUrl(accountId: string, gatewayId: string): string { return `https://gateway.ai.cloudflare.com/v1/${accountId}/${gatewayId}/anthropic`; } +function buildCloudflareAiGatewayCatalogProvider(params: { + credential: + | Parameters[0] + | { + metadata?: { + accountId?: string; + gatewayId?: string; + }; + } + | undefined; + envApiKey?: string; +}) { + const apiKey = + params.envApiKey?.trim() || + resolveApiKeyFromCredential( + params.credential as Parameters[0], + )?.apiKey; + const accountId = params.credential?.metadata?.accountId?.trim(); + const gatewayId = params.credential?.metadata?.gatewayId?.trim(); + if (!apiKey || !accountId || !gatewayId) { + return null; + } + return { + baseUrl: expectedCloudflareGatewayBaseUrl(accountId, gatewayId), + api: "anthropic-messages", + apiKey, + models: [{ id: "cloudflare-ai-gateway" }], + }; +} + describe("cloudflare-ai-gateway profile provenance", () => { it("prefers env keyRef marker over runtime plaintext for persistence", () => { const envSnapshot = captureEnv(["CLOUDFLARE_AI_GATEWAY_API_KEY"]); diff --git a/src/agents/models-config.providers.discovery-auth.test.ts b/src/agents/models-config.providers.discovery-auth.test.ts index 35296233093..835c1ce38f4 100644 --- a/src/agents/models-config.providers.discovery-auth.test.ts +++ b/src/agents/models-config.providers.discovery-auth.test.ts @@ -1,50 +1,9 @@ -import { afterEach, describe, expect, it, vi } from "vitest"; -import { buildHuggingfaceProvider } from "../../extensions/huggingface/provider-catalog.js"; -import { buildVllmProvider } from "../../extensions/vllm/models.js"; +import { describe, expect, it } from "vitest"; import { NON_ENV_SECRETREF_MARKER } from "./model-auth-markers.js"; import { resolveApiKeyFromCredential } from "./models-config.providers.secrets.js"; describe("provider discovery auth marker guardrails", () => { - let originalVitest: string | undefined; - let originalNodeEnv: string | undefined; - let originalFetch: typeof globalThis.fetch | undefined; - - afterEach(() => { - if (originalVitest !== undefined) { - process.env.VITEST = originalVitest; - } else { - delete process.env.VITEST; - } - if (originalNodeEnv !== undefined) { - process.env.NODE_ENV = originalNodeEnv; - } else { - delete process.env.NODE_ENV; - } - if (originalFetch) { - globalThis.fetch = originalFetch; - } - }); - - function enableDiscovery() { - originalVitest = process.env.VITEST ?? "true"; - originalNodeEnv = process.env.NODE_ENV ?? "test"; - originalFetch = globalThis.fetch; - delete process.env.VITEST; - delete process.env.NODE_ENV; - } - - function installFetchMock(response?: unknown) { - const fetchMock = - response === undefined - ? vi.fn() - : vi.fn().mockResolvedValue({ ok: true, json: async () => response }); - globalThis.fetch = fetchMock as unknown as typeof fetch; - return fetchMock; - } - - it("does not send marker value as vLLM bearer token during discovery", async () => { - enableDiscovery(); - const fetchMock = installFetchMock({ data: [] }); + it("suppresses discovery secrets for marker-backed vLLM credentials", () => { const resolved = resolveApiKeyFromCredential({ type: "api_key", provider: "vllm", @@ -52,16 +11,10 @@ describe("provider discovery auth marker guardrails", () => { }); expect(resolved?.apiKey).toBe(NON_ENV_SECRETREF_MARKER); - await buildVllmProvider({ apiKey: resolved?.discoveryApiKey }); - const request = fetchMock.mock.calls[0]?.[1] as - | { headers?: Record } - | undefined; - expect(request?.headers?.Authorization).toBeUndefined(); + expect(resolved?.discoveryApiKey).toBeUndefined(); }); - it("does not call Hugging Face discovery with marker-backed credentials", async () => { - enableDiscovery(); - const fetchMock = installFetchMock(); + it("suppresses discovery secrets for marker-backed Hugging Face credentials", () => { const resolved = resolveApiKeyFromCredential({ type: "api_key", provider: "huggingface", @@ -69,16 +22,10 @@ describe("provider discovery auth marker guardrails", () => { }); expect(resolved?.apiKey).toBe(NON_ENV_SECRETREF_MARKER); - await buildHuggingfaceProvider(resolved?.discoveryApiKey); - const huggingfaceCalls = fetchMock.mock.calls.filter(([url]) => - String(url).includes("router.huggingface.co"), - ); - expect(huggingfaceCalls).toHaveLength(0); + expect(resolved?.discoveryApiKey).toBeUndefined(); }); - it("keeps all-caps plaintext API keys for authenticated discovery", async () => { - enableDiscovery(); - const fetchMock = installFetchMock({ data: [{ id: "vllm/test-model" }] }); + it("keeps all-caps plaintext API keys for authenticated discovery", () => { const resolved = resolveApiKeyFromCredential({ type: "api_key", provider: "vllm", @@ -86,9 +33,6 @@ describe("provider discovery auth marker guardrails", () => { }); expect(resolved?.apiKey).toBe("ALLCAPS_SAMPLE"); - await buildVllmProvider({ apiKey: resolved?.discoveryApiKey }); - const vllmCall = fetchMock.mock.calls.find(([url]) => String(url).includes(":8000")); - const request = vllmCall?.[1] as { headers?: Record } | undefined; - expect(request?.headers?.Authorization).toBe("Bearer ALLCAPS_SAMPLE"); + expect(resolved?.discoveryApiKey).toBe("ALLCAPS_SAMPLE"); }); }); diff --git a/src/agents/models-config.providers.moonshot.test.ts b/src/agents/models-config.providers.moonshot.test.ts index e142749fbbd..1501929fdca 100644 --- a/src/agents/models-config.providers.moonshot.test.ts +++ b/src/agents/models-config.providers.moonshot.test.ts @@ -1,11 +1,28 @@ import { describe, expect, it } from "vitest"; -import { - applyMoonshotNativeStreamingUsageCompat, - buildMoonshotProvider, - MOONSHOT_CN_BASE_URL, -} from "../../extensions/moonshot/api.js"; +import { applyProviderNativeStreamingUsageCompat } from "../plugin-sdk/provider-catalog-shared.js"; import { resolveMissingProviderApiKey } from "./models-config.providers.secrets.js"; +const MOONSHOT_BASE_URL = "https://api.moonshot.ai/v1"; +const MOONSHOT_CN_BASE_URL = "https://api.moonshot.cn/v1"; + +function buildMoonshotProvider() { + return { + baseUrl: MOONSHOT_BASE_URL, + api: "openai-completions", + models: [ + { + id: "kimi-k2.5", + name: "Kimi K2.5", + reasoning: false, + input: ["text", "image"], + cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, + contextWindow: 262144, + maxTokens: 262144, + }, + ], + }; +} + describe("moonshot implicit provider (#33637)", () => { it("uses explicit CN baseUrl when provided", () => { const provider = { @@ -16,8 +33,10 @@ describe("moonshot implicit provider (#33637)", () => { expect(provider.baseUrl).toBe(MOONSHOT_CN_BASE_URL); expect(provider.models?.[0]?.compat?.supportsUsageInStreaming).toBeUndefined(); expect( - applyMoonshotNativeStreamingUsageCompat(provider).models?.[0]?.compat - ?.supportsUsageInStreaming, + applyProviderNativeStreamingUsageCompat({ + providerId: "moonshot", + providerConfig: provider, + }).models?.[0]?.compat?.supportsUsageInStreaming, ).toBe(true); }); @@ -30,8 +49,10 @@ describe("moonshot implicit provider (#33637)", () => { expect(provider.baseUrl).toBe("https://proxy.example.com/v1"); expect(provider.models?.[0]?.compat?.supportsUsageInStreaming).toBeUndefined(); expect( - applyMoonshotNativeStreamingUsageCompat(provider).models?.[0]?.compat - ?.supportsUsageInStreaming, + applyProviderNativeStreamingUsageCompat({ + providerId: "moonshot", + providerConfig: provider, + }).models?.[0]?.compat?.supportsUsageInStreaming, ).toBeUndefined(); }); diff --git a/src/agents/models-config.providers.nvidia.test.ts b/src/agents/models-config.providers.nvidia.test.ts index cc6e9e0208d..f1bca13e30a 100644 --- a/src/agents/models-config.providers.nvidia.test.ts +++ b/src/agents/models-config.providers.nvidia.test.ts @@ -1,21 +1,38 @@ import { mkdtempSync } from "node:fs"; -import { writeFile } from "node:fs/promises"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; import { withEnvAsync } from "../test-utils/env.js"; import { resolveApiKeyForProvider } from "./model-auth.js"; -import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js"; +import { + installModelsConfigTestHooks, + resolveImplicitProvidersForTest, +} from "./models-config.e2e-harness.js"; +import { + resolveEnvApiKeyVarName, + resolveMissingProviderApiKey, +} from "./models-config.providers.secrets.js"; + +const NVIDIA_BASE_URL = "https://integrate.api.nvidia.com/v1"; +const MINIMAX_BASE_URL = "https://api.minimax.io/anthropic"; +const VLLM_DEFAULT_BASE_URL = "http://127.0.0.1:8000/v1"; + +installModelsConfigTestHooks(); describe("NVIDIA provider", () => { - it("should include nvidia when NVIDIA_API_KEY is configured", async () => { - const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); - const providers = await resolveImplicitProvidersForTest({ - agentDir, - env: { NVIDIA_API_KEY: "test-key" }, + it("should include nvidia when NVIDIA_API_KEY is configured", () => { + const provider = resolveMissingProviderApiKey({ + providerKey: "nvidia", + provider: { + baseUrl: NVIDIA_BASE_URL, + api: "openai-completions", + models: [{ id: "nvidia/test-model" }], + }, + env: { NVIDIA_API_KEY: "test-key" } as NodeJS.ProcessEnv, + profileApiKey: undefined, }); - expect(providers?.nvidia).toBeDefined(); - expect(providers?.nvidia?.models?.length).toBeGreaterThan(0); + expect(provider.apiKey).toBe("NVIDIA_API_KEY"); + expect(provider.models?.length).toBeGreaterThan(0); }); it("resolves the nvidia api key value from env", async () => { @@ -34,16 +51,23 @@ describe("NVIDIA provider", () => { }); describe("MiniMax implicit provider (#15275)", () => { - it("should use anthropic-messages API for API-key provider", { timeout: 240_000 }, async () => { - const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); - const providers = await resolveImplicitProvidersForTest({ - agentDir, - env: { MINIMAX_API_KEY: "test-key" }, + it("should use anthropic-messages API for API-key provider", () => { + const provider = resolveMissingProviderApiKey({ + providerKey: "minimax", + provider: { + baseUrl: MINIMAX_BASE_URL, + api: "anthropic-messages", + authHeader: true, + models: [{ id: "MiniMax-M2.7" }], + }, + env: { MINIMAX_API_KEY: "test-key" } as NodeJS.ProcessEnv, + profileApiKey: undefined, }); - expect(providers?.minimax).toBeDefined(); - expect(providers?.minimax?.api).toBe("anthropic-messages"); - expect(providers?.minimax?.authHeader).toBe(true); - expect(providers?.minimax?.baseUrl).toBe("https://api.minimax.io/anthropic"); + + expect(provider.api).toBe("anthropic-messages"); + expect(provider.authHeader).toBe(true); + expect(provider.apiKey).toBe("MINIMAX_API_KEY"); + expect(provider.baseUrl).toBe("https://api.minimax.io/anthropic"); }); it("should respect MINIMAX_API_HOST env var for CN endpoint (#34487)", async () => { @@ -55,69 +79,55 @@ describe("MiniMax implicit provider (#15275)", () => { MINIMAX_API_HOST: "https://api.minimaxi.com", }, }); - expect(providers?.minimax).toBeDefined(); + expect(providers?.minimax?.baseUrl).toBe("https://api.minimaxi.com/anthropic"); expect(providers?.["minimax-portal"]?.baseUrl).toBe("https://api.minimaxi.com/anthropic"); }); it("should set authHeader for minimax portal provider", async () => { const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); - await writeFile( - join(agentDir, "auth-profiles.json"), - JSON.stringify( - { - version: 1, - profiles: { - "minimax-portal:default": { - type: "oauth", - provider: "minimax-portal", - access: "token", - refresh: "refresh-token", - expires: Date.now() + 60_000, - }, - }, - }, - null, - 2, - ), - "utf8", - ); - - const providers = await resolveImplicitProvidersForTest({ agentDir }); + const providers = await resolveImplicitProvidersForTest({ + agentDir, + env: { MINIMAX_OAUTH_TOKEN: "portal-token" }, + }); expect(providers?.["minimax-portal"]?.authHeader).toBe(true); }); it("should include minimax portal provider when MINIMAX_OAUTH_TOKEN is configured", async () => { + expect( + resolveEnvApiKeyVarName("minimax-portal", { + MINIMAX_OAUTH_TOKEN: "portal-token", + } as NodeJS.ProcessEnv), + ).toBe("MINIMAX_OAUTH_TOKEN"); const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); const providers = await resolveImplicitProvidersForTest({ agentDir, env: { MINIMAX_OAUTH_TOKEN: "portal-token" }, }); - expect(providers?.["minimax-portal"]).toBeDefined(); expect(providers?.["minimax-portal"]?.authHeader).toBe(true); }); }); describe("vLLM provider", () => { - it("should not include vllm when no API key is configured", async () => { - const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); - const providers = await resolveImplicitProvidersForTest({ agentDir, env: {} }); - expect(providers?.vllm).toBeUndefined(); + it("should not include vllm when no API key is configured", () => { + expect(resolveEnvApiKeyVarName("vllm", {} as NodeJS.ProcessEnv)).toBeUndefined(); }); - it("should include vllm when VLLM_API_KEY is set", async () => { - const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); - const providers = await resolveImplicitProvidersForTest({ - agentDir, - env: { VLLM_API_KEY: "test-key" }, + it("should include vllm when VLLM_API_KEY is set", () => { + const provider = resolveMissingProviderApiKey({ + providerKey: "vllm", + provider: { + baseUrl: VLLM_DEFAULT_BASE_URL, + api: "openai-completions", + models: [], + }, + env: { VLLM_API_KEY: "test-key" } as NodeJS.ProcessEnv, + profileApiKey: undefined, }); - expect(providers?.vllm).toBeDefined(); - expect(providers?.vllm?.apiKey).toBe("VLLM_API_KEY"); - expect(providers?.vllm?.baseUrl).toBe("http://127.0.0.1:8000/v1"); - expect(providers?.vllm?.api).toBe("openai-completions"); - - // Note: discovery is disabled in test environments (VITEST check) - expect(providers?.vllm?.models).toEqual([]); + expect(provider.apiKey).toBe("VLLM_API_KEY"); + expect(provider.baseUrl).toBe(VLLM_DEFAULT_BASE_URL); + expect(provider.api).toBe("openai-completions"); + expect(provider.models).toEqual([]); }); }); diff --git a/src/agents/models-config.providers.openai-codex.test.ts b/src/agents/models-config.providers.openai-codex.test.ts deleted file mode 100644 index 77591b76a06..00000000000 --- a/src/agents/models-config.providers.openai-codex.test.ts +++ /dev/null @@ -1,58 +0,0 @@ -import { beforeAll, describe, expect, it } from "vitest"; - -let buildOpenAICodexProviderPlugin: typeof import("../../extensions/openai/openai-codex-provider.js").buildOpenAICodexProviderPlugin; - -describe("openai-codex implicit provider", () => { - beforeAll(async () => { - ({ buildOpenAICodexProviderPlugin } = - await import("../../extensions/openai/openai-codex-provider.js")); - }); - - it("normalizes generated openai-codex rows back to the Codex transport", () => { - const provider = buildOpenAICodexProviderPlugin(); - const normalized = provider.normalizeResolvedModel?.({ - provider: "openai-codex", - model: { - id: "gpt-5.4", - name: "GPT-5.4", - provider: "openai-codex", - api: "openai-responses", - baseUrl: "https://api.openai.com/v1", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 1_000_000, - maxTokens: 100_000, - }, - } as never); - - expect(normalized).toMatchObject({ - baseUrl: "https://chatgpt.com/backend-api", - api: "openai-codex-responses", - }); - }); - - it("preserves an existing Codex baseUrl for explicit openai-codex config", () => { - const provider = buildOpenAICodexProviderPlugin(); - const normalized = provider.normalizeResolvedModel?.({ - provider: "openai-codex", - model: { - id: "gpt-5.4", - name: "GPT-5.4", - provider: "openai-codex", - api: "openai-codex-responses", - baseUrl: "https://chatgpt.com/backend-api", - reasoning: true, - input: ["text"], - cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, - contextWindow: 1_000_000, - maxTokens: 100_000, - }, - } as never); - - expect(normalized).toMatchObject({ - baseUrl: "https://chatgpt.com/backend-api", - api: "openai-codex-responses", - }); - }); -}); diff --git a/src/agents/models-config.providers.stepfun.test.ts b/src/agents/models-config.providers.stepfun.test.ts index 701e8bdfda6..38da8fd3443 100644 --- a/src/agents/models-config.providers.stepfun.test.ts +++ b/src/agents/models-config.providers.stepfun.test.ts @@ -2,37 +2,26 @@ import { mkdtempSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { describe, expect, it } from "vitest"; -import stepfunPlugin from "../../extensions/stepfun/index.js"; -import { - buildStepFunPlanProvider, - buildStepFunProvider, -} from "../../extensions/stepfun/provider-catalog.js"; -import { - registerProviderPlugin, - requireRegisteredProvider, -} from "../../test/helpers/plugins/provider-registration.js"; import { upsertAuthProfile } from "./auth-profiles.js"; -import { resolveImplicitProvidersForTest } from "./models-config.e2e-harness.js"; -import { resolveMissingProviderApiKey } from "./models-config.providers.secrets.js"; +import { + installModelsConfigTestHooks, + resolveImplicitProvidersForTest, +} from "./models-config.e2e-harness.js"; const EXPECTED_STANDARD_MODELS = ["step-3.5-flash"]; const EXPECTED_PLAN_MODELS = ["step-3.5-flash", "step-3.5-flash-2603"]; +installModelsConfigTestHooks(); + describe("StepFun provider catalog", () => { it("includes standard and Step Plan providers when STEPFUN_API_KEY is configured", async () => { - const env = { STEPFUN_API_KEY: "test-stepfun-key" } as NodeJS.ProcessEnv; - const standardProvider = resolveMissingProviderApiKey({ - providerKey: "stepfun", - provider: buildStepFunProvider(), - env, - profileApiKey: undefined, - }); - const planProvider = resolveMissingProviderApiKey({ - providerKey: "stepfun-plan", - provider: buildStepFunPlanProvider(), - env, - profileApiKey: undefined, + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const providers = await resolveImplicitProvidersForTest({ + agentDir, + env: { STEPFUN_API_KEY: "test-stepfun-key" }, }); + const standardProvider = providers?.stepfun; + const planProvider = providers?.["stepfun-plan"]; expect(standardProvider).toMatchObject({ baseUrl: "https://api.stepfun.ai/v1", @@ -81,51 +70,24 @@ describe("StepFun provider catalog", () => { }); it("uses China endpoints when explicit config points the paired surface at the China host", async () => { - const { providers } = await registerProviderPlugin({ - plugin: stepfunPlugin, - id: "stepfun", - name: "StepFun", - }); - const standardProvider = requireRegisteredProvider(providers, "stepfun"); - const planProvider = requireRegisteredProvider(providers, "stepfun-plan"); - const config = { - models: { - providers: { - "stepfun-plan": { - baseUrl: "https://api.stepfun.com/step_plan/v1", - models: [], + const agentDir = mkdtempSync(join(tmpdir(), "openclaw-test-")); + const providers = await resolveImplicitProvidersForTest({ + agentDir, + env: { STEPFUN_API_KEY: "test-stepfun-key" }, + config: { + models: { + providers: { + "stepfun-plan": { + baseUrl: "https://api.stepfun.com/step_plan/v1", + models: [], + }, }, }, }, - }; - const resolveProviderApiKey = () => ({ - apiKey: "STEPFUN_API_KEY", - discoveryApiKey: "test-stepfun-key", - }); - const resolveProviderAuth = () => ({ - apiKey: "STEPFUN_API_KEY", - discoveryApiKey: "test-stepfun-key", - mode: "api_key" as const, - source: "env" as const, }); - const standardCatalog = await standardProvider.catalog?.run({ - agentDir: "/tmp/openclaw-stepfun-test", - env: { STEPFUN_API_KEY: "test-stepfun-key" } as NodeJS.ProcessEnv, - config, - resolveProviderApiKey, - resolveProviderAuth, - } as never); - const planCatalog = await planProvider.catalog?.run({ - agentDir: "/tmp/openclaw-stepfun-test", - env: { STEPFUN_API_KEY: "test-stepfun-key" } as NodeJS.ProcessEnv, - config, - resolveProviderApiKey, - resolveProviderAuth, - } as never); - - expect(standardCatalog?.provider.baseUrl).toBe("https://api.stepfun.com/v1"); - expect(planCatalog?.provider.baseUrl).toBe("https://api.stepfun.com/step_plan/v1"); + expect(providers?.stepfun?.baseUrl).toBe("https://api.stepfun.com/v1"); + expect(providers?.["stepfun-plan"]?.baseUrl).toBe("https://api.stepfun.com/step_plan/v1"); }); it("discovers both providers from shared regional auth profiles", async () => {