


























@@ -1,5 +1,6 @@
11import type { OpenClawConfig } from "openclaw/plugin-sdk/config-types";
22import type { OpenClawPluginApi } from "openclaw/plugin-sdk/plugin-runtime";
3+import { createTestPluginApi } from "openclaw/plugin-sdk/plugin-test-api";
34import { beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
45import {
56DEFAULT_TAVILY_BASE_URL,
@@ -33,13 +34,15 @@ describe("tavily tools", () => {
3334let createTavilySearchTool: typeof import("./tavily-search-tool.js").createTavilySearchTool;
3435let createTavilyExtractTool: typeof import("./tavily-extract-tool.js").createTavilyExtractTool;
3536let tavilyClientTesting: typeof import("./tavily-client.js").__testing;
37+let tavilyPlugin: typeof import("../index.js").default;
36383739beforeAll(async () => {
3840({ createTavilyWebSearchProvider } = await import("./tavily-search-provider.js"));
3941({ createTavilySearchTool } = await import("./tavily-search-tool.js"));
4042({ createTavilyExtractTool } = await import("./tavily-extract-tool.js"));
4143({ __testing: tavilyClientTesting } =
4244await vi.importActual<typeof import("./tavily-client.js")>("./tavily-client.js"));
45+({ default: tavilyPlugin } = await import("../index.js"));
4346});
44474548beforeEach(() => {
@@ -140,6 +143,85 @@ describe("tavily tools", () => {
140143});
141144});
142145146+it("late-binds dedicated tools to the resolved runtime config snapshot", async () => {
147+const rawConfig = {
148+plugins: {
149+entries: {
150+tavily: {
151+config: {
152+webSearch: {
153+apiKey: { source: "exec", provider: "default", id: "printf resolved-key" },
154+},
155+},
156+},
157+},
158+},
159+} as OpenClawConfig;
160+const runtimeConfig = {
161+plugins: {
162+entries: {
163+tavily: {
164+config: {
165+webSearch: {
166+apiKey: "resolved-key",
167+},
168+},
169+},
170+},
171+},
172+} as OpenClawConfig;
173+const registeredTools: Array<Parameters<OpenClawPluginApi["registerTool"]>[0]> = [];
174+const registeredOptions: Array<Parameters<OpenClawPluginApi["registerTool"]>[1]> = [];
175+const api = createTestPluginApi({
176+config: rawConfig,
177+registerTool(tool, opts) {
178+registeredTools.push(tool);
179+registeredOptions.push(opts);
180+},
181+});
182+183+tavilyPlugin.register(api);
184+const searchFactory = registeredTools.find(
185+(tool, index) =>
186+registeredOptions[index]?.name === "tavily_search" && typeof tool === "function",
187+);
188+const extractFactory = registeredTools.find(
189+(tool, index) =>
190+registeredOptions[index]?.name === "tavily_extract" && typeof tool === "function",
191+);
192+if (typeof searchFactory !== "function" || typeof extractFactory !== "function") {
193+throw new Error("Expected Tavily tools to register as runtime-context factories");
194+}
195+196+const searchTool = searchFactory({
197+config: rawConfig,
198+ runtimeConfig,
199+});
200+const extractTool = extractFactory({
201+config: rawConfig,
202+getRuntimeConfig: () => runtimeConfig,
203+});
204+if (Array.isArray(searchTool) || !searchTool || Array.isArray(extractTool) || !extractTool) {
205+throw new Error("Expected single Tavily tool definitions");
206+}
207+208+await searchTool.execute("search-call", { query: "openclaw" });
209+await extractTool.execute("extract-call", { urls: ["https://example.com"] });
210+211+expect(runTavilySearch).toHaveBeenCalledWith(
212+expect.objectContaining({
213+cfg: runtimeConfig,
214+query: "openclaw",
215+}),
216+);
217+expect(runTavilyExtract).toHaveBeenCalledWith(
218+expect.objectContaining({
219+cfg: runtimeConfig,
220+urls: ["https://example.com"],
221+}),
222+);
223+});
224+143225it("drops empty domain arrays and forwards query-scoped chunking", async () => {
144226runTavilySearch.mockImplementationOnce(async (params: Record<string, unknown>) => ({
145227ok: true,
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。