test: add unit tests for Anthropic native web search provider (#49949)
- 15 tests for config resolution, tool version, and server tool building - 3 tests for stream wrapper (injection, dedup, non-anthropic passthrough) - All 18 tests green
This commit is contained in:
parent
a19cb27f9e
commit
706dce9c00
@ -0,0 +1,53 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { createAnthropicNativeSearchStreamWrapper } from "./anthropic-native-search-wrapper.js";
|
||||
|
||||
describe("createAnthropicNativeSearchStreamWrapper", () => {
|
||||
it("injects web_search server tool into anthropic-messages payloads", () => {
|
||||
const capturedPayloads: any[] = [];
|
||||
const fakeStream = vi.fn((_model: any, _context: any, options: any) => {
|
||||
// Simulate calling onPayload
|
||||
if (options?.onPayload) {
|
||||
const payload = { tools: [{ type: "function", name: "other_tool" }] };
|
||||
options.onPayload(payload);
|
||||
capturedPayloads.push(payload);
|
||||
}
|
||||
return { type: "stream", events: [] } as any;
|
||||
});
|
||||
|
||||
const wrapped = createAnthropicNativeSearchStreamWrapper(fakeStream as any);
|
||||
wrapped({ api: "anthropic-messages" } as any, {} as any, {} as any);
|
||||
|
||||
expect(fakeStream).toHaveBeenCalledOnce();
|
||||
expect(capturedPayloads[0].tools).toHaveLength(2);
|
||||
expect(capturedPayloads[0].tools[1].type).toMatch(/^web_search_/);
|
||||
expect(capturedPayloads[0].tools[1].name).toBe("web_search");
|
||||
});
|
||||
|
||||
it("does not inject if web_search server tool already present", () => {
|
||||
const capturedPayloads: any[] = [];
|
||||
const fakeStream = vi.fn((_model: any, _context: any, options: any) => {
|
||||
if (options?.onPayload) {
|
||||
const payload = { tools: [{ type: "web_search_20260209", name: "web_search" }] };
|
||||
options.onPayload(payload);
|
||||
capturedPayloads.push(payload);
|
||||
}
|
||||
return { type: "stream", events: [] } as any;
|
||||
});
|
||||
|
||||
const wrapped = createAnthropicNativeSearchStreamWrapper(fakeStream as any);
|
||||
wrapped({ api: "anthropic-messages" } as any, {} as any, {} as any);
|
||||
|
||||
expect(capturedPayloads[0].tools).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("passes through non-anthropic API calls unchanged", () => {
|
||||
const fakeStream = vi.fn(() => ({ type: "stream" }) as any);
|
||||
const wrapped = createAnthropicNativeSearchStreamWrapper(fakeStream as any);
|
||||
|
||||
wrapped({ api: "openai-chat" } as any, {} as any, { onPayload: vi.fn() } as any);
|
||||
|
||||
// onPayload should be the original, not our wrapper
|
||||
const passedOptions = fakeStream.mock.calls[0][2];
|
||||
expect(passedOptions.onPayload).toBeDefined();
|
||||
});
|
||||
});
|
||||
122
extensions/anthropic/src/anthropic-web-search.test.ts
Normal file
122
extensions/anthropic/src/anthropic-web-search.test.ts
Normal file
@ -0,0 +1,122 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { __testing, buildAnthropicWebSearchServerTool } from "./anthropic-web-search-provider.js";
|
||||
|
||||
const { resolveAnthropicWebSearchConfig, resolveToolVersion, DEFAULT_TOOL_VERSION } = __testing;
|
||||
|
||||
describe("resolveAnthropicWebSearchConfig", () => {
|
||||
it("returns empty object when no config", () => {
|
||||
expect(resolveAnthropicWebSearchConfig(undefined)).toEqual({});
|
||||
expect(resolveAnthropicWebSearchConfig({})).toEqual({});
|
||||
});
|
||||
|
||||
it("returns empty object for non-object anthropic value", () => {
|
||||
expect(resolveAnthropicWebSearchConfig({ anthropic: "invalid" } as any)).toEqual({});
|
||||
expect(resolveAnthropicWebSearchConfig({ anthropic: [] } as any)).toEqual({});
|
||||
});
|
||||
|
||||
it("passes through anthropic config object", () => {
|
||||
const config = { anthropic: { maxUses: 5, allowedDomains: ["example.com"] } };
|
||||
expect(resolveAnthropicWebSearchConfig(config as any)).toEqual({
|
||||
maxUses: 5,
|
||||
allowedDomains: ["example.com"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveToolVersion", () => {
|
||||
it("defaults to latest version", () => {
|
||||
expect(resolveToolVersion({})).toBe(DEFAULT_TOOL_VERSION);
|
||||
});
|
||||
|
||||
it("accepts valid versions", () => {
|
||||
expect(resolveToolVersion({ toolVersion: "web_search_20250305" })).toBe("web_search_20250305");
|
||||
expect(resolveToolVersion({ toolVersion: "web_search_20260209" })).toBe("web_search_20260209");
|
||||
});
|
||||
|
||||
it("falls back to default for invalid versions", () => {
|
||||
expect(resolveToolVersion({ toolVersion: "invalid" })).toBe(DEFAULT_TOOL_VERSION);
|
||||
expect(resolveToolVersion({ toolVersion: " " })).toBe(DEFAULT_TOOL_VERSION);
|
||||
});
|
||||
});
|
||||
|
||||
describe("buildAnthropicWebSearchServerTool", () => {
|
||||
it("builds minimal tool with defaults", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool();
|
||||
expect(tool).toEqual({
|
||||
type: DEFAULT_TOOL_VERSION,
|
||||
name: "web_search",
|
||||
});
|
||||
});
|
||||
|
||||
it("includes allowed_domains when configured", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: { allowedDomains: ["example.com", "docs.dev"] },
|
||||
} as any);
|
||||
expect(tool.allowed_domains).toEqual(["example.com", "docs.dev"]);
|
||||
});
|
||||
|
||||
it("includes blocked_domains when configured", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: { blockedDomains: ["spam.com"] },
|
||||
} as any);
|
||||
expect(tool.blocked_domains).toEqual(["spam.com"]);
|
||||
});
|
||||
|
||||
it("includes max_uses when positive", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: { maxUses: 3 },
|
||||
} as any);
|
||||
expect(tool.max_uses).toBe(3);
|
||||
});
|
||||
|
||||
it("excludes max_uses when zero or negative", () => {
|
||||
expect(
|
||||
buildAnthropicWebSearchServerTool({ anthropic: { maxUses: 0 } } as any).max_uses,
|
||||
).toBeUndefined();
|
||||
expect(
|
||||
buildAnthropicWebSearchServerTool({ anthropic: { maxUses: -1 } } as any).max_uses,
|
||||
).toBeUndefined();
|
||||
});
|
||||
|
||||
it("includes user_location when configured", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: {
|
||||
userLocation: {
|
||||
type: "approximate",
|
||||
city: "Denver",
|
||||
region: "Colorado",
|
||||
country: "US",
|
||||
timezone: "America/Denver",
|
||||
},
|
||||
},
|
||||
} as any);
|
||||
expect(tool.user_location).toEqual({
|
||||
type: "approximate",
|
||||
city: "Denver",
|
||||
region: "Colorado",
|
||||
country: "US",
|
||||
timezone: "America/Denver",
|
||||
});
|
||||
});
|
||||
|
||||
it("omits user_location when empty", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: { userLocation: {} },
|
||||
} as any);
|
||||
expect(tool.user_location).toBeUndefined();
|
||||
});
|
||||
|
||||
it("respects custom tool version", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: { toolVersion: "web_search_20250305" },
|
||||
} as any);
|
||||
expect(tool.type).toBe("web_search_20250305");
|
||||
});
|
||||
|
||||
it("omits empty allowed_domains array", () => {
|
||||
const tool = buildAnthropicWebSearchServerTool({
|
||||
anthropic: { allowedDomains: [] },
|
||||
} as any);
|
||||
expect(tool.allowed_domains).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Loading…
x
Reference in New Issue
Block a user