Refactor test cases in search-replace and unified strategies to use async/await for applyDiff method, ensuring consistent handling of asynchronous operations across all tests.

This commit is contained in:
Daniel Riccio
2025-01-07 19:00:29 -05:00
parent 70cf27071b
commit 2857dd4996
2 changed files with 130 additions and 130 deletions

View File

@@ -8,7 +8,7 @@ describe('SearchReplaceDiffStrategy', () => {
strategy = new SearchReplaceDiffStrategy(1.0, 5) // Default 1.0 threshold for exact matching, 5 line buffer for tests strategy = new SearchReplaceDiffStrategy(1.0, 5) // Default 1.0 threshold for exact matching, 5 line buffer for tests
}) })
it('should replace matching content', () => { it('should replace matching content', async () => {
const originalContent = 'function hello() {\n console.log("hello")\n}\n' const originalContent = 'function hello() {\n console.log("hello")\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -21,14 +21,14 @@ function hello() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('function hello() {\n console.log("hello world")\n}\n') expect(result.content).toBe('function hello() {\n console.log("hello world")\n}\n')
} }
}) })
it('should match content with different surrounding whitespace', () => { it('should match content with different surrounding whitespace', async () => {
const originalContent = '\nfunction example() {\n return 42;\n}\n\n' const originalContent = '\nfunction example() {\n return 42;\n}\n\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -41,14 +41,14 @@ function example() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('\nfunction example() {\n return 43;\n}\n\n') expect(result.content).toBe('\nfunction example() {\n return 43;\n}\n\n')
} }
}) })
it('should match content with different indentation in search block', () => { it('should match content with different indentation in search block', async () => {
const originalContent = ' function test() {\n return true;\n }\n' const originalContent = ' function test() {\n return true;\n }\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -61,14 +61,14 @@ function test() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(' function test() {\n return false;\n }\n') expect(result.content).toBe(' function test() {\n return false;\n }\n')
} }
}) })
it('should handle tab-based indentation', () => { it('should handle tab-based indentation', async () => {
const originalContent = "function test() {\n\treturn true;\n}\n" const originalContent = "function test() {\n\treturn true;\n}\n"
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -81,14 +81,14 @@ function test() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe("function test() {\n\treturn false;\n}\n") expect(result.content).toBe("function test() {\n\treturn false;\n}\n")
} }
}) })
it('should preserve mixed tabs and spaces', () => { it('should preserve mixed tabs and spaces', async () => {
const originalContent = "\tclass Example {\n\t constructor() {\n\t\tthis.value = 0;\n\t }\n\t}" const originalContent = "\tclass Example {\n\t constructor() {\n\t\tthis.value = 0;\n\t }\n\t}"
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -105,14 +105,14 @@ function test() {
\t} \t}
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe("\tclass Example {\n\t constructor() {\n\t\tthis.value = 1;\n\t }\n\t}") expect(result.content).toBe("\tclass Example {\n\t constructor() {\n\t\tthis.value = 1;\n\t }\n\t}")
} }
}) })
it('should handle additional indentation with tabs', () => { it('should handle additional indentation with tabs', async () => {
const originalContent = "\tfunction test() {\n\t\treturn true;\n\t}" const originalContent = "\tfunction test() {\n\t\treturn true;\n\t}"
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -126,14 +126,14 @@ function test() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe("\tfunction test() {\n\t\t// Add comment\n\t\treturn false;\n\t}") expect(result.content).toBe("\tfunction test() {\n\t\t// Add comment\n\t\treturn false;\n\t}")
} }
}) })
it('should preserve exact indentation characters when adding lines', () => { it('should preserve exact indentation characters when adding lines', async () => {
const originalContent = "\tfunction test() {\n\t\treturn true;\n\t}" const originalContent = "\tfunction test() {\n\t\treturn true;\n\t}"
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -148,14 +148,14 @@ function test() {
\t} \t}
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe("\tfunction test() {\n\t\t// First comment\n\t\t// Second comment\n\t\treturn true;\n\t}") expect(result.content).toBe("\tfunction test() {\n\t\t// First comment\n\t\t// Second comment\n\t\treturn true;\n\t}")
} }
}) })
it('should handle Windows-style CRLF line endings', () => { it('should handle Windows-style CRLF line endings', async () => {
const originalContent = "function test() {\r\n return true;\r\n}\r\n" const originalContent = "function test() {\r\n return true;\r\n}\r\n"
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -168,14 +168,14 @@ function test() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe("function test() {\r\n return false;\r\n}\r\n") expect(result.content).toBe("function test() {\r\n return false;\r\n}\r\n")
} }
}) })
it('should return false if search content does not match', () => { it('should return false if search content does not match', async () => {
const originalContent = 'function hello() {\n console.log("hello")\n}\n' const originalContent = 'function hello() {\n console.log("hello")\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -188,19 +188,19 @@ function hello() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(false) expect(result.success).toBe(false)
}) })
it('should return false if diff format is invalid', () => { it('should return false if diff format is invalid', async () => {
const originalContent = 'function hello() {\n console.log("hello")\n}\n' const originalContent = 'function hello() {\n console.log("hello")\n}\n'
const diffContent = `test.ts\nInvalid diff format` const diffContent = `test.ts\nInvalid diff format`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(false) expect(result.success).toBe(false)
}) })
it('should handle multiple lines with proper indentation', () => { it('should handle multiple lines with proper indentation', async () => {
const originalContent = 'class Example {\n constructor() {\n this.value = 0\n }\n\n getValue() {\n return this.value\n }\n}\n' const originalContent = 'class Example {\n constructor() {\n this.value = 0\n }\n\n getValue() {\n return this.value\n }\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -215,14 +215,14 @@ function hello() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('class Example {\n constructor() {\n this.value = 0\n }\n\n getValue() {\n // Add logging\n console.log("Getting value")\n return this.value\n }\n}\n') expect(result.content).toBe('class Example {\n constructor() {\n this.value = 0\n }\n\n getValue() {\n // Add logging\n console.log("Getting value")\n return this.value\n }\n}\n')
} }
}) })
it('should preserve whitespace exactly in the output', () => { it('should preserve whitespace exactly in the output', async () => {
const originalContent = " indented\n more indented\n back\n" const originalContent = " indented\n more indented\n back\n"
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -235,14 +235,14 @@ function hello() {
end end
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(" modified\n still indented\n end\n") expect(result.content).toBe(" modified\n still indented\n end\n")
} }
}) })
it('should preserve indentation when adding new lines after existing content', () => { it('should preserve indentation when adding new lines after existing content', async () => {
const originalContent = ' onScroll={() => updateHighlights()}' const originalContent = ' onScroll={() => updateHighlights()}'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -255,14 +255,14 @@ function hello() {
}} }}
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(' onScroll={() => updateHighlights()}\n onDragOver={(e) => {\n e.preventDefault()\n e.stopPropagation()\n }}') expect(result.content).toBe(' onScroll={() => updateHighlights()}\n onDragOver={(e) => {\n e.preventDefault()\n e.stopPropagation()\n }}')
} }
}) })
it('should handle varying indentation levels correctly', () => { it('should handle varying indentation levels correctly', async () => {
const originalContent = ` const originalContent = `
class Example { class Example {
constructor() { constructor() {
@@ -296,7 +296,7 @@ class Example {
} }
>>>>>>> REPLACE`.trim(); >>>>>>> REPLACE`.trim();
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(` expect(result.content).toBe(`
@@ -313,7 +313,7 @@ class Example {
} }
}) })
it('should handle mixed indentation styles in the same file', () => { it('should handle mixed indentation styles in the same file', async () => {
const originalContent = `class Example { const originalContent = `class Example {
constructor() { constructor() {
this.value = 0; this.value = 0;
@@ -340,7 +340,7 @@ class Example {
} }
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`class Example { expect(result.content).toBe(`class Example {
@@ -355,7 +355,7 @@ class Example {
} }
}) })
it('should handle Python-style significant whitespace', () => { it('should handle Python-style significant whitespace', async () => {
const originalContent = `def example(): const originalContent = `def example():
if condition: if condition:
do_something() do_something()
@@ -376,7 +376,7 @@ class Example {
process(item) process(item)
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`def example(): expect(result.content).toBe(`def example():
@@ -389,7 +389,7 @@ class Example {
} }
}); });
it('should preserve empty lines with indentation', () => { it('should preserve empty lines with indentation', async () => {
const originalContent = `function test() { const originalContent = `function test() {
const x = 1; const x = 1;
@@ -409,7 +409,7 @@ class Example {
if (x) { if (x) {
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function test() { expect(result.content).toBe(`function test() {
@@ -423,7 +423,7 @@ class Example {
} }
}); });
it('should handle indentation when replacing entire blocks', () => { it('should handle indentation when replacing entire blocks', async () => {
const originalContent = `class Test { const originalContent = `class Test {
method() { method() {
if (true) { if (true) {
@@ -450,7 +450,7 @@ class Example {
} }
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`class Test { expect(result.content).toBe(`class Test {
@@ -467,7 +467,7 @@ class Example {
} }
}); });
it('should handle negative indentation relative to search content', () => { it('should handle negative indentation relative to search content', async () => {
const originalContent = `class Example { const originalContent = `class Example {
constructor() { constructor() {
if (true) { if (true) {
@@ -485,7 +485,7 @@ class Example {
this.setup(); this.setup();
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`class Example { expect(result.content).toBe(`class Example {
@@ -499,7 +499,7 @@ class Example {
} }
}); });
it('should handle extreme negative indentation (no indent)', () => { it('should handle extreme negative indentation (no indent)', async () => {
const originalContent = `class Example { const originalContent = `class Example {
constructor() { constructor() {
if (true) { if (true) {
@@ -514,7 +514,7 @@ class Example {
this.init(); this.init();
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`class Example { expect(result.content).toBe(`class Example {
@@ -527,7 +527,7 @@ this.init();
} }
}); });
it('should handle mixed indentation changes in replace block', () => { it('should handle mixed indentation changes in replace block', async () => {
const originalContent = `class Example { const originalContent = `class Example {
constructor() { constructor() {
if (true) { if (true) {
@@ -548,7 +548,7 @@ this.init();
this.validate(); this.validate();
>>>>>>> REPLACE`; >>>>>>> REPLACE`;
const result = strategy.applyDiff(originalContent, diffContent); const result = await strategy.applyDiff(originalContent, diffContent);
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`class Example { expect(result.content).toBe(`class Example {
@@ -563,7 +563,7 @@ this.init();
} }
}); });
it('should find matches from middle out', () => { it('should find matches from middle out', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return "target"; return "target";
@@ -595,7 +595,7 @@ function five() {
// Search around the middle (function three) // Search around the middle (function three)
// Even though all functions contain the target text, // Even though all functions contain the target text,
// it should match the one closest to line 9 first // it should match the one closest to line 9 first
const result = strategy.applyDiff(originalContent, diffContent, 9, 9) const result = await strategy.applyDiff(originalContent, diffContent, 9, 9)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -621,15 +621,15 @@ function five() {
}) })
}) })
describe('line number stripping', () => { describe('line number stripping', async () => {
describe('line number stripping', () => { describe('line number stripping', async () => {
let strategy: SearchReplaceDiffStrategy let strategy: SearchReplaceDiffStrategy
beforeEach(() => { beforeEach(() => {
strategy = new SearchReplaceDiffStrategy() strategy = new SearchReplaceDiffStrategy()
}) })
it('should strip line numbers from both search and replace sections', () => { it('should strip line numbers from both search and replace sections', async () => {
const originalContent = 'function test() {\n return true;\n}\n' const originalContent = 'function test() {\n return true;\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -642,14 +642,14 @@ function five() {
3 | } 3 | }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('function test() {\n return false;\n}\n') expect(result.content).toBe('function test() {\n return false;\n}\n')
} }
}) })
it('should strip line numbers with leading spaces', () => { it('should strip line numbers with leading spaces', async () => {
const originalContent = 'function test() {\n return true;\n}\n' const originalContent = 'function test() {\n return true;\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -662,14 +662,14 @@ function five() {
3 | } 3 | }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('function test() {\n return false;\n}\n') expect(result.content).toBe('function test() {\n return false;\n}\n')
} }
}) })
it('should not strip when not all lines have numbers in either section', () => { it('should not strip when not all lines have numbers in either section', async () => {
const originalContent = 'function test() {\n return true;\n}\n' const originalContent = 'function test() {\n return true;\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -682,11 +682,11 @@ function five() {
3 | } 3 | }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(false) expect(result.success).toBe(false)
}) })
it('should preserve content that naturally starts with pipe', () => { it('should preserve content that naturally starts with pipe', async () => {
const originalContent = '|header|another|\n|---|---|\n|data|more|\n' const originalContent = '|header|another|\n|---|---|\n|data|more|\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -699,14 +699,14 @@ function five() {
3 | |data|updated| 3 | |data|updated|
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('|header|another|\n|---|---|\n|data|updated|\n') expect(result.content).toBe('|header|another|\n|---|---|\n|data|updated|\n')
} }
}) })
it('should preserve indentation when stripping line numbers', () => { it('should preserve indentation when stripping line numbers', async () => {
const originalContent = ' function test() {\n return true;\n }\n' const originalContent = ' function test() {\n return true;\n }\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -719,14 +719,14 @@ function five() {
3 | } 3 | }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(' function test() {\n return false;\n }\n') expect(result.content).toBe(' function test() {\n return false;\n }\n')
} }
}) })
it('should handle different line numbers between sections', () => { it('should handle different line numbers between sections', async () => {
const originalContent = 'function test() {\n return true;\n}\n' const originalContent = 'function test() {\n return true;\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -739,14 +739,14 @@ function five() {
22 | } 22 | }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('function test() {\n return false;\n}\n') expect(result.content).toBe('function test() {\n return false;\n}\n')
} }
}) })
it('should not strip content that starts with pipe but no line number', () => { it('should not strip content that starts with pipe but no line number', async () => {
const originalContent = '| Pipe\n|---|\n| Data\n' const originalContent = '| Pipe\n|---|\n| Data\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -759,14 +759,14 @@ function five() {
| Updated | Updated
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('| Pipe\n|---|\n| Updated\n') expect(result.content).toBe('| Pipe\n|---|\n| Updated\n')
} }
}) })
it('should handle mix of line-numbered and pipe-only content', () => { it('should handle mix of line-numbered and pipe-only content', async () => {
const originalContent = '| Pipe\n|---|\n| Data\n' const originalContent = '| Pipe\n|---|\n| Data\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -779,7 +779,7 @@ function five() {
3 | | NewData 3 | | NewData
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('1 | | Pipe\n2 | |---|\n3 | | NewData\n') expect(result.content).toBe('1 | | Pipe\n2 | |---|\n3 | | NewData\n')
@@ -788,15 +788,15 @@ function five() {
}) })
}); });
describe('insertion/deletion', () => { describe('insertion/deletion', async () => {
let strategy: SearchReplaceDiffStrategy let strategy: SearchReplaceDiffStrategy
beforeEach(() => { beforeEach(() => {
strategy = new SearchReplaceDiffStrategy() strategy = new SearchReplaceDiffStrategy()
}) })
describe('deletion', () => { describe('deletion', async () => {
it('should delete code when replace block is empty', () => { it('should delete code when replace block is empty', async () => {
const originalContent = `function test() { const originalContent = `function test() {
console.log("hello"); console.log("hello");
// Comment to remove // Comment to remove
@@ -808,7 +808,7 @@ function five() {
======= =======
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function test() { expect(result.content).toBe(`function test() {
@@ -818,7 +818,7 @@ function five() {
} }
}) })
it('should delete multiple lines when replace block is empty', () => { it('should delete multiple lines when replace block is empty', async () => {
const originalContent = `class Example { const originalContent = `class Example {
constructor() { constructor() {
// Initialize // Initialize
@@ -838,7 +838,7 @@ function five() {
======= =======
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`class Example { expect(result.content).toBe(`class Example {
@@ -848,7 +848,7 @@ function five() {
} }
}) })
it('should preserve indentation when deleting nested code', () => { it('should preserve indentation when deleting nested code', async () => {
const originalContent = `function outer() { const originalContent = `function outer() {
if (true) { if (true) {
// Remove this // Remove this
@@ -865,7 +865,7 @@ function five() {
======= =======
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function outer() { expect(result.content).toBe(`function outer() {
@@ -877,8 +877,8 @@ function five() {
}) })
}) })
describe('insertion', () => { describe('insertion', async () => {
it('should insert code at specified line when search block is empty', () => { it('should insert code at specified line when search block is empty', async () => {
const originalContent = `function test() { const originalContent = `function test() {
const x = 1; const x = 1;
return x; return x;
@@ -889,7 +889,7 @@ function five() {
console.log("Adding log"); console.log("Adding log");
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 2, 2) const result = await strategy.applyDiff(originalContent, diffContent, 2, 2)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function test() { expect(result.content).toBe(`function test() {
@@ -900,7 +900,7 @@ function five() {
} }
}) })
it('should preserve indentation when inserting at nested location', () => { it('should preserve indentation when inserting at nested location', async () => {
const originalContent = `function test() { const originalContent = `function test() {
if (true) { if (true) {
const x = 1; const x = 1;
@@ -913,7 +913,7 @@ function five() {
console.log("After"); console.log("After");
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 3, 3) const result = await strategy.applyDiff(originalContent, diffContent, 3, 3)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function test() { expect(result.content).toBe(`function test() {
@@ -926,7 +926,7 @@ function five() {
} }
}) })
it('should handle insertion at start of file', () => { it('should handle insertion at start of file', async () => {
const originalContent = `function test() { const originalContent = `function test() {
return true; return true;
}` }`
@@ -938,7 +938,7 @@ function five() {
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 1, 1) const result = await strategy.applyDiff(originalContent, diffContent, 1, 1)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`// Copyright 2024 expect(result.content).toBe(`// Copyright 2024
@@ -950,7 +950,7 @@ function test() {
} }
}) })
it('should handle insertion at end of file', () => { it('should handle insertion at end of file', async () => {
const originalContent = `function test() { const originalContent = `function test() {
return true; return true;
}` }`
@@ -961,7 +961,7 @@ function test() {
// End of file // End of file
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 4, 4) const result = await strategy.applyDiff(originalContent, diffContent, 4, 4)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function test() { expect(result.content).toBe(`function test() {
@@ -972,7 +972,7 @@ function test() {
} }
}) })
it('should error if no start_line is provided for insertion', () => { it('should error if no start_line is provided for insertion', async () => {
const originalContent = `function test() { const originalContent = `function test() {
return true; return true;
}` }`
@@ -982,19 +982,19 @@ function test() {
console.log("test"); console.log("test");
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(false) expect(result.success).toBe(false)
}) })
}) })
}) })
describe('fuzzy matching', () => { describe('fuzzy matching', async () => {
let strategy: SearchReplaceDiffStrategy let strategy: SearchReplaceDiffStrategy
beforeEach(() => { beforeEach(() => {
strategy = new SearchReplaceDiffStrategy(0.9, 5) // 90% similarity threshold, 5 line buffer for tests strategy = new SearchReplaceDiffStrategy(0.9, 5) // 90% similarity threshold, 5 line buffer for tests
}) })
it('should match content with small differences (>90% similar)', () => { it('should match content with small differences (>90% similar)', async () => {
const originalContent = 'function getData() {\n const results = fetchData();\n return results.filter(Boolean);\n}\n' const originalContent = 'function getData() {\n const results = fetchData();\n return results.filter(Boolean);\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -1011,14 +1011,14 @@ function getData() {
strategy = new SearchReplaceDiffStrategy(0.9, 5) // Use 5 line buffer for tests strategy = new SearchReplaceDiffStrategy(0.9, 5) // Use 5 line buffer for tests
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('function getData() {\n const data = fetchData();\n return data.filter(Boolean);\n}\n') expect(result.content).toBe('function getData() {\n const data = fetchData();\n return data.filter(Boolean);\n}\n')
} }
}) })
it('should not match when content is too different (<90% similar)', () => { it('should not match when content is too different (<90% similar)', async () => {
const originalContent = 'function processUsers(data) {\n return data.map(user => user.name);\n}\n' const originalContent = 'function processUsers(data) {\n return data.map(user => user.name);\n}\n'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -1031,11 +1031,11 @@ function processData(data) {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(false) expect(result.success).toBe(false)
}) })
it('should match content with extra whitespace', () => { it('should match content with extra whitespace', async () => {
const originalContent = 'function sum(a, b) {\n return a + b;\n}' const originalContent = 'function sum(a, b) {\n return a + b;\n}'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -1048,14 +1048,14 @@ function sum(a, b) {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('function sum(a, b) {\n return a + b + 1;\n}') expect(result.content).toBe('function sum(a, b) {\n return a + b + 1;\n}')
} }
}) })
it('should not exact match empty lines', () => { it('should not exact match empty lines', async () => {
const originalContent = 'function sum(a, b) {\n\n return a + b;\n}' const originalContent = 'function sum(a, b) {\n\n return a + b;\n}'
const diffContent = `test.ts const diffContent = `test.ts
<<<<<<< SEARCH <<<<<<< SEARCH
@@ -1065,7 +1065,7 @@ import { a } from "a";
function sum(a, b) { function sum(a, b) {
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe('import { a } from "a";\nfunction sum(a, b) {\n\n return a + b;\n}') expect(result.content).toBe('import { a } from "a";\nfunction sum(a, b) {\n\n return a + b;\n}')
@@ -1073,14 +1073,14 @@ function sum(a, b) {
}) })
}) })
describe('line-constrained search', () => { describe('line-constrained search', async () => {
let strategy: SearchReplaceDiffStrategy let strategy: SearchReplaceDiffStrategy
beforeEach(() => { beforeEach(() => {
strategy = new SearchReplaceDiffStrategy(0.9, 5) strategy = new SearchReplaceDiffStrategy(0.9, 5)
}) })
it('should find and replace within specified line range', () => { it('should find and replace within specified line range', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1105,7 +1105,7 @@ function two() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 5, 7) const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1122,7 +1122,7 @@ function three() {
} }
}) })
it('should find and replace within buffer zone (5 lines before/after)', () => { it('should find and replace within buffer zone (5 lines before/after)', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1149,7 +1149,7 @@ function three() {
// Even though we specify lines 5-7, it should still find the match at lines 9-11 // Even though we specify lines 5-7, it should still find the match at lines 9-11
// because it's within the 5-line buffer zone // because it's within the 5-line buffer zone
const result = strategy.applyDiff(originalContent, diffContent, 5, 7) const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1166,7 +1166,7 @@ function three() {
} }
}) })
it('should not find matches outside search range and buffer zone', () => { it('should not find matches outside search range and buffer zone', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1201,11 +1201,11 @@ function five() {
// Searching around function two() (lines 5-7) // Searching around function two() (lines 5-7)
// function five() is more than 5 lines away, so it shouldn't match // function five() is more than 5 lines away, so it shouldn't match
const result = strategy.applyDiff(originalContent, diffContent, 5, 7) const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
expect(result.success).toBe(false) expect(result.success).toBe(false)
}) })
it('should handle search range at start of file', () => { it('should handle search range at start of file', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1226,7 +1226,7 @@ function one() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 1, 3) const result = await strategy.applyDiff(originalContent, diffContent, 1, 3)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1239,7 +1239,7 @@ function two() {
} }
}) })
it('should handle search range at end of file', () => { it('should handle search range at end of file', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1260,7 +1260,7 @@ function two() {
} }
>>>>>>> REPLACE` >>>>>>> REPLACE`
const result = strategy.applyDiff(originalContent, diffContent, 5, 7) const result = await strategy.applyDiff(originalContent, diffContent, 5, 7)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1273,7 +1273,7 @@ function two() {
} }
}) })
it('should match specific instance of duplicate code using line numbers', () => { it('should match specific instance of duplicate code using line numbers', async () => {
const originalContent = ` const originalContent = `
function processData(data) { function processData(data) {
return data.map(x => x * 2); return data.map(x => x * 2);
@@ -1306,7 +1306,7 @@ function processData(data) {
>>>>>>> REPLACE` >>>>>>> REPLACE`
// Target the second instance of processData // Target the second instance of processData
const result = strategy.applyDiff(originalContent, diffContent, 10, 12) const result = await strategy.applyDiff(originalContent, diffContent, 10, 12)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function processData(data) { expect(result.content).toBe(`function processData(data) {
@@ -1330,7 +1330,7 @@ function moreStuff() {
} }
}) })
it('should search from start line to end of file when only start_line is provided', () => { it('should search from start line to end of file when only start_line is provided', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1356,7 +1356,7 @@ function three() {
>>>>>>> REPLACE` >>>>>>> REPLACE`
// Only provide start_line, should search from there to end of file // Only provide start_line, should search from there to end of file
const result = strategy.applyDiff(originalContent, diffContent, 8) const result = await strategy.applyDiff(originalContent, diffContent, 8)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1373,7 +1373,7 @@ function three() {
} }
}) })
it('should search from start of file to end line when only end_line is provided', () => { it('should search from start of file to end line when only end_line is provided', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1399,7 +1399,7 @@ function one() {
>>>>>>> REPLACE` >>>>>>> REPLACE`
// Only provide end_line, should search from start of file to there // Only provide end_line, should search from start of file to there
const result = strategy.applyDiff(originalContent, diffContent, undefined, 4) const result = await strategy.applyDiff(originalContent, diffContent, undefined, 4)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1416,7 +1416,7 @@ function three() {
} }
}) })
it('should prioritize exact line match over expanded search', () => { it('should prioritize exact line match over expanded search', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1446,7 +1446,7 @@ function process() {
// Should match the second instance exactly at lines 10-12 // Should match the second instance exactly at lines 10-12
// even though the first instance at 6-8 is within the expanded search range // even though the first instance at 6-8 is within the expanded search range
const result = strategy.applyDiff(originalContent, diffContent, 10, 12) const result = await strategy.applyDiff(originalContent, diffContent, 10, 12)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(` expect(result.content).toBe(`
@@ -1468,7 +1468,7 @@ function two() {
} }
}) })
it('should fall back to expanded search only if exact match fails', () => { it('should fall back to expanded search only if exact match fails', async () => {
const originalContent = ` const originalContent = `
function one() { function one() {
return 1; return 1;
@@ -1494,7 +1494,7 @@ function process() {
// Specify wrong line numbers (3-5), but content exists at 6-8 // Specify wrong line numbers (3-5), but content exists at 6-8
// Should still find and replace it since it's within the expanded range // Should still find and replace it since it's within the expanded range
const result = strategy.applyDiff(originalContent, diffContent, 3, 5) const result = await strategy.applyDiff(originalContent, diffContent, 3, 5)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(`function one() { expect(result.content).toBe(`function one() {
@@ -1512,21 +1512,21 @@ function two() {
}) })
}) })
describe('getToolDescription', () => { describe('getToolDescription', async () => {
let strategy: SearchReplaceDiffStrategy let strategy: SearchReplaceDiffStrategy
beforeEach(() => { beforeEach(() => {
strategy = new SearchReplaceDiffStrategy() strategy = new SearchReplaceDiffStrategy()
}) })
it('should include the current working directory', () => { it('should include the current working directory', async () => {
const cwd = '/test/dir' const cwd = '/test/dir'
const description = strategy.getToolDescription(cwd) const description = await strategy.getToolDescription(cwd)
expect(description).toContain(`relative to the current working directory ${cwd}`) expect(description).toContain(`relative to the current working directory ${cwd}`)
}) })
it('should include required format elements', () => { it('should include required format elements', async () => {
const description = strategy.getToolDescription('/test') const description = await strategy.getToolDescription('/test')
expect(description).toContain('<<<<<<< SEARCH') expect(description).toContain('<<<<<<< SEARCH')
expect(description).toContain('=======') expect(description).toContain('=======')
expect(description).toContain('>>>>>>> REPLACE') expect(description).toContain('>>>>>>> REPLACE')
@@ -1534,8 +1534,8 @@ function two() {
expect(description).toContain('</apply_diff>') expect(description).toContain('</apply_diff>')
}) })
it('should document start_line and end_line parameters', () => { it('should document start_line and end_line parameters', async () => {
const description = strategy.getToolDescription('/test') const description = await strategy.getToolDescription('/test')
expect(description).toContain('start_line: (required) The line number where the search block starts.') expect(description).toContain('start_line: (required) The line number where the search block starts.')
expect(description).toContain('end_line: (required) The line number where the search block ends.') expect(description).toContain('end_line: (required) The line number where the search block ends.')
}) })

View File

@@ -20,7 +20,7 @@ describe('UnifiedDiffStrategy', () => {
}) })
describe('applyDiff', () => { describe('applyDiff', () => {
it('should successfully apply a function modification diff', () => { it('should successfully apply a function modification diff', async () => {
const originalContent = `import { Logger } from '../logger'; const originalContent = `import { Logger } from '../logger';
function calculateTotal(items: number[]): number { function calculateTotal(items: number[]): number {
@@ -58,14 +58,14 @@ function calculateTotal(items: number[]): number {
export { calculateTotal };` export { calculateTotal };`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(expected) expect(result.content).toBe(expected)
} }
}) })
it('should successfully apply a diff adding a new method', () => { it('should successfully apply a diff adding a new method', async () => {
const originalContent = `class Calculator { const originalContent = `class Calculator {
add(a: number, b: number): number { add(a: number, b: number): number {
return a + b; return a + b;
@@ -95,14 +95,14 @@ export { calculateTotal };`
} }
}` }`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(expected) expect(result.content).toBe(expected)
} }
}) })
it('should successfully apply a diff modifying imports', () => { it('should successfully apply a diff modifying imports', async () => {
const originalContent = `import { useState } from 'react'; const originalContent = `import { useState } from 'react';
import { Button } from './components'; import { Button } from './components';
@@ -133,14 +133,14 @@ function App() {
return <Button onClick={() => setCount(count + 1)}>{count}</Button>; return <Button onClick={() => setCount(count + 1)}>{count}</Button>;
}` }`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(expected) expect(result.content).toBe(expected)
} }
}) })
it('should successfully apply a diff with multiple hunks', () => { it('should successfully apply a diff with multiple hunks', async () => {
const originalContent = `import { readFile, writeFile } from 'fs'; const originalContent = `import { readFile, writeFile } from 'fs';
function processFile(path: string) { function processFile(path: string) {
@@ -198,14 +198,14 @@ async function processFile(path: string) {
export { processFile };` export { processFile };`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(expected) expect(result.content).toBe(expected)
} }
}) })
it('should handle empty original content', () => { it('should handle empty original content', async () => {
const originalContent = '' const originalContent = ''
const diffContent = `--- empty.ts const diffContent = `--- empty.ts
+++ empty.ts +++ empty.ts
@@ -218,7 +218,7 @@ export { processFile };`
return \`Hello, \${name}!\`; return \`Hello, \${name}!\`;
}\n` }\n`
const result = strategy.applyDiff(originalContent, diffContent) const result = await strategy.applyDiff(originalContent, diffContent)
expect(result.success).toBe(true) expect(result.success).toBe(true)
if (result.success) { if (result.success) {
expect(result.content).toBe(expected) expect(result.content).toBe(expected)