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,275 @@
|
||||
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
||||
|
||||
// 定义可控的 mock 变量
|
||||
let mockExecAsyncResult: { stdout: string; stderr: string } | Error = {
|
||||
stdout: '[main abc1234] Test commit\n 1 file changed, 1 insertion(+)',
|
||||
stderr: '',
|
||||
};
|
||||
|
||||
let mockPermissionResult = {
|
||||
allowed: true,
|
||||
action: 'allow' as const,
|
||||
reason: undefined as string | undefined,
|
||||
needsConfirmation: false,
|
||||
};
|
||||
|
||||
// Mock child_process
|
||||
vi.mock('child_process', () => ({
|
||||
exec: vi.fn(),
|
||||
}));
|
||||
|
||||
// Mock util - 返回函数使用外部变量
|
||||
vi.mock('util', () => ({
|
||||
promisify: vi.fn(() => vi.fn(async () => {
|
||||
if (mockExecAsyncResult instanceof Error) {
|
||||
throw mockExecAsyncResult;
|
||||
}
|
||||
return mockExecAsyncResult;
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock permission manager
|
||||
vi.mock('../../../../src/permission/index.js', () => ({
|
||||
getPermissionManager: vi.fn(() => ({
|
||||
checkGitPermission: vi.fn(async () => mockPermissionResult),
|
||||
})),
|
||||
}));
|
||||
|
||||
// Mock loadDescription
|
||||
vi.mock('../../../../src/tools/load_description.js', () => ({
|
||||
loadDescription: vi.fn(() => '提交 Git 变更'),
|
||||
}));
|
||||
|
||||
import { gitCommitTool } from '../../../../src/tools/git/git_commit.js';
|
||||
import { getPermissionManager } from '../../../../src/permission/index.js';
|
||||
|
||||
describe('gitCommitTool - Git 提交工具扩展测试', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
mockExecAsyncResult = {
|
||||
stdout: '[main abc1234] Test commit\n 1 file changed, 1 insertion(+)',
|
||||
stderr: '',
|
||||
};
|
||||
mockPermissionResult = {
|
||||
allowed: true,
|
||||
action: 'allow',
|
||||
reason: undefined,
|
||||
needsConfirmation: false,
|
||||
};
|
||||
});
|
||||
|
||||
describe('基本提交', () => {
|
||||
it('成功提交变更', async () => {
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Test commit message',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.output).toContain('Test commit');
|
||||
});
|
||||
|
||||
it('没有 message 返回错误', async () => {
|
||||
const result = await gitCommitTool.execute({});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('提交信息是必填的');
|
||||
});
|
||||
|
||||
it('amend 模式无 message 允许', async () => {
|
||||
const result = await gitCommitTool.execute({
|
||||
amend: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('提交选项', () => {
|
||||
it('使用 -a 选项暂存所有变更', async () => {
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Auto stage commit',
|
||||
all: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('amend 带 message', async () => {
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Updated message',
|
||||
amend: true,
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('转义 message 中的引号', async () => {
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Message with "quotes"',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('权限检查', () => {
|
||||
it('权限拒绝返回错误', async () => {
|
||||
mockPermissionResult = {
|
||||
allowed: false,
|
||||
action: 'deny',
|
||||
reason: '不允许提交到此仓库',
|
||||
needsConfirmation: false,
|
||||
};
|
||||
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Test commit',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('权限被拒绝');
|
||||
expect(result.error).toContain('不允许提交到此仓库');
|
||||
});
|
||||
|
||||
it('需要确认时返回提示', async () => {
|
||||
mockPermissionResult = {
|
||||
allowed: false,
|
||||
action: 'ask',
|
||||
reason: '首次提交',
|
||||
needsConfirmation: true,
|
||||
};
|
||||
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'First commit',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('需要用户确认');
|
||||
expect(result.error).toContain('首次提交');
|
||||
});
|
||||
|
||||
it('权限检查包含正确上下文', async () => {
|
||||
const mockCheck = vi.fn().mockResolvedValue({
|
||||
allowed: true,
|
||||
action: 'allow',
|
||||
});
|
||||
vi.mocked(getPermissionManager).mockReturnValue({
|
||||
checkGitPermission: mockCheck,
|
||||
} as any);
|
||||
|
||||
await gitCommitTool.execute({
|
||||
message: 'Check context',
|
||||
});
|
||||
|
||||
expect(mockCheck).toHaveBeenCalledWith({
|
||||
operation: 'commit',
|
||||
message: 'Check context',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('错误处理', () => {
|
||||
it('没有变更可提交', async () => {
|
||||
mockExecAsyncResult = Object.assign(
|
||||
new Error('nothing to commit, working tree clean'),
|
||||
{ stdout: '', stderr: '', message: 'nothing to commit, working tree clean' }
|
||||
);
|
||||
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Empty commit',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('没有变更需要提交');
|
||||
expect(result.error).toContain('git_add');
|
||||
});
|
||||
|
||||
it('stderr 包含 nothing to commit', async () => {
|
||||
mockExecAsyncResult = Object.assign(
|
||||
new Error('Command failed'),
|
||||
{ stdout: '', stderr: 'nothing to commit', message: 'Command failed' }
|
||||
);
|
||||
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Empty commit',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('没有变更需要提交');
|
||||
});
|
||||
|
||||
it('其他 Git 错误', async () => {
|
||||
mockExecAsyncResult = Object.assign(
|
||||
new Error('fatal: not a git repository'),
|
||||
{ stdout: '', stderr: 'fatal: not a git repository', message: 'fatal: not a git repository' }
|
||||
);
|
||||
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Test commit',
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('not a git repository');
|
||||
});
|
||||
|
||||
it('保留 stdout 在错误中', async () => {
|
||||
mockExecAsyncResult = Object.assign(
|
||||
new Error('error'),
|
||||
{ stdout: 'some output', stderr: 'error message', message: 'error' }
|
||||
);
|
||||
|
||||
const result = await gitCommitTool.execute({
|
||||
message: 'Test',
|
||||
});
|
||||
|
||||
expect(result.output).toBe('some output');
|
||||
expect(result.error).toBe('error message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('工具元数据', () => {
|
||||
it('包含正确的名称', () => {
|
||||
expect(gitCommitTool.name).toBe('git_commit');
|
||||
});
|
||||
|
||||
it('包含正确的类别', () => {
|
||||
expect(gitCommitTool.metadata.category).toBe('git');
|
||||
});
|
||||
|
||||
it('包含关键词', () => {
|
||||
expect(gitCommitTool.metadata.keywords).toContain('git');
|
||||
expect(gitCommitTool.metadata.keywords).toContain('commit');
|
||||
expect(gitCommitTool.metadata.keywords).toContain('提交');
|
||||
});
|
||||
|
||||
it('参数定义正确', () => {
|
||||
expect(gitCommitTool.parameters.message.required).toBe(true);
|
||||
expect(gitCommitTool.parameters.amend.required).toBe(false);
|
||||
expect(gitCommitTool.parameters.all.required).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('输出格式', () => {
|
||||
it('包含 stdout', async () => {
|
||||
mockExecAsyncResult = {
|
||||
stdout: 'commit output',
|
||||
stderr: '',
|
||||
};
|
||||
|
||||
const result = await gitCommitTool.execute({ message: 'test' });
|
||||
|
||||
expect(result.output).toBe('commit output');
|
||||
});
|
||||
|
||||
it('包含 stdout 和 stderr', async () => {
|
||||
mockExecAsyncResult = {
|
||||
stdout: 'commit output',
|
||||
stderr: 'warning message',
|
||||
};
|
||||
|
||||
const result = await gitCommitTool.execute({ message: 'test' });
|
||||
|
||||
expect(result.output).toContain('commit output');
|
||||
expect(result.output).toContain('warning message');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user