bca19b7741
新增测试文件: - agent/executor-extended.test.ts, presets/ - context/manager-extended.test.ts - core/agent.test.ts, providers.test.ts - lsp/cli.test.ts, client-extended.test.ts, index.test.ts - permission/file-prompt.test.ts, prompt.test.ts - skills/builtin/ - tools/filesystem/write_file-extended.test.ts - tools/git/git_commit-extended.test.ts - tools/load_description.test.ts - tools/todo/todo-manager.test.ts - tools/tool-search.test.ts - types/ - utils/config-extended.test.ts, diff-extended.test.ts 修改现有测试: - agent/manager.test.ts - tools/skill/skill.test.ts - utils/config.test.ts, diff.test.ts, image.test.ts
265 lines
7.1 KiB
TypeScript
265 lines
7.1 KiB
TypeScript
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||
import { getModelFactory, providers } from '../../../src/core/providers.js';
|
||
|
||
// Mock AI SDK providers
|
||
vi.mock('@ai-sdk/anthropic', () => ({
|
||
createAnthropic: vi.fn(() => {
|
||
const modelFn = (model: string) => ({ modelId: `anthropic:${model}` });
|
||
return modelFn;
|
||
}),
|
||
}));
|
||
|
||
vi.mock('@ai-sdk/deepseek', () => ({
|
||
createDeepSeek: vi.fn(() => {
|
||
const modelFn = (model: string) => ({ modelId: `deepseek:${model}` });
|
||
return modelFn;
|
||
}),
|
||
}));
|
||
|
||
vi.mock('@ai-sdk/openai', () => ({
|
||
createOpenAI: vi.fn(() => {
|
||
const modelFn = (model: string) => ({ modelId: `openai:${model}` });
|
||
return modelFn;
|
||
}),
|
||
}));
|
||
|
||
vi.mock('qwen-ai-provider-v5', () => ({
|
||
createQwen: vi.fn(() => {
|
||
const modelFn = (model: string) => ({ modelId: `qwen:${model}` });
|
||
return modelFn;
|
||
}),
|
||
}));
|
||
|
||
import { createAnthropic } from '@ai-sdk/anthropic';
|
||
import { createDeepSeek } from '@ai-sdk/deepseek';
|
||
import { createOpenAI } from '@ai-sdk/openai';
|
||
import { createQwen } from 'qwen-ai-provider-v5';
|
||
|
||
describe('providers', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
describe('providers 注册表', () => {
|
||
it('包含所有支持的 provider 类型', () => {
|
||
expect(providers).toHaveProperty('anthropic');
|
||
expect(providers).toHaveProperty('deepseek');
|
||
expect(providers).toHaveProperty('openai');
|
||
});
|
||
|
||
it('每个 provider 是一个工厂函数', () => {
|
||
expect(typeof providers.anthropic).toBe('function');
|
||
expect(typeof providers.deepseek).toBe('function');
|
||
expect(typeof providers.openai).toBe('function');
|
||
});
|
||
});
|
||
|
||
describe('Anthropic provider', () => {
|
||
it('创建 Anthropic 客户端', () => {
|
||
const factory = providers.anthropic({
|
||
apiKey: 'test-api-key',
|
||
});
|
||
|
||
expect(createAnthropic).toHaveBeenCalledWith({
|
||
apiKey: 'test-api-key',
|
||
baseURL: undefined,
|
||
});
|
||
expect(typeof factory).toBe('function');
|
||
});
|
||
|
||
it('支持自定义 baseUrl', () => {
|
||
providers.anthropic({
|
||
apiKey: 'test-api-key',
|
||
baseUrl: 'https://custom.anthropic.com',
|
||
});
|
||
|
||
expect(createAnthropic).toHaveBeenCalledWith({
|
||
apiKey: 'test-api-key',
|
||
baseURL: 'https://custom.anthropic.com',
|
||
});
|
||
});
|
||
|
||
it('返回的工厂函数可以创建模型', () => {
|
||
const factory = providers.anthropic({
|
||
apiKey: 'test-api-key',
|
||
});
|
||
|
||
const model = factory('claude-3-opus');
|
||
expect(model).toEqual({ modelId: 'anthropic:claude-3-opus' });
|
||
});
|
||
});
|
||
|
||
describe('DeepSeek provider', () => {
|
||
it('创建 DeepSeek 客户端', () => {
|
||
const factory = providers.deepseek({
|
||
apiKey: 'test-deepseek-key',
|
||
});
|
||
|
||
expect(createDeepSeek).toHaveBeenCalledWith({
|
||
apiKey: 'test-deepseek-key',
|
||
baseURL: undefined,
|
||
});
|
||
expect(typeof factory).toBe('function');
|
||
});
|
||
|
||
it('支持自定义 baseUrl', () => {
|
||
providers.deepseek({
|
||
apiKey: 'test-deepseek-key',
|
||
baseUrl: 'https://custom.deepseek.com',
|
||
});
|
||
|
||
expect(createDeepSeek).toHaveBeenCalledWith({
|
||
apiKey: 'test-deepseek-key',
|
||
baseURL: 'https://custom.deepseek.com',
|
||
});
|
||
});
|
||
|
||
it('返回的工厂函数可以创建模型', () => {
|
||
const factory = providers.deepseek({
|
||
apiKey: 'test-deepseek-key',
|
||
});
|
||
|
||
const model = factory('deepseek-chat');
|
||
expect(model).toEqual({ modelId: 'deepseek:deepseek-chat' });
|
||
});
|
||
});
|
||
|
||
describe('OpenAI provider', () => {
|
||
it('创建 OpenAI 客户端(标准 URL)', () => {
|
||
const factory = providers.openai({
|
||
apiKey: 'test-openai-key',
|
||
});
|
||
|
||
expect(createOpenAI).toHaveBeenCalledWith({
|
||
apiKey: 'test-openai-key',
|
||
baseURL: undefined,
|
||
});
|
||
expect(createQwen).not.toHaveBeenCalled();
|
||
expect(typeof factory).toBe('function');
|
||
});
|
||
|
||
it('支持自定义 baseUrl(非 DashScope)', () => {
|
||
providers.openai({
|
||
apiKey: 'test-openai-key',
|
||
baseUrl: 'https://custom.openai.com/v1',
|
||
});
|
||
|
||
expect(createOpenAI).toHaveBeenCalledWith({
|
||
apiKey: 'test-openai-key',
|
||
baseURL: 'https://custom.openai.com/v1',
|
||
});
|
||
expect(createQwen).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('返回的工厂函数可以创建模型', () => {
|
||
const factory = providers.openai({
|
||
apiKey: 'test-openai-key',
|
||
});
|
||
|
||
const model = factory('gpt-4');
|
||
expect(model).toEqual({ modelId: 'openai:gpt-4' });
|
||
});
|
||
});
|
||
|
||
describe('DashScope (Qwen) 检测', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
it('检测 dashscope URL 并使用 Qwen provider', () => {
|
||
providers.openai({
|
||
apiKey: 'test-qwen-key',
|
||
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||
});
|
||
|
||
expect(createQwen).toHaveBeenCalledWith({
|
||
apiKey: 'test-qwen-key',
|
||
baseURL: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
||
});
|
||
expect(createOpenAI).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('检测包含 dashscope 的任意 URL', () => {
|
||
providers.openai({
|
||
apiKey: 'test-qwen-key',
|
||
baseUrl: 'https://api.dashscope.example.com/v1',
|
||
});
|
||
|
||
expect(createQwen).toHaveBeenCalled();
|
||
expect(createOpenAI).not.toHaveBeenCalled();
|
||
});
|
||
|
||
it('Qwen 工厂函数可以创建模型', () => {
|
||
const factory = providers.openai({
|
||
apiKey: 'test-qwen-key',
|
||
baseUrl: 'https://dashscope.aliyuncs.com/v1',
|
||
});
|
||
|
||
const model = factory('qwen-turbo');
|
||
expect(model).toEqual({ modelId: 'qwen:qwen-turbo' });
|
||
});
|
||
});
|
||
});
|
||
|
||
describe('getModelFactory', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
|
||
it('获取 Anthropic 模型工厂', () => {
|
||
const factory = getModelFactory('anthropic', {
|
||
apiKey: 'test-key',
|
||
});
|
||
|
||
expect(typeof factory).toBe('function');
|
||
expect(createAnthropic).toHaveBeenCalled();
|
||
});
|
||
|
||
it('获取 DeepSeek 模型工厂', () => {
|
||
const factory = getModelFactory('deepseek', {
|
||
apiKey: 'test-key',
|
||
});
|
||
|
||
expect(typeof factory).toBe('function');
|
||
expect(createDeepSeek).toHaveBeenCalled();
|
||
});
|
||
|
||
it('获取 OpenAI 模型工厂', () => {
|
||
const factory = getModelFactory('openai', {
|
||
apiKey: 'test-key',
|
||
});
|
||
|
||
expect(typeof factory).toBe('function');
|
||
expect(createOpenAI).toHaveBeenCalled();
|
||
});
|
||
|
||
it('传递正确的选项给 provider', () => {
|
||
getModelFactory('anthropic', {
|
||
apiKey: 'my-api-key',
|
||
baseUrl: 'https://my-proxy.com',
|
||
});
|
||
|
||
expect(createAnthropic).toHaveBeenCalledWith({
|
||
apiKey: 'my-api-key',
|
||
baseURL: 'https://my-proxy.com',
|
||
});
|
||
});
|
||
|
||
it('不支持的 provider 抛出错误', () => {
|
||
expect(() => {
|
||
getModelFactory('unsupported' as any, {
|
||
apiKey: 'test-key',
|
||
});
|
||
}).toThrow('不支持的 provider: unsupported');
|
||
});
|
||
|
||
it('返回的工厂函数可以创建模型实例', () => {
|
||
const factory = getModelFactory('anthropic', {
|
||
apiKey: 'test-key',
|
||
});
|
||
|
||
const model = factory('claude-3-sonnet');
|
||
expect(model).toEqual({ modelId: 'anthropic:claude-3-sonnet' });
|
||
});
|
||
});
|