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:
2025-12-11 20:37:03 +08:00
parent f8b0cd4bec
commit bca19b7741
24 changed files with 6347 additions and 4 deletions
+362
View File
@@ -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);
});
});
});