test: untangle provider tests from extension internals

This commit is contained in:
Peter Steinberger 2026-04-05 16:09:19 +01:00
parent f64a058348
commit 511e6c4189
No known key found for this signature in database
7 changed files with 189 additions and 274 deletions

View File

@ -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",
]);

View File

@ -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<typeof resolveApiKeyFromCredential>[0]
| {
metadata?: {
accountId?: string;
gatewayId?: string;
};
}
| undefined;
envApiKey?: string;
}) {
const apiKey =
params.envApiKey?.trim() ||
resolveApiKeyFromCredential(
params.credential as Parameters<typeof resolveApiKeyFromCredential>[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"]);

View File

@ -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<string, string> }
| 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<string, string> } | undefined;
expect(request?.headers?.Authorization).toBe("Bearer ALLCAPS_SAMPLE");
expect(resolved?.discoveryApiKey).toBe("ALLCAPS_SAMPLE");
});
});

View File

@ -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();
});

View File

@ -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([]);
});
});

View File

@ -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",
});
});
});

View File

@ -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 () => {