test: 补充单元测试提升代码覆盖率
新增测试文件: - 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
This commit is contained in:
@@ -0,0 +1,362 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { z } from 'zod';
|
||||
import { buildZodSchema, type ToolParameter } from '../../../src/types/index.js';
|
||||
|
||||
describe('buildZodSchema', () => {
|
||||
describe('基本类型转换', () => {
|
||||
it('转换 string 类型参数', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
name: {
|
||||
type: 'string',
|
||||
description: '用户名',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
expect(schema.shape.name).toBeDefined();
|
||||
|
||||
// 验证 schema 可以正确解析
|
||||
const result = schema.safeParse({ name: 'test' });
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.name).toBe('test');
|
||||
}
|
||||
});
|
||||
|
||||
it('转换 number 类型参数', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
age: {
|
||||
type: 'number',
|
||||
description: '年龄',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
const result = schema.safeParse({ age: 25 });
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.age).toBe(25);
|
||||
}
|
||||
|
||||
// 字符串应该失败
|
||||
const invalidResult = schema.safeParse({ age: '25' });
|
||||
expect(invalidResult.success).toBe(false);
|
||||
});
|
||||
|
||||
it('转换 boolean 类型参数', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
active: {
|
||||
type: 'boolean',
|
||||
description: '是否激活',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
expect(schema.safeParse({ active: true }).success).toBe(true);
|
||||
expect(schema.safeParse({ active: false }).success).toBe(true);
|
||||
expect(schema.safeParse({ active: 'true' }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('转换 array 类型参数', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
items: {
|
||||
type: 'array',
|
||||
description: '项目列表',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
const result = schema.safeParse({ items: [1, 'two', { three: 3 }] });
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.items).toEqual([1, 'two', { three: 3 }]);
|
||||
}
|
||||
});
|
||||
|
||||
it('转换 object 类型参数', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
config: {
|
||||
type: 'object',
|
||||
description: '配置对象',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
const result = schema.safeParse({ config: { key: 'value', nested: { a: 1 } } });
|
||||
expect(result.success).toBe(true);
|
||||
if (result.success) {
|
||||
expect(result.data.config).toEqual({ key: 'value', nested: { a: 1 } });
|
||||
}
|
||||
});
|
||||
|
||||
it('处理未知类型为 unknown', () => {
|
||||
const parameters = {
|
||||
data: {
|
||||
type: 'custom' as any,
|
||||
description: '自定义类型',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
// unknown 类型接受任何值
|
||||
expect(schema.safeParse({ data: 'anything' }).success).toBe(true);
|
||||
expect(schema.safeParse({ data: 123 }).success).toBe(true);
|
||||
expect(schema.safeParse({ data: { obj: true } }).success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('可选参数', () => {
|
||||
it('required=false 时参数可选', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
optional_field: {
|
||||
type: 'string',
|
||||
description: '可选字段',
|
||||
required: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
// 不提供可选字段应该成功
|
||||
expect(schema.safeParse({}).success).toBe(true);
|
||||
|
||||
// 提供可选字段也应该成功
|
||||
expect(schema.safeParse({ optional_field: 'value' }).success).toBe(true);
|
||||
});
|
||||
|
||||
it('required 未设置时默认可选', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
field: {
|
||||
type: 'string',
|
||||
description: '字段',
|
||||
// required 未设置
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
// 应该是可选的
|
||||
expect(schema.safeParse({}).success).toBe(true);
|
||||
});
|
||||
|
||||
it('required=true 时参数必须', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
required_field: {
|
||||
type: 'string',
|
||||
description: '必填字段',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
// 不提供必填字段应该失败
|
||||
expect(schema.safeParse({}).success).toBe(false);
|
||||
|
||||
// 提供必填字段应该成功
|
||||
expect(schema.safeParse({ required_field: 'value' }).success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('混合参数', () => {
|
||||
it('处理多个混合类型参数', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: '文件路径',
|
||||
required: true,
|
||||
},
|
||||
line_number: {
|
||||
type: 'number',
|
||||
description: '行号',
|
||||
required: false,
|
||||
},
|
||||
recursive: {
|
||||
type: 'boolean',
|
||||
description: '递归处理',
|
||||
required: false,
|
||||
},
|
||||
patterns: {
|
||||
type: 'array',
|
||||
description: '模式列表',
|
||||
required: false,
|
||||
},
|
||||
options: {
|
||||
type: 'object',
|
||||
description: '额外选项',
|
||||
required: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
// 只提供必填参数
|
||||
expect(schema.safeParse({ path: '/test/file.txt' }).success).toBe(true);
|
||||
|
||||
// 提供所有参数
|
||||
const fullResult = schema.safeParse({
|
||||
path: '/test/file.txt',
|
||||
line_number: 42,
|
||||
recursive: true,
|
||||
patterns: ['*.ts', '*.js'],
|
||||
options: { encoding: 'utf-8' },
|
||||
});
|
||||
expect(fullResult.success).toBe(true);
|
||||
});
|
||||
|
||||
it('空参数对象返回空 schema', () => {
|
||||
const parameters: Record<string, ToolParameter> = {};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
expect(schema.safeParse({}).success).toBe(true);
|
||||
expect(Object.keys(schema.shape)).toHaveLength(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('描述信息', () => {
|
||||
it('保留参数描述', () => {
|
||||
const parameters: Record<string, ToolParameter> = {
|
||||
message: {
|
||||
type: 'string',
|
||||
description: '要发送的消息内容',
|
||||
required: true,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(parameters);
|
||||
|
||||
// 验证 schema 被正确创建
|
||||
expect(schema.shape.message).toBeDefined();
|
||||
|
||||
// 验证 schema 可以正常工作
|
||||
const result = schema.safeParse({ message: 'test' });
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('实际工具参数示例', () => {
|
||||
it('bash 工具参数', () => {
|
||||
const bashParameters: Record<string, ToolParameter> = {
|
||||
command: {
|
||||
type: 'string',
|
||||
description: '要执行的 bash 命令',
|
||||
required: true,
|
||||
},
|
||||
timeout: {
|
||||
type: 'number',
|
||||
description: '超时时间(毫秒)',
|
||||
required: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(bashParameters);
|
||||
|
||||
expect(schema.safeParse({ command: 'ls -la' }).success).toBe(true);
|
||||
expect(schema.safeParse({ command: 'ls -la', timeout: 5000 }).success).toBe(true);
|
||||
expect(schema.safeParse({}).success).toBe(false);
|
||||
});
|
||||
|
||||
it('read_file 工具参数', () => {
|
||||
const readFileParameters: Record<string, ToolParameter> = {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: '文件路径',
|
||||
required: true,
|
||||
},
|
||||
encoding: {
|
||||
type: 'string',
|
||||
description: '文件编码',
|
||||
required: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(readFileParameters);
|
||||
|
||||
expect(schema.safeParse({ path: '/etc/hosts' }).success).toBe(true);
|
||||
expect(schema.safeParse({ path: '/etc/hosts', encoding: 'utf-8' }).success).toBe(true);
|
||||
});
|
||||
|
||||
it('write_file 工具参数', () => {
|
||||
const writeFileParameters: Record<string, ToolParameter> = {
|
||||
path: {
|
||||
type: 'string',
|
||||
description: '文件路径',
|
||||
required: true,
|
||||
},
|
||||
content: {
|
||||
type: 'string',
|
||||
description: '文件内容',
|
||||
required: true,
|
||||
},
|
||||
create_dirs: {
|
||||
type: 'boolean',
|
||||
description: '自动创建目录',
|
||||
required: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(writeFileParameters);
|
||||
|
||||
expect(schema.safeParse({ path: '/tmp/test.txt', content: 'hello' }).success).toBe(true);
|
||||
expect(schema.safeParse({ path: '/tmp/test.txt', content: 'hello', create_dirs: true }).success).toBe(true);
|
||||
expect(schema.safeParse({ path: '/tmp/test.txt' }).success).toBe(false);
|
||||
});
|
||||
|
||||
it('task 工具参数', () => {
|
||||
const taskParameters: Record<string, ToolParameter> = {
|
||||
description: {
|
||||
type: 'string',
|
||||
description: '任务描述',
|
||||
required: true,
|
||||
},
|
||||
prompt: {
|
||||
type: 'string',
|
||||
description: '任务提示',
|
||||
required: true,
|
||||
},
|
||||
subagent_type: {
|
||||
type: 'string',
|
||||
description: 'Agent 类型',
|
||||
required: true,
|
||||
},
|
||||
run_in_background: {
|
||||
type: 'boolean',
|
||||
description: '是否后台运行',
|
||||
required: false,
|
||||
},
|
||||
images: {
|
||||
type: 'array',
|
||||
description: '图片数据',
|
||||
required: false,
|
||||
},
|
||||
};
|
||||
|
||||
const schema = buildZodSchema(taskParameters);
|
||||
|
||||
expect(schema.safeParse({
|
||||
description: 'Test task',
|
||||
prompt: 'Do something',
|
||||
subagent_type: 'explore',
|
||||
}).success).toBe(true);
|
||||
|
||||
expect(schema.safeParse({
|
||||
description: 'Vision task',
|
||||
prompt: 'Analyze image',
|
||||
subagent_type: 'vision',
|
||||
images: [{ data: 'base64...', mimeType: 'image/png' }],
|
||||
}).success).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user