






















@@ -6,9 +6,7 @@ import {
66import { beforeEach, describe, expect, it, vi, type MockedFunction } from "vitest";
77import {
88buildClawRouterProviderConfig,
9-clearClawRouterCatalogForTests,
109normalizeClawRouterResolvedModel,
11-resolveDiscoveredClawRouterModel,
1210} from "./provider-catalog.js";
13111412const CATALOG = {
@@ -101,12 +99,12 @@ const CATALOG = {
10199],
102100};
103101104-function buildFetchGuard(): {
102+function buildFetchGuard(catalog: unknown = CATALOG): {
105103fetchGuard: LiveModelCatalogFetchGuard;
106104fetchGuardMock: MockedFunction<LiveModelCatalogFetchGuard>;
107105} {
108106const fetchGuardMock: MockedFunction<LiveModelCatalogFetchGuard> = vi.fn(async () => ({
109-response: new Response(JSON.stringify(CATALOG)),
107+response: new Response(JSON.stringify(catalog)),
110108finalUrl: "https://clawrouter.example/v1/catalog",
111109release: async () => undefined,
112110}));
@@ -116,7 +114,6 @@ function buildFetchGuard(): {
116114describe("clawrouter provider catalog", () => {
117115beforeEach(() => {
118116clearLiveCatalogCacheForTests();
119-clearClawRouterCatalogForTests();
120117});
121118122119it("maps credential-scoped catalog rows to their real provider transports", async () => {
@@ -131,7 +128,6 @@ describe("clawrouter provider catalog", () => {
131128expect(provider).toMatchObject({
132129api: "openai-responses",
133130apiKey: "clawrouter-test-key",
134-authHeader: true,
135131baseUrl: "https://clawrouter.example/v1",
136132});
137133expect(provider.models.map((model) => model.id)).toEqual([
@@ -163,20 +159,9 @@ describe("clawrouter provider catalog", () => {
163159id: "claude-sonnet-4-5-20250929",
164160api: "anthropic-messages",
165161baseUrl: "https://clawrouter.example/v1/native/anthropic",
166-headers: {
167-Authorization: "Bearer clawrouter-test-key",
168-},
169-});
170-171-const dynamic = resolveDiscoveredClawRouterModel({
172-baseUrl: provider.baseUrl,
173-modelId: "google/gemini-default",
174-});
175-expect(dynamic).toMatchObject({
176-id: "google/gemini-default",
177-provider: "clawrouter",
178-api: "google-generative-ai",
179162});
163+expect(normalized?.params).toBeUndefined();
164+expect(JSON.stringify(provider.models)).not.toContain("clawrouter-test-key");
180165});
181166182167it("caches the auth-scoped catalog for the discovery TTL", async () => {
@@ -196,40 +181,40 @@ describe("clawrouter provider catalog", () => {
196181expect((headers as Headers).get("authorization")).toBe("Bearer clawrouter-test-key");
197182});
198183199-it("replaces stale discovery state when the active catalog changes", async () => {
200-const first = buildFetchGuard();
201-const provider = await buildClawRouterProviderConfig({
184+it("keeps credential-scoped route metadata isolated on each catalog result", async () => {
185+const firstCatalog = structuredClone(CATALOG);
186+firstCatalog.providers[1].models[0].upstream = "first-upstream";
187+const first = buildFetchGuard(firstCatalog);
188+const firstProvider = await buildClawRouterProviderConfig({
202189apiKey: "first-key",
203-baseUrl: "https://first.example",
190+baseUrl: "https://clawrouter.example",
204191fetchGuard: first.fetchGuard,
205192});
206-const anthropic = provider.models.find((model) => model.id === "anthropic/default");
193+const firstAnthropic = firstProvider.models.find((model) => model.id === "anthropic/default");
207194208-const second = buildFetchGuard();
209-await buildClawRouterProviderConfig({
195+const secondCatalog = structuredClone(CATALOG);
196+secondCatalog.providers[1].models[0].upstream = "second-upstream";
197+const second = buildFetchGuard(secondCatalog);
198+const secondProvider = await buildClawRouterProviderConfig({
210199apiKey: "second-key",
211-baseUrl: "https://second.example",
200+baseUrl: "https://clawrouter.example",
212201fetchGuard: second.fetchGuard,
213202});
203+const secondAnthropic = secondProvider.models.find((model) => model.id === "anthropic/default");
214204215205expect(
216-resolveDiscoveredClawRouterModel({
217-baseUrl: "https://first.example/v1",
218-modelId: "openai/gpt-5.5-mini",
219-}),
220-).toBeUndefined();
221-expect(
222-resolveDiscoveredClawRouterModel({
223-baseUrl: "https://second.example/v1",
224-modelId: "openai/gpt-5.5-mini",
225-}),
226-).toBeDefined();
206+normalizeClawRouterResolvedModel({
207+ ...firstAnthropic,
208+baseUrl: firstProvider.baseUrl,
209+provider: "clawrouter",
210+} as ProviderRuntimeModel)?.id,
211+).toBe("first-upstream");
227212expect(
228213normalizeClawRouterResolvedModel({
229- ...anthropic,
230-baseUrl: provider.baseUrl,
214+ ...secondAnthropic,
215+baseUrl: secondProvider.baseUrl,
231216provider: "clawrouter",
232-} as ProviderRuntimeModel),
233-).toBeUndefined();
217+} as ProviderRuntimeModel)?.id,
218+).toBe("second-upstream");
234219});
235220});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。