From 7dba076e069a07d55c7229fab8ae5362fceb21e6 Mon Sep 17 00:00:00 2001 From: kumarabhirup Date: Sat, 31 Jan 2026 22:32:09 -0800 Subject: [PATCH] agents: add tests for AI SDK provider and runner Add unit tests for provider adapter and runner functionality. Tests cover provider initialization, tool conversion, and basic run scenarios. --- src/agents/aisdk/provider.test.ts | 151 ++++++++++++++++++++++++++++++ src/agents/aisdk/run.test.ts | 46 +++++++++ 2 files changed, 197 insertions(+) create mode 100644 src/agents/aisdk/provider.test.ts create mode 100644 src/agents/aisdk/run.test.ts diff --git a/src/agents/aisdk/provider.test.ts b/src/agents/aisdk/provider.test.ts new file mode 100644 index 00000000000..86eff33a8dd --- /dev/null +++ b/src/agents/aisdk/provider.test.ts @@ -0,0 +1,151 @@ +import { describe, expect, it } from "vitest"; +import { + getDefaultConfig, + parseModelRef, + validateConfig, + listAvailableProviders, +} from "./provider.js"; + +describe("AI SDK Provider", () => { + describe("parseModelRef", () => { + it("parses valid model reference", () => { + const result = parseModelRef("anthropic/claude-sonnet-4"); + expect(result.providerId).toBe("anthropic"); + expect(result.modelId).toBe("claude-sonnet-4"); + }); + + it("parses model ref with multiple slashes", () => { + const result = parseModelRef("openai/gpt-4o/preview"); + expect(result.providerId).toBe("openai"); + expect(result.modelId).toBe("gpt-4o/preview"); + }); + + it("throws on invalid model reference without slash", () => { + expect(() => parseModelRef("claude-sonnet-4")).toThrow( + 'Invalid model reference "claude-sonnet-4"', + ); + }); + }); + + describe("getDefaultConfig", () => { + it("returns gateway mode when AI_GATEWAY_API_KEY is set", () => { + const original = process.env.AI_GATEWAY_API_KEY; + try { + process.env.AI_GATEWAY_API_KEY = "test-key"; + const config = getDefaultConfig(); + expect(config.mode).toBe("gateway"); + expect(config.gateway?.apiKey).toBe("test-key"); + } finally { + if (original === undefined) { + delete process.env.AI_GATEWAY_API_KEY; + } else { + process.env.AI_GATEWAY_API_KEY = original; + } + } + }); + + it("returns direct mode when no gateway key", () => { + const original = process.env.AI_GATEWAY_API_KEY; + try { + delete process.env.AI_GATEWAY_API_KEY; + const config = getDefaultConfig(); + expect(config.mode).toBe("direct"); + } finally { + if (original !== undefined) { + process.env.AI_GATEWAY_API_KEY = original; + } + } + }); + + it("includes default model reference", () => { + const config = getDefaultConfig(); + expect(config.defaultModel).toBeDefined(); + expect(config.defaultModel).toContain("/"); + }); + }); + + describe("validateConfig", () => { + it("returns null for valid gateway config", () => { + const result = validateConfig({ + mode: "gateway", + gateway: { apiKey: "test-key" }, + }); + expect(result).toBeNull(); + }); + + it("returns error for gateway mode without key", () => { + const original = process.env.AI_GATEWAY_API_KEY; + try { + delete process.env.AI_GATEWAY_API_KEY; + const result = validateConfig({ + mode: "gateway", + }); + expect(result).toContain("AI Gateway"); + } finally { + if (original !== undefined) { + process.env.AI_GATEWAY_API_KEY = original; + } + } + }); + + it("returns error for direct mode without any provider keys", () => { + // Clear all provider env vars temporarily + const saved: Record = {}; + const providerVars = [ + "ANTHROPIC_API_KEY", + "OPENAI_API_KEY", + "GOOGLE_GENERATIVE_AI_API_KEY", + "GOOGLE_API_KEY", + "GROQ_API_KEY", + "MISTRAL_API_KEY", + "XAI_API_KEY", + "OPENROUTER_API_KEY", + "AZURE_API_KEY", + ]; + for (const v of providerVars) { + saved[v] = process.env[v]; + delete process.env[v]; + } + try { + const result = validateConfig({ + mode: "direct", + providers: {}, + }); + expect(result).toContain("at least one provider"); + } finally { + for (const v of providerVars) { + if (saved[v] !== undefined) { + process.env[v] = saved[v]; + } + } + } + }); + }); + + describe("listAvailableProviders", () => { + it("returns all providers for gateway mode", () => { + const providers = listAvailableProviders({ mode: "gateway" }); + expect(providers).toContain("anthropic"); + expect(providers).toContain("openai"); + expect(providers).toContain("google"); + }); + + it("returns only providers with keys for direct mode", () => { + const original = process.env.ANTHROPIC_API_KEY; + try { + process.env.ANTHROPIC_API_KEY = "test-key"; + const providers = listAvailableProviders({ + mode: "direct", + providers: {}, + }); + expect(providers).toContain("anthropic"); + } finally { + if (original === undefined) { + delete process.env.ANTHROPIC_API_KEY; + } else { + process.env.ANTHROPIC_API_KEY = original; + } + } + }); + }); +}); diff --git a/src/agents/aisdk/run.test.ts b/src/agents/aisdk/run.test.ts new file mode 100644 index 00000000000..100c78b3ecf --- /dev/null +++ b/src/agents/aisdk/run.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, it } from "vitest"; +import { mapThinkLevelToAnthropicOptions } from "./run.js"; + +describe("AI SDK Run", () => { + describe("mapThinkLevelToAnthropicOptions", () => { + it("returns empty object for non-anthropic provider", () => { + expect(mapThinkLevelToAnthropicOptions("high", "openai")).toEqual({}); + expect(mapThinkLevelToAnthropicOptions("high", "google")).toEqual({}); + }); + + it("returns empty object for off thinking level", () => { + expect(mapThinkLevelToAnthropicOptions("off", "anthropic")).toEqual({}); + expect(mapThinkLevelToAnthropicOptions(undefined, "anthropic")).toEqual({}); + }); + + it("maps minimal to 2000 budget tokens", () => { + const result = mapThinkLevelToAnthropicOptions("minimal", "anthropic"); + expect(result.thinking).toEqual({ type: "enabled", budgetTokens: 2000 }); + expect(result.effort).toBe("low"); + }); + + it("maps low to 4000 budget tokens", () => { + const result = mapThinkLevelToAnthropicOptions("low", "anthropic"); + expect(result.thinking).toEqual({ type: "enabled", budgetTokens: 4000 }); + expect(result.effort).toBe("low"); + }); + + it("maps medium to 8000 budget tokens", () => { + const result = mapThinkLevelToAnthropicOptions("medium", "anthropic"); + expect(result.thinking).toEqual({ type: "enabled", budgetTokens: 8000 }); + expect(result.effort).toBe("medium"); + }); + + it("maps high to 16000 budget tokens with high effort", () => { + const result = mapThinkLevelToAnthropicOptions("high", "anthropic"); + expect(result.thinking).toEqual({ type: "enabled", budgetTokens: 16000 }); + expect(result.effort).toBe("high"); + }); + + it("maps xhigh to 32000 budget tokens with high effort", () => { + const result = mapThinkLevelToAnthropicOptions("xhigh", "anthropic"); + expect(result.thinking).toEqual({ type: "enabled", budgetTokens: 32000 }); + expect(result.effort).toBe("high"); + }); + }); +});