mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-21 17:27:54 +00:00
🎯 PR: Improve Edit Tool Reliability with Fuzzy Matching Pipeline (#1025)
This commit is contained in:
153
packages/core/src/utils/editHelper.test.ts
Normal file
153
packages/core/src/utils/editHelper.test.ts
Normal file
@@ -0,0 +1,153 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2025 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
countOccurrences,
|
||||
maybeAugmentOldStringForDeletion,
|
||||
normalizeEditStrings,
|
||||
} from './editHelper.js';
|
||||
|
||||
describe('normalizeEditStrings', () => {
|
||||
const file = `const one = 1;
|
||||
const two = 2;
|
||||
`;
|
||||
|
||||
it('returns literal matches unchanged and trims new_string trailing whitespace', () => {
|
||||
const result = normalizeEditStrings(
|
||||
file,
|
||||
'const two = 2;',
|
||||
' const two = 42; ',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
oldString: 'const two = 2;',
|
||||
newString: ' const two = 42;',
|
||||
});
|
||||
});
|
||||
|
||||
it('normalizes smart quotes to match on-disk text', () => {
|
||||
const result = normalizeEditStrings(
|
||||
"const greeting = 'Don't';\n",
|
||||
'const greeting = ‘Don’t’;',
|
||||
'const greeting = “Hello”; ',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
oldString: "const greeting = 'Don't';",
|
||||
newString: 'const greeting = “Hello”;',
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to original strings when no match is found', () => {
|
||||
const result = normalizeEditStrings(file, 'missing text', 'replacement');
|
||||
expect(result).toEqual({
|
||||
oldString: 'missing text',
|
||||
newString: 'replacement',
|
||||
});
|
||||
});
|
||||
|
||||
it('still trims new_string when editing a brand-new file', () => {
|
||||
const result = normalizeEditStrings(null, '', 'new file contents ');
|
||||
expect(result).toEqual({
|
||||
oldString: '',
|
||||
newString: 'new file contents',
|
||||
});
|
||||
});
|
||||
|
||||
it('matches unicode dash variants', () => {
|
||||
const result = normalizeEditStrings(
|
||||
'const range = "1-2";\n',
|
||||
'const range = "1\u20132";',
|
||||
'const range = "3\u20135"; ',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
oldString: 'const range = "1-2";',
|
||||
newString: 'const range = "3\u20135";',
|
||||
});
|
||||
});
|
||||
|
||||
it('matches when trailing whitespace differs only at line ends', () => {
|
||||
const result = normalizeEditStrings(
|
||||
'value = 1;\n',
|
||||
'value = 1; \n',
|
||||
'value = 2; \n',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
oldString: 'value = 1;\n',
|
||||
newString: 'value = 2;\n',
|
||||
});
|
||||
});
|
||||
|
||||
it('treats non-breaking spaces as regular spaces', () => {
|
||||
const result = normalizeEditStrings(
|
||||
'const label = "hello world";\n',
|
||||
'const label = "hello\u00a0world";',
|
||||
'const label = "hi\u00a0world";',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
oldString: 'const label = "hello world";',
|
||||
newString: 'const label = "hi\u00a0world";',
|
||||
});
|
||||
});
|
||||
|
||||
it('drops trailing newline from new content when the file lacks it', () => {
|
||||
const result = normalizeEditStrings(
|
||||
'console.log("hi")',
|
||||
'console.log("hi")\n',
|
||||
'console.log("bye")\n',
|
||||
);
|
||||
expect(result).toEqual({
|
||||
oldString: 'console.log("hi")',
|
||||
newString: 'console.log("bye")',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('countOccurrences', () => {
|
||||
it('returns zero when substring empty or missing', () => {
|
||||
expect(countOccurrences('abc', '')).toBe(0);
|
||||
expect(countOccurrences('abc', 'z')).toBe(0);
|
||||
});
|
||||
|
||||
it('counts non-overlapping occurrences', () => {
|
||||
expect(countOccurrences('aaaa', 'aa')).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('maybeAugmentOldStringForDeletion', () => {
|
||||
const file = 'console.log("hi")\nconsole.log("bye")\n';
|
||||
|
||||
it('appends newline when deleting text followed by newline', () => {
|
||||
expect(
|
||||
maybeAugmentOldStringForDeletion(file, 'console.log("hi")', ''),
|
||||
).toBe('console.log("hi")\n');
|
||||
});
|
||||
|
||||
it('leaves strings untouched when not deleting', () => {
|
||||
expect(
|
||||
maybeAugmentOldStringForDeletion(
|
||||
file,
|
||||
'console.log("hi")',
|
||||
'replacement',
|
||||
),
|
||||
).toBe('console.log("hi")');
|
||||
});
|
||||
|
||||
it('does not append newline when file lacks the variant', () => {
|
||||
expect(
|
||||
maybeAugmentOldStringForDeletion(
|
||||
'console.log("hi")',
|
||||
'console.log("hi")',
|
||||
'',
|
||||
),
|
||||
).toBe('console.log("hi")');
|
||||
});
|
||||
|
||||
it('no-ops when the old string already ends with a newline', () => {
|
||||
expect(
|
||||
maybeAugmentOldStringForDeletion(file, 'console.log("bye")\n', ''),
|
||||
).toBe('console.log("bye")\n');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user