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
354 lines
10 KiB
TypeScript
354 lines
10 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { computeDiff, countChanges, formatEditDiff, formatDiff } from '../../../src/utils/diff.js';
|
|
|
|
describe('computeDiff - 计算文件差异', () => {
|
|
describe('新文件', () => {
|
|
it('新文件所有行标记为新增', () => {
|
|
const diff = computeDiff(null, 'line1\nline2\nline3');
|
|
|
|
expect(diff.isNew).toBe(true);
|
|
expect(diff.oldContent).toBeNull();
|
|
expect(diff.hunks).toHaveLength(1);
|
|
|
|
const hunk = diff.hunks[0];
|
|
expect(hunk.oldStart).toBe(0);
|
|
expect(hunk.oldCount).toBe(0);
|
|
expect(hunk.newStart).toBe(1);
|
|
expect(hunk.newCount).toBe(3);
|
|
|
|
expect(hunk.lines).toHaveLength(3);
|
|
expect(hunk.lines.every((l) => l.type === 'add')).toBe(true);
|
|
});
|
|
|
|
it('空新文件', () => {
|
|
const diff = computeDiff(null, '');
|
|
|
|
expect(diff.isNew).toBe(true);
|
|
expect(diff.hunks).toHaveLength(1);
|
|
expect(diff.hunks[0].lines).toHaveLength(1); // 空行也是一行
|
|
});
|
|
});
|
|
|
|
describe('修改文件', () => {
|
|
it('相同内容无变化', () => {
|
|
const content = 'line1\nline2\nline3';
|
|
const diff = computeDiff(content, content);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks).toHaveLength(0);
|
|
});
|
|
|
|
it('单行修改', () => {
|
|
const oldContent = 'line1\nline2\nline3';
|
|
const newContent = 'line1\nmodified\nline3';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
|
|
// 应该有删除和新增
|
|
const allLines = diff.hunks.flatMap((h) => h.lines);
|
|
expect(allLines.some((l) => l.type === 'remove' && l.content === 'line2')).toBe(true);
|
|
expect(allLines.some((l) => l.type === 'add' && l.content === 'modified')).toBe(true);
|
|
});
|
|
|
|
it('添加行', () => {
|
|
const oldContent = 'line1\nline3';
|
|
const newContent = 'line1\nline2\nline3';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
const allLines = diff.hunks.flatMap((h) => h.lines);
|
|
expect(allLines.some((l) => l.type === 'add' && l.content === 'line2')).toBe(true);
|
|
});
|
|
|
|
it('删除行', () => {
|
|
const oldContent = 'line1\nline2\nline3';
|
|
const newContent = 'line1\nline3';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
const allLines = diff.hunks.flatMap((h) => h.lines);
|
|
expect(allLines.some((l) => l.type === 'remove' && l.content === 'line2')).toBe(true);
|
|
});
|
|
|
|
it('全部替换', () => {
|
|
const oldContent = 'old1\nold2\nold3';
|
|
const newContent = 'new1\nnew2';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
|
|
const changes = countChanges(diff);
|
|
expect(changes.deletions).toBeGreaterThan(0);
|
|
expect(changes.additions).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('特殊情况', () => {
|
|
it('空文件变为非空', () => {
|
|
const diff = computeDiff('', 'new content');
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('非空文件变为空', () => {
|
|
const diff = computeDiff('old content', '');
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('包含空行的内容', () => {
|
|
const oldContent = 'line1\n\nline3';
|
|
const newContent = 'line1\nline2\n\nline3';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
// 应该正确处理空行
|
|
});
|
|
|
|
it('单行文件', () => {
|
|
const diff = computeDiff('old', 'new');
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
describe('行号正确性', () => {
|
|
it('新增行有正确的行号', () => {
|
|
const diff = computeDiff(null, 'line1\nline2\nline3');
|
|
|
|
const lines = diff.hunks[0].lines;
|
|
expect(lines[0].lineNumber).toBe(1);
|
|
expect(lines[1].lineNumber).toBe(2);
|
|
expect(lines[2].lineNumber).toBe(3);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('countChanges - 统计变更数量', () => {
|
|
it('新文件计算新增行', () => {
|
|
const diff = computeDiff(null, 'line1\nline2\nline3');
|
|
const changes = countChanges(diff);
|
|
|
|
expect(changes.additions).toBe(3);
|
|
expect(changes.deletions).toBe(0);
|
|
});
|
|
|
|
it('修改文件计算增删', () => {
|
|
const diff = computeDiff('old1\nold2', 'new1\nold2\nnew2');
|
|
const changes = countChanges(diff);
|
|
|
|
expect(changes.additions).toBeGreaterThan(0);
|
|
expect(changes.deletions).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('空 diff 返回零', () => {
|
|
const diff = computeDiff('same', 'same');
|
|
const changes = countChanges(diff);
|
|
|
|
expect(changes.additions).toBe(0);
|
|
expect(changes.deletions).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe('formatEditDiff - 格式化编辑差异', () => {
|
|
it('显示删除和新增内容', () => {
|
|
const result = formatEditDiff('old text', 'new text');
|
|
|
|
expect(result).toContain('变更内容');
|
|
expect(result).toContain('old text');
|
|
expect(result).toContain('new text');
|
|
});
|
|
|
|
it('多行内容正确显示', () => {
|
|
const result = formatEditDiff('line1\nline2', 'new1\nnew2\nnew3');
|
|
|
|
expect(result).toContain('line1');
|
|
expect(result).toContain('line2');
|
|
expect(result).toContain('new1');
|
|
expect(result).toContain('new2');
|
|
expect(result).toContain('new3');
|
|
});
|
|
|
|
it('空内容处理', () => {
|
|
const result = formatEditDiff('', 'new');
|
|
|
|
expect(result).toContain('new');
|
|
});
|
|
});
|
|
|
|
describe('DiffResult 结构', () => {
|
|
it('包含所有必要字段', () => {
|
|
const diff = computeDiff('old', 'new');
|
|
|
|
expect(diff).toHaveProperty('oldContent');
|
|
expect(diff).toHaveProperty('newContent');
|
|
expect(diff).toHaveProperty('isNew');
|
|
expect(diff).toHaveProperty('hunks');
|
|
});
|
|
|
|
it('hunk 包含所有必要字段', () => {
|
|
const diff = computeDiff('old', 'new');
|
|
|
|
if (diff.hunks.length > 0) {
|
|
const hunk = diff.hunks[0];
|
|
expect(hunk).toHaveProperty('oldStart');
|
|
expect(hunk).toHaveProperty('oldCount');
|
|
expect(hunk).toHaveProperty('newStart');
|
|
expect(hunk).toHaveProperty('newCount');
|
|
expect(hunk).toHaveProperty('lines');
|
|
}
|
|
});
|
|
|
|
it('line 包含所有必要字段', () => {
|
|
const diff = computeDiff(null, 'content');
|
|
|
|
const line = diff.hunks[0].lines[0];
|
|
expect(line).toHaveProperty('type');
|
|
expect(line).toHaveProperty('lineNumber');
|
|
expect(line).toHaveProperty('content');
|
|
});
|
|
});
|
|
|
|
describe('LCS 算法测试', () => {
|
|
it('相同前缀保留', () => {
|
|
const oldContent = 'prefix\ncommon\nold';
|
|
const newContent = 'prefix\ncommon\nnew';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
// common 行应该保持为上下文
|
|
const contextLines = diff.hunks.flatMap((h) =>
|
|
h.lines.filter((l) => l.type === 'context')
|
|
);
|
|
// prefix 和 common 可能作为上下文保留
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('相同后缀保留', () => {
|
|
const oldContent = 'old\ncommon\nsuffix';
|
|
const newContent = 'new\ncommon\nsuffix';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('完全不同的内容', () => {
|
|
const oldContent = 'a\nb\nc';
|
|
const newContent = 'x\ny\nz';
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
const changes = countChanges(diff);
|
|
expect(changes.additions).toBe(3);
|
|
expect(changes.deletions).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe('formatDiff - 格式化 diff 输出', () => {
|
|
it('新文件显示新增标记', () => {
|
|
const diff = computeDiff(null, 'line1\nline2');
|
|
const result = formatDiff(diff, '/test/file.ts');
|
|
|
|
expect(result).toContain('新文件');
|
|
expect(result).toContain('/test/file.ts');
|
|
});
|
|
|
|
it('修改文件显示原文件和修改后标记', () => {
|
|
const diff = computeDiff('old', 'new');
|
|
const result = formatDiff(diff, '/test/file.ts');
|
|
|
|
expect(result).toContain('原文件');
|
|
expect(result).toContain('修改后');
|
|
expect(result).toContain('/test/file.ts');
|
|
});
|
|
|
|
it('hunk 头部显示正确的行号范围', () => {
|
|
const diff = computeDiff('line1\nline2', 'line1\nnew\nline2');
|
|
const result = formatDiff(diff, '/test/file.ts');
|
|
|
|
expect(result).toContain('@@');
|
|
});
|
|
|
|
it('新增行显示 + 前缀', () => {
|
|
const diff = computeDiff(null, 'added line');
|
|
const result = formatDiff(diff, '/test/file.ts');
|
|
|
|
expect(result).toContain('+');
|
|
expect(result).toContain('added line');
|
|
});
|
|
|
|
it('删除行显示 - 前缀', () => {
|
|
const diff = computeDiff('removed line', 'new line');
|
|
const result = formatDiff(diff, '/test/file.ts');
|
|
|
|
expect(result).toContain('-');
|
|
expect(result).toContain('removed line');
|
|
});
|
|
|
|
it('空 diff 返回基本格式', () => {
|
|
const diff = computeDiff('same', 'same');
|
|
const result = formatDiff(diff, '/test/file.ts');
|
|
|
|
// 没有 hunks 时只有头部
|
|
expect(result).toContain('/test/file.ts');
|
|
});
|
|
});
|
|
|
|
describe('实际代码场景', () => {
|
|
it('函数修改', () => {
|
|
const oldContent = `function hello() {
|
|
console.log("Hello");
|
|
}`;
|
|
|
|
const newContent = `function hello() {
|
|
console.log("Hello World");
|
|
return true;
|
|
}`;
|
|
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.isNew).toBe(false);
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
|
|
const changes = countChanges(diff);
|
|
expect(changes.additions).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('导入语句添加', () => {
|
|
const oldContent = `import { a } from 'module';
|
|
|
|
export function test() {}`;
|
|
|
|
const newContent = `import { a } from 'module';
|
|
import { b } from 'another';
|
|
|
|
export function test() {}`;
|
|
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
const changes = countChanges(diff);
|
|
expect(changes.additions).toBeGreaterThan(0);
|
|
});
|
|
|
|
it('配置文件修改', () => {
|
|
const oldContent = `{
|
|
"name": "test",
|
|
"version": "1.0.0"
|
|
}`;
|
|
|
|
const newContent = `{
|
|
"name": "test",
|
|
"version": "1.1.0",
|
|
"description": "Added description"
|
|
}`;
|
|
|
|
const diff = computeDiff(oldContent, newContent);
|
|
|
|
expect(diff.hunks.length).toBeGreaterThan(0);
|
|
});
|
|
});
|