























@@ -0,0 +1,165 @@
1+import { beforeEach, describe, expect, it, vi } from "vitest";
2+3+const mocks = vi.hoisted(() => ({
4+runWebSearch: vi.fn(),
5+resolveManifestContractOwnerPluginId: vi.fn(),
6+getActiveRuntimeWebToolsMetadata: vi.fn(),
7+getActiveSecretsRuntimeSnapshot: vi.fn(),
8+}));
9+10+vi.mock("../../web-search/runtime.js", () => ({
11+resolveWebSearchProviderId: vi.fn(() => "mock"),
12+runWebSearch: mocks.runWebSearch,
13+}));
14+15+vi.mock("../../plugins/plugin-registry.js", () => ({
16+resolveManifestContractOwnerPluginId: mocks.resolveManifestContractOwnerPluginId,
17+}));
18+19+vi.mock("../../secrets/runtime-web-tools-state.js", () => ({
20+getActiveRuntimeWebToolsMetadata: mocks.getActiveRuntimeWebToolsMetadata,
21+}));
22+23+vi.mock("../../secrets/runtime.js", () => ({
24+getActiveSecretsRuntimeSnapshot: mocks.getActiveSecretsRuntimeSnapshot,
25+}));
26+27+describe("web_search late-bound runtime fallback", () => {
28+beforeEach(() => {
29+mocks.runWebSearch.mockReset();
30+mocks.runWebSearch.mockResolvedValue({
31+provider: "brave",
32+result: { ok: true },
33+});
34+mocks.resolveManifestContractOwnerPluginId.mockReset();
35+mocks.resolveManifestContractOwnerPluginId.mockReturnValue(undefined);
36+mocks.getActiveRuntimeWebToolsMetadata.mockReset();
37+mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue(null);
38+mocks.getActiveSecretsRuntimeSnapshot.mockReset();
39+mocks.getActiveSecretsRuntimeSnapshot.mockReturnValue(null);
40+});
41+42+it("falls back to options.runtimeWebSearch when active runtime web tools metadata is absent", async () => {
43+const { createWebSearchTool } = await import("./web-search.js");
44+const tool = createWebSearchTool({
45+config: {},
46+lateBindRuntimeConfig: true,
47+runtimeWebSearch: {
48+selectedProvider: "brave",
49+providerConfigured: "brave",
50+providerSource: "plugin",
51+diagnostics: [],
52+},
53+});
54+55+await tool?.execute("call-search", { query: "openclaw" }, undefined);
56+57+expect(mocks.runWebSearch).toHaveBeenCalledWith(
58+expect.objectContaining({
59+runtimeWebSearch: expect.objectContaining({ selectedProvider: "brave" }),
60+}),
61+);
62+});
63+64+it("falls back to options.config when getActiveSecretsRuntimeSnapshot is null", async () => {
65+const { createWebSearchTool } = await import("./web-search.js");
66+const fallbackConfig = {
67+tools: { web: { search: { provider: "brave" } } },
68+};
69+const tool = createWebSearchTool({
70+config: fallbackConfig,
71+lateBindRuntimeConfig: true,
72+});
73+74+await tool?.execute("call-search", { query: "openclaw" }, undefined);
75+76+expect(mocks.runWebSearch).toHaveBeenCalledWith(
77+expect.objectContaining({
78+config: fallbackConfig,
79+}),
80+);
81+});
82+83+it("uses configured provider id from config when no runtime selection is present", async () => {
84+const { createWebSearchTool } = await import("./web-search.js");
85+const config = {
86+tools: { web: { search: { provider: "Brave" } } },
87+};
88+const tool = createWebSearchTool({
89+ config,
90+lateBindRuntimeConfig: true,
91+});
92+93+await tool?.execute("call-search", { query: "openclaw" }, undefined);
94+95+expect(mocks.resolveManifestContractOwnerPluginId).toHaveBeenCalledWith(
96+expect.objectContaining({ value: "brave" }),
97+);
98+expect(mocks.runWebSearch).toHaveBeenCalledWith(
99+expect.objectContaining({ preferRuntimeProviders: true }),
100+);
101+});
102+103+it("does not prefer runtime providers when no provider id is selected anywhere", async () => {
104+const { createWebSearchTool } = await import("./web-search.js");
105+const tool = createWebSearchTool({
106+config: {},
107+lateBindRuntimeConfig: true,
108+});
109+110+await tool?.execute("call-search", { query: "openclaw" }, undefined);
111+112+expect(mocks.resolveManifestContractOwnerPluginId).not.toHaveBeenCalled();
113+expect(mocks.runWebSearch).toHaveBeenCalledWith(
114+expect.objectContaining({ preferRuntimeProviders: false }),
115+);
116+});
117+118+it("does not prefer runtime providers when the configured provider is a bundled manifest owner", async () => {
119+mocks.resolveManifestContractOwnerPluginId.mockReturnValue("openclaw-bundled-brave");
120+const { createWebSearchTool } = await import("./web-search.js");
121+const config = {
122+tools: { web: { search: { provider: "brave" } } },
123+};
124+const tool = createWebSearchTool({
125+ config,
126+lateBindRuntimeConfig: true,
127+});
128+129+await tool?.execute("call-search", { query: "openclaw" }, undefined);
130+131+expect(mocks.runWebSearch).toHaveBeenCalledWith(
132+expect.objectContaining({ preferRuntimeProviders: false }),
133+);
134+});
135+136+it("prefers active runtime metadata over options.runtimeWebSearch when present", async () => {
137+mocks.getActiveRuntimeWebToolsMetadata.mockReturnValue({
138+search: {
139+selectedProvider: "perplexity",
140+providerConfigured: "perplexity",
141+providerSource: "plugin",
142+diagnostics: [],
143+},
144+});
145+const { createWebSearchTool } = await import("./web-search.js");
146+const tool = createWebSearchTool({
147+config: {},
148+lateBindRuntimeConfig: true,
149+runtimeWebSearch: {
150+selectedProvider: "brave",
151+providerConfigured: "brave",
152+providerSource: "plugin",
153+diagnostics: [],
154+},
155+});
156+157+await tool?.execute("call-search", { query: "openclaw" }, undefined);
158+159+expect(mocks.runWebSearch).toHaveBeenCalledWith(
160+expect.objectContaining({
161+runtimeWebSearch: expect.objectContaining({ selectedProvider: "perplexity" }),
162+}),
163+);
164+});
165+});
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。