Skip to content

Commit a32140c

Browse files
authored
Merge branch 'main' into vincentkoc-code/deps-fast-xml-parser-markdown-it
2 parents 8ec155c + a8f7c27 commit a32140c

File tree

7 files changed

+592
-403
lines changed

7 files changed

+592
-403
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
2+
import { afterEach, describe, expect, it, vi } from "vitest";
3+
import {
4+
DEFAULT_TAVILY_BASE_URL,
5+
DEFAULT_TAVILY_EXTRACT_TIMEOUT_SECONDS,
6+
DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS,
7+
resolveTavilyApiKey,
8+
resolveTavilyBaseUrl,
9+
resolveTavilyExtractTimeoutSeconds,
10+
resolveTavilySearchConfig,
11+
resolveTavilySearchTimeoutSeconds,
12+
} from "./config.js";
13+
14+
describe("tavily config helpers", () => {
15+
afterEach(() => {
16+
vi.unstubAllEnvs();
17+
});
18+
19+
it("reads plugin web search config and prefers it over env defaults", () => {
20+
vi.stubEnv("TAVILY_API_KEY", "env-key");
21+
vi.stubEnv("TAVILY_BASE_URL", "https://env.tavily.test");
22+
23+
const cfg = {
24+
plugins: {
25+
entries: {
26+
tavily: {
27+
config: {
28+
webSearch: {
29+
apiKey: "plugin-key",
30+
baseUrl: "https://plugin.tavily.test",
31+
},
32+
},
33+
},
34+
},
35+
},
36+
} as OpenClawConfig;
37+
38+
expect(resolveTavilySearchConfig(cfg)).toEqual({
39+
apiKey: "plugin-key",
40+
baseUrl: "https://plugin.tavily.test",
41+
});
42+
expect(resolveTavilyApiKey(cfg)).toBe("plugin-key");
43+
expect(resolveTavilyBaseUrl(cfg)).toBe("https://plugin.tavily.test");
44+
});
45+
46+
it("falls back to environment values and defaults", () => {
47+
vi.stubEnv("TAVILY_API_KEY", "env-key");
48+
vi.stubEnv("TAVILY_BASE_URL", "https://env.tavily.test");
49+
50+
expect(resolveTavilyApiKey()).toBe("env-key");
51+
expect(resolveTavilyBaseUrl()).toBe("https://env.tavily.test");
52+
expect(resolveTavilyBaseUrl({} as OpenClawConfig)).not.toBe(DEFAULT_TAVILY_BASE_URL);
53+
expect(resolveTavilySearchTimeoutSeconds()).toBe(DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS);
54+
expect(resolveTavilyExtractTimeoutSeconds()).toBe(DEFAULT_TAVILY_EXTRACT_TIMEOUT_SECONDS);
55+
});
56+
57+
it("accepts positive numeric timeout overrides and floors them", () => {
58+
expect(resolveTavilySearchTimeoutSeconds(19.9)).toBe(19);
59+
expect(resolveTavilyExtractTimeoutSeconds(42.7)).toBe(42);
60+
expect(resolveTavilySearchTimeoutSeconds(0)).toBe(DEFAULT_TAVILY_SEARCH_TIMEOUT_SECONDS);
61+
expect(resolveTavilyExtractTimeoutSeconds(Number.NaN)).toBe(
62+
DEFAULT_TAVILY_EXTRACT_TIMEOUT_SECONDS,
63+
);
64+
});
65+
});
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { describe, expect, it } from "vitest";
2+
import { __testing } from "./tavily-client.js";
3+
4+
describe("tavily client helpers", () => {
5+
it("appends endpoints to reverse-proxy base urls", () => {
6+
expect(__testing.resolveEndpoint("https://proxy.example/api/tavily", "/search")).toBe(
7+
"https://proxy.example/api/tavily/search",
8+
);
9+
expect(__testing.resolveEndpoint("https://proxy.example/api/tavily/", "/extract")).toBe(
10+
"https://proxy.example/api/tavily/extract",
11+
);
12+
});
13+
14+
it("falls back to the default host for invalid base urls", () => {
15+
expect(__testing.resolveEndpoint("not a url", "/search")).toBe(
16+
"https://api.tavily.com/search",
17+
);
18+
expect(__testing.resolveEndpoint("", "/extract")).toBe("https://api.tavily.com/extract");
19+
});
20+
});
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
const runTavilySearch = vi.fn(async (params: Record<string, unknown>) => params);
4+
5+
vi.mock("./tavily-client.js", () => ({
6+
runTavilySearch,
7+
}));
8+
9+
describe("tavily web search provider", () => {
10+
it("exposes the expected metadata and selection wiring", async () => {
11+
const { createTavilyWebSearchProvider } = await import("./tavily-search-provider.js");
12+
13+
const provider = createTavilyWebSearchProvider();
14+
const applied = provider.applySelectionConfig({});
15+
16+
expect(provider.id).toBe("tavily");
17+
expect(provider.credentialPath).toBe("plugins.entries.tavily.config.webSearch.apiKey");
18+
expect(applied.plugins?.entries?.tavily?.enabled).toBe(true);
19+
});
20+
21+
it("maps generic tool arguments into Tavily search params", async () => {
22+
const { createTavilyWebSearchProvider } = await import("./tavily-search-provider.js");
23+
const provider = createTavilyWebSearchProvider();
24+
const tool = provider.createTool({
25+
config: { test: true },
26+
} as never);
27+
28+
const result = await tool.execute({
29+
query: "weather sf",
30+
count: 7,
31+
});
32+
33+
expect(runTavilySearch).toHaveBeenCalledWith({
34+
cfg: { test: true },
35+
query: "weather sf",
36+
maxResults: 7,
37+
});
38+
expect(result).toEqual({
39+
cfg: { test: true },
40+
query: "weather sf",
41+
maxResults: 7,
42+
});
43+
});
44+
});
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import { describe, expect, it, vi } from "vitest";
2+
3+
const runTavilySearch = vi.fn(async (params: Record<string, unknown>) => ({
4+
ok: true,
5+
params,
6+
}));
7+
8+
vi.mock("./tavily-client.js", () => ({
9+
runTavilySearch,
10+
}));
11+
12+
describe("tavily search tool", () => {
13+
it("normalizes optional parameters before invoking Tavily", async () => {
14+
const { createTavilySearchTool } = await import("./tavily-search-tool.js");
15+
const tool = createTavilySearchTool({
16+
config: { env: "test" },
17+
} as never);
18+
19+
const result = await tool.execute("call-1", {
20+
query: "best docs",
21+
search_depth: "advanced",
22+
topic: "news",
23+
max_results: 5,
24+
include_answer: true,
25+
time_range: "week",
26+
include_domains: ["docs.openclaw.ai", "", "openclaw.ai"],
27+
exclude_domains: ["bad.example", ""],
28+
});
29+
30+
expect(runTavilySearch).toHaveBeenCalledWith({
31+
cfg: { env: "test" },
32+
query: "best docs",
33+
searchDepth: "advanced",
34+
topic: "news",
35+
maxResults: 5,
36+
includeAnswer: true,
37+
timeRange: "week",
38+
includeDomains: ["docs.openclaw.ai", "openclaw.ai"],
39+
excludeDomains: ["bad.example"],
40+
});
41+
expect(result).toMatchObject({
42+
details: {
43+
ok: true,
44+
params: {
45+
cfg: { env: "test" },
46+
query: "best docs",
47+
searchDepth: "advanced",
48+
topic: "news",
49+
maxResults: 5,
50+
includeAnswer: true,
51+
timeRange: "week",
52+
includeDomains: ["docs.openclaw.ai", "openclaw.ai"],
53+
excludeDomains: ["bad.example"],
54+
},
55+
},
56+
});
57+
expect(result.content[0]).toMatchObject({
58+
type: "text",
59+
});
60+
});
61+
62+
it("requires a query and drops empty domain arrays", async () => {
63+
const { createTavilySearchTool } = await import("./tavily-search-tool.js");
64+
const tool = createTavilySearchTool({
65+
config: { env: "test" },
66+
} as never);
67+
68+
await expect(
69+
tool.execute("call-2", {
70+
query: "simple",
71+
include_domains: [""],
72+
exclude_domains: [],
73+
}),
74+
).resolves.toMatchObject({
75+
details: {
76+
ok: true,
77+
params: {
78+
cfg: { env: "test" },
79+
query: "simple",
80+
includeAnswer: false,
81+
},
82+
},
83+
});
84+
});
85+
});

0 commit comments

Comments
 (0)