
























11import type { StreamFn } from "openclaw/plugin-sdk/agent-core";
22import { capturePluginRegistration } from "openclaw/plugin-sdk/plugin-test-runtime";
3-import { describe, expect, it } from "vitest";
3+import { clearLiveCatalogCacheForTests } from "openclaw/plugin-sdk/provider-catalog-live-runtime";
4+import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
5+6+const providerAuthRuntimeMocks = vi.hoisted(() => ({
7+resolveApiKeyForProvider: vi.fn(),
8+}));
9+10+vi.mock("openclaw/plugin-sdk/provider-auth-runtime", () => providerAuthRuntimeMocks);
11+412import plugin from "./index.js";
51314+const LIVE_CATALOG = {
15+providers: [
16+{
17+id: "openai",
18+displayName: "OpenAI",
19+openaiCompatible: true,
20+nativeBaseUrl: "/v1/native/openai",
21+routes: [
22+{
23+path: "/v1/responses",
24+methods: ["POST"],
25+requestFormat: "openai.responses",
26+},
27+],
28+models: [
29+{
30+id: "openai/gpt-5.5-mini",
31+upstream: "gpt-5.5-mini",
32+capabilities: ["llm.responses"],
33+},
34+],
35+},
36+],
37+};
38+639describe("clawrouter provider plugin", () => {
40+beforeEach(() => {
41+clearLiveCatalogCacheForTests();
42+providerAuthRuntimeMocks.resolveApiKeyForProvider.mockReset();
43+});
44+45+afterEach(() => {
46+vi.unstubAllGlobals();
47+});
48+749it("registers managed proxy-key auth and transport routing hooks", () => {
850const captured = capturePluginRegistration(plugin);
951const provider = captured.providers[0];
@@ -17,13 +59,15 @@ describe("clawrouter provider plugin", () => {
1759buildReplayPolicy: expect.any(Function),
1860normalizeResolvedModel: expect.any(Function),
1961sanitizeReplayHistory: expect.any(Function),
62+wrapSimpleCompletionStreamFn: expect.any(Function),
2063wrapStreamFn: expect.any(Function),
2164});
2265expect(provider?.auth[0]).toMatchObject({
2366id: "api-key",
2467label: "ClawRouter proxy key",
2568kind: "api_key",
2669});
70+expect(provider?.wrapSimpleCompletionStreamFn).toBe(provider?.wrapStreamFn);
2771});
28722973it("attaches the resolved proxy key only when dispatching a request", () => {
@@ -66,6 +110,53 @@ describe("clawrouter provider plugin", () => {
66110expect(calls[1]?.headers).toBeUndefined();
67111});
68112113+it("resolves managed secret refs before credential-scoped discovery", async () => {
114+providerAuthRuntimeMocks.resolveApiKeyForProvider.mockResolvedValue({
115+apiKey: "resolved-proxy-key",
116+mode: "api-key",
117+source: "models.json secretref",
118+});
119+const fetchMock = vi.fn(async () => Response.json(LIVE_CATALOG));
120+vi.stubGlobal("fetch", fetchMock as unknown as typeof fetch);
121+const provider = capturePluginRegistration(plugin).providers[0];
122+123+const result = await provider?.catalog?.run({
124+config: { models: {} },
125+agentDir: "/agent",
126+workspaceDir: "/workspace",
127+env: {},
128+resolveProviderAuth: () => ({
129+apiKey: "secretref-managed",
130+discoveryApiKey: undefined,
131+mode: "api_key",
132+source: "profile",
133+profileId: "clawrouter-profile",
134+}),
135+resolveProviderApiKey: () => ({
136+apiKey: "secretref-managed",
137+discoveryApiKey: undefined,
138+}),
139+});
140+141+if (!result || !("provider" in result)) {
142+throw new Error("expected ClawRouter catalog provider result");
143+}
144+expect(result.provider.apiKey).toBe("secretref-managed");
145+expect(result.provider.models.map((model) => model.id)).toEqual(["openai/gpt-5.5-mini"]);
146+expect(providerAuthRuntimeMocks.resolveApiKeyForProvider).toHaveBeenCalledWith({
147+provider: "clawrouter",
148+cfg: { models: {} },
149+agentDir: "/agent",
150+workspaceDir: "/workspace",
151+profileId: "clawrouter-profile",
152+lockedProfile: true,
153+});
154+const fetchCall = fetchMock.mock.calls[0] as unknown as [string, RequestInit] | undefined;
155+expect(new Headers(fetchCall?.[1]?.headers).get("Authorization")).toBe(
156+"Bearer resolved-proxy-key",
157+);
158+});
159+69160it("normalizes configured ClawRouter roots to the API base URL", () => {
70161const provider = capturePluginRegistration(plugin).providers[0];
71162const normalized = provider?.normalizeConfig?.({
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。