| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- import { describe, expect, test, vi } from "vitest";
- // Mock dependencies that cause import issues
- vi.mock("@/lib/proxy-agent", () => ({
- createProxyAgentForProvider: vi.fn(),
- }));
- vi.mock("@/repository/key", () => ({
- validateApiKeyAndGetUser: vi.fn(),
- }));
- vi.mock("@/app/v1/_lib/proxy/provider-selector", () => ({
- ProxyProviderResolver: {
- selectProviderByType: vi.fn(),
- },
- }));
- import {
- formatAnthropicResponse,
- formatGeminiResponse,
- formatOpenAIResponse,
- getProviderTypesForFormat,
- inferOwner,
- type FetchedModel,
- } from "@/app/v1/_lib/models/available-models";
- import type {
- AnthropicModelsResponse,
- GeminiModelsResponse,
- OpenAIModelsResponse,
- } from "@/types/models";
- describe("inferOwner - 根据模型 ID 推断所有者", () => {
- describe("Anthropic 模型", () => {
- test("claude-* 模型应返回 anthropic", () => {
- expect(inferOwner("claude-3-opus-20240229")).toBe("anthropic");
- expect(inferOwner("claude-3-sonnet-20240229")).toBe("anthropic");
- expect(inferOwner("claude-3-haiku-20240307")).toBe("anthropic");
- expect(inferOwner("claude-2.1")).toBe("anthropic");
- expect(inferOwner("claude-instant-1.2")).toBe("anthropic");
- });
- });
- describe("OpenAI 模型", () => {
- test("gpt-* 模型应返回 openai", () => {
- expect(inferOwner("gpt-4")).toBe("openai");
- expect(inferOwner("gpt-4-turbo")).toBe("openai");
- expect(inferOwner("gpt-3.5-turbo")).toBe("openai");
- expect(inferOwner("gpt-4o")).toBe("openai");
- });
- test("o1* 模型应返回 openai", () => {
- expect(inferOwner("o1-preview")).toBe("openai");
- expect(inferOwner("o1-mini")).toBe("openai");
- expect(inferOwner("o1")).toBe("openai");
- });
- test("o3* 模型应返回 openai", () => {
- expect(inferOwner("o3-mini")).toBe("openai");
- expect(inferOwner("o3")).toBe("openai");
- });
- });
- describe("Google 模型", () => {
- test("gemini-* 模型应返回 google", () => {
- expect(inferOwner("gemini-pro")).toBe("google");
- expect(inferOwner("gemini-1.5-pro")).toBe("google");
- expect(inferOwner("gemini-1.5-flash")).toBe("google");
- expect(inferOwner("gemini-2.0-flash-exp")).toBe("google");
- });
- });
- describe("DeepSeek 模型", () => {
- test("deepseek* 模型应返回 deepseek", () => {
- expect(inferOwner("deepseek-chat")).toBe("deepseek");
- expect(inferOwner("deepseek-coder")).toBe("deepseek");
- expect(inferOwner("deepseek-v3")).toBe("deepseek");
- });
- });
- describe("Alibaba 模型", () => {
- test("qwen* 模型应返回 alibaba", () => {
- expect(inferOwner("qwen-turbo")).toBe("alibaba");
- expect(inferOwner("qwen-plus")).toBe("alibaba");
- expect(inferOwner("qwen-max")).toBe("alibaba");
- });
- });
- describe("未知模型", () => {
- test("无法识别的模型应返回 unknown", () => {
- expect(inferOwner("llama-2-70b")).toBe("unknown");
- expect(inferOwner("mistral-7b")).toBe("unknown");
- expect(inferOwner("custom-model")).toBe("unknown");
- });
- });
- });
- describe("getProviderTypesForFormat - 客户端格式到 Provider 类型映射", () => {
- test("claude 格式应返回 claude 和 claude-auth 类型", () => {
- expect(getProviderTypesForFormat("claude")).toEqual(["claude", "claude-auth"]);
- });
- test("openai 格式应返回 codex 和 openai-compatible 类型", () => {
- expect(getProviderTypesForFormat("openai")).toEqual(["codex", "openai-compatible"]);
- });
- test("gemini 格式应返回 gemini 和 gemini-cli 类型", () => {
- expect(getProviderTypesForFormat("gemini")).toEqual(["gemini", "gemini-cli"]);
- });
- test("gemini-cli 格式应返回 gemini 和 gemini-cli 类型", () => {
- expect(getProviderTypesForFormat("gemini-cli")).toEqual(["gemini", "gemini-cli"]);
- });
- test("response 格式应仅返回 codex 类型", () => {
- expect(getProviderTypesForFormat("response")).toEqual(["codex"]);
- });
- });
- describe("formatOpenAIResponse - OpenAI 格式响应", () => {
- test("空模型列表应返回空 data 数组", () => {
- const result: OpenAIModelsResponse = formatOpenAIResponse([]);
- expect(result.object).toBe("list");
- expect(result.data).toEqual([]);
- });
- test("应正确格式化模型列表", () => {
- const models: FetchedModel[] = [
- { id: "gpt-4" },
- { id: "claude-3-opus-20240229" },
- { id: "gemini-pro" },
- ];
- const result: OpenAIModelsResponse = formatOpenAIResponse(models);
- expect(result.object).toBe("list");
- expect(result.data).toHaveLength(3);
- expect(result.data[0].id).toBe("gpt-4");
- expect(result.data[0].object).toBe("model");
- expect(result.data[0].owned_by).toBe("openai");
- expect(typeof result.data[0].created).toBe("number");
- expect(result.data[1].id).toBe("claude-3-opus-20240229");
- expect(result.data[1].owned_by).toBe("anthropic");
- expect(result.data[2].id).toBe("gemini-pro");
- expect(result.data[2].owned_by).toBe("google");
- });
- test("created 时间戳应为当前时间(秒)", () => {
- const before = Math.floor(Date.now() / 1000);
- const result: OpenAIModelsResponse = formatOpenAIResponse([{ id: "test" }]);
- const after = Math.floor(Date.now() / 1000);
- expect(result.data[0].created).toBeGreaterThanOrEqual(before);
- expect(result.data[0].created).toBeLessThanOrEqual(after);
- });
- });
- describe("formatAnthropicResponse - Anthropic 格式响应", () => {
- test("空模型列表应返回空 data 数组", () => {
- const result: AnthropicModelsResponse = formatAnthropicResponse([]);
- expect(result.data).toEqual([]);
- expect(result.has_more).toBe(false);
- });
- test("应正确格式化模型列表", () => {
- const models: FetchedModel[] = [
- {
- id: "claude-3-opus-20240229",
- displayName: "Claude 3 Opus",
- createdAt: "2024-02-29T00:00:00Z",
- },
- { id: "claude-3-sonnet-20240229" },
- ];
- const result: AnthropicModelsResponse = formatAnthropicResponse(models);
- expect(result.has_more).toBe(false);
- expect(result.data).toHaveLength(2);
- expect(result.data[0].id).toBe("claude-3-opus-20240229");
- expect(result.data[0].type).toBe("model");
- expect(result.data[0].display_name).toBe("Claude 3 Opus");
- expect(result.data[0].created_at).toBe("2024-02-29T00:00:00Z");
- expect(result.data[1].id).toBe("claude-3-sonnet-20240229");
- expect(result.data[1].display_name).toBe("claude-3-sonnet-20240229");
- expect(result.data[1].created_at).toBeDefined();
- });
- test("缺少 displayName 时应使用 id 作为 display_name", () => {
- const result: AnthropicModelsResponse = formatAnthropicResponse([{ id: "test-model" }]);
- expect(result.data[0].display_name).toBe("test-model");
- });
- });
- describe("formatGeminiResponse - Gemini 格式响应", () => {
- test("空模型列表应返回空 models 数组", () => {
- const result: GeminiModelsResponse = formatGeminiResponse([]);
- expect(result.models).toEqual([]);
- });
- test("应正确格式化模型列表", () => {
- const models: FetchedModel[] = [
- { id: "gemini-pro", displayName: "Gemini Pro" },
- { id: "gemini-1.5-flash" },
- ];
- const result: GeminiModelsResponse = formatGeminiResponse(models);
- expect(result.models).toHaveLength(2);
- expect(result.models[0].name).toBe("models/gemini-pro");
- expect(result.models[0].displayName).toBe("Gemini Pro");
- expect(result.models[0].supportedGenerationMethods).toEqual(["generateContent"]);
- expect(result.models[1].name).toBe("models/gemini-1.5-flash");
- expect(result.models[1].displayName).toBe("gemini-1.5-flash");
- });
- test("模型名称应添加 models/ 前缀", () => {
- const result: GeminiModelsResponse = formatGeminiResponse([{ id: "test-model" }]);
- expect(result.models[0].name).toBe("models/test-model");
- });
- test("缺少 displayName 时应使用 id", () => {
- const result: GeminiModelsResponse = formatGeminiResponse([{ id: "test-model" }]);
- expect(result.models[0].displayName).toBe("test-model");
- });
- });
|