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
276 lines
7.5 KiB
TypeScript
276 lines
7.5 KiB
TypeScript
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');
|
|
});
|
|
});
|
|
});
|