import { describe, it, expect } from 'vitest'; import { applyDiff } from './udiff'; describe('Unified Diff - Comprehensive LLM Resilience Tests', () => { describe('Malformed diff headers', () => { it('should handle missing --- header', () => { const original = 'line1\nline2\nline3'; const diff = `+++ b/file.txt @@ -1,3 +1,3 @@ line1 -line2 +modified line3`; // The diff parser is resilient and can still apply the diff const result = applyDiff(original, diff); expect(result).toBe('line1\nmodified\nline3'); }); it('should handle missing +++ header', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt @@ -1,3 +1,3 @@ line1 -line2 +modified line3`; // The diff parser is resilient and can still apply the diff const result = applyDiff(original, diff); expect(result).toBe('line1\nmodified\nline3'); }); it('should handle malformed @@ header', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ garbage @@ line1 -line2 +modified line3`; // The diff parser is resilient and can still apply the diff const result = applyDiff(original, diff); expect(result).toBe('line1\nmodified\nline3'); }); it('should handle @@ header with wrong numbers', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,5 +1,3 @@ line1 -line2 +modified line3`; // Should either handle gracefully or throw consistently try { const result = applyDiff(original, diff); // If it doesn't throw, check result is reasonable expect(result).toContain('line1'); } catch (e) { // If it throws, that's also acceptable expect(e).toBeDefined(); } }); }); describe('LLM-specific formatting issues', () => { it('should handle extra spaces after diff markers', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ line1 - line2 + modified line3`; const result = applyDiff(original, diff); // Note: The implementation currently strips trailing spaces from the prefix expect(result).toBe('line1\n modified\nline3'); }); it('should handle tabs mixed with spaces', () => { const original = 'function test() {\n return true;\n}'; const diff = `--- a/file.js +++ b/file.js @@ -1,3 +1,3 @@ function test() { - return true; + return false; }`; const result = applyDiff(original, diff); expect(result).toBe('function test() {\n\treturn false;\n}'); }); it('should handle missing newline at end of file', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ line1 line2 -line3 +line3 modified \\ No newline at end of file`; const result = applyDiff(original, diff); expect(result).toBe('line1\nline2\nline3 modified'); }); it('should handle Windows CRLF in diff', () => { const original = 'line1\r\nline2\r\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ line1\r -line2\r +modified\r line3`; const result = applyDiff(original, diff); expect(result).toBe('line1\r\nmodified\r\nline3'); }); it('should handle Mac CR line endings', () => { const original = 'line1\rline2\rline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ line1 -line2 +modified line3`; const result = applyDiff(original, diff); expect(result.split(/\r|\n/).filter(l => l)).toContain('modified'); }); }); describe('Edge cases with line numbers', () => { it('should handle @@ -0,0 +1,3 @@ (adding to empty file)', () => { const original = ''; const diff = `--- a/file.txt +++ b/file.txt @@ -0,0 +1,3 @@ +line1 +line2 +line3`; const result = applyDiff(original, diff); // Note: Implementation adds an extra newline when starting from empty file expect(result).toBe('\nline1\nline2\nline3'); }); it('should handle @@ -1,3 +0,0 @@ (deleting entire file)', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +0,0 @@ -line1 -line2 -line3`; // Should throw an error for safety when trying to delete entire file expect(() => applyDiff(original, diff)).toThrow('Hunk #1 would delete entire file content - aborting for safety'); }); it('should handle multiple hunks with context overlap', () => { const original = 'a\nb\nc\nd\ne\nf\ng\nh\ni\nj'; const diff = `--- a/file.txt +++ b/file.txt @@ -2,4 +2,4 @@ a b c -d +D e @@ -6,4 +6,4 @@ e f g -h +H i`; const result = applyDiff(original, diff); expect(result).toBe('a\nb\nc\nD\ne\nf\ng\nH\ni\nj'); }); it('should handle hunks that are out of order', () => { const original = 'line1\nline2\nline3\nline4\nline5'; const diff = `--- a/file.txt +++ b/file.txt @@ -4,2 +4,2 @@ line4 -line5 +line5 modified @@ -1,2 +1,2 @@ -line1 +line1 modified line2`; // Should either apply correctly or throw try { const result = applyDiff(original, diff); expect(result).toContain('modified'); } catch (e) { expect(e).toBeDefined(); } }); }); describe('Malicious or confusing content', () => { it('should handle diff markers in content', () => { const original = 'normal line\n--- this looks like a header\n+++ but its not\nnormal end'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,4 +1,4 @@ normal line --- this looks like a header +++ but its not -normal end +modified end`; const result = applyDiff(original, diff); expect(result).toBe('normal line\n--- this looks like a header\n+++ but its not\nmodified end'); }); it('should handle @@ in content', () => { const original = 'line1\n@@ fake header @@\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ line1 -@@ fake header @@ +@@ real content @@ line3`; // Fixed: regex now only matches @@ at line start, so content with @@ works correctly const result = applyDiff(original, diff); expect(result).toBe('line1\n@@ real content @@\nline3'); }); it('should handle backslash escapes', () => { const original = 'line with \\ backslash\nand \\\\ double'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,2 +1,2 @@ -line with \\ backslash +line with \\\\ more backslash and \\\\ double`; const result = applyDiff(original, diff); expect(result).toBe('line with \\\\ more backslash\nand \\\\ double'); }); }); describe('Unicode and encoding issues', () => { it('should handle emoji in diff', () => { const original = 'Hello 👋 World\nHow are you?'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,2 +1,2 @@ -Hello 👋 World +Hello 🌍 World How are you?`; const result = applyDiff(original, diff); expect(result).toBe('Hello 🌍 World\nHow are you?'); }); it('should handle multi-byte unicode', () => { const original = '日本語\n中文\n한국어'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ 日本語 -中文 +中文字符 한국어`; const result = applyDiff(original, diff); expect(result).toBe('日本語\n中文字符\n한국어'); }); it('should handle zero-width characters', () => { const original = 'visible\u200Btext'; // zero-width space const diff = `--- a/file.txt +++ b/file.txt @@ -1 +1 @@ -visible\u200Btext +visible\u200B\u200Btext`; // two zero-width spaces const result = applyDiff(original, diff); expect(result).toBe('visible\u200B\u200Btext'); }); }); describe('Performance and stress tests', () => { it('should handle very long lines', () => { const longLine = 'a'.repeat(10000); const original = `short\n${longLine}\nshort`; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ short -${longLine} +${'b'.repeat(10000)} short`; // Security check prevents lines over 10000 characters expect(() => applyDiff(original, diff)).toThrow('Diff contains excessively long lines'); }); it('should handle many small hunks', () => { const lines = Array.from({ length: 100 }, (_, i) => `line${i}`); const original = lines.join('\n'); // Create diff that changes every 10th line let diff = `--- a/file.txt\n+++ b/file.txt\n`; for (let i = 0; i < 100; i += 10) { diff += `@@ -${i + 1},3 +${i + 1},3 @@\n`; if (i > 0) diff += ` line${i - 1}\n`; diff += `-line${i}\n`; diff += `+line${i} modified\n`; if (i < 99) diff += ` line${i + 1}\n`; } const result = applyDiff(original, diff); expect(result.split('\n')[0]).toBe('line0 modified'); expect(result.split('\n')[10]).toBe('line10 modified'); }); it('should handle file with no trailing newline to adding one', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,4 @@ line1 line2 -line3 \\ No newline at end of file +line3 +`; const result = applyDiff(original, diff); expect(result).toBe('line1\nline2\nline3\n'); }); }); describe('Real-world LLM mistakes', () => { it('should handle extra context lines from confused LLM', () => { const original = 'a\nb\nc\nd\ne'; const diff = `--- a/file.txt +++ b/file.txt @@ -2,3 +2,3 @@ a b -c +C modified d e f`; // LLM added extra 'f' that doesn't exist try { const result = applyDiff(original, diff); expect(result).toContain('C modified'); } catch (e) { // Also acceptable to throw on invalid diff expect(e).toBeDefined(); } }); it('should handle missing context lines', () => { const original = 'a\nb\nc\nd\ne'; const diff = `--- a/file.txt +++ b/file.txt @@ -2,3 +2,3 @@ -c +C modified d`; // Missing 'b' context try { const result = applyDiff(original, diff); // If it works, check reasonable output expect(result.split('\n')).toContain('C modified'); } catch (e) { expect(e).toBeDefined(); } }); it('should handle LLM adding line numbers in content', () => { const original = 'function test() {\n return true;\n}'; const diff = `--- a/file.js +++ b/file.js @@ -1,3 +1,3 @@ function test() { - return true; + return false; // Line 2: changed return value }`; const result = applyDiff(original, diff); expect(result).toBe('function test() {\n return false; // Line 2: changed return value\n}'); }); it('should handle LLM word-wrapping long lines', () => { const original = 'short line\nvery long line that goes on and on and on and should not be wrapped\nshort line'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,4 @@ short line -very long line that goes on and on and on and should not be wrapped +very long line that goes on and on +and on and should not be wrapped short line`; const result = applyDiff(original, diff); expect(result).toBe('short line\nvery long line that goes on and on\nand on and should not be wrapped\nshort line'); }); it('should handle LLM mixing spaces and tabs', () => { const original = '{\n "indent": "spaces"\n}'; const diff = `--- a/file.json +++ b/file.json @@ -1,3 +1,3 @@ { - "indent": "spaces" + "indent": "tabs" }`; const result = applyDiff(original, diff); expect(result).toBe('{\n\t"indent": "tabs"\n}'); }); }); describe('Binary and special content', () => { it('should handle null bytes in content', () => { const original = 'before\0null\0after'; const diff = `--- a/file.txt +++ b/file.txt @@ -1 +1 @@ -before\0null\0after +before\0NULL\0after`; const result = applyDiff(original, diff); expect(result).toBe('before\0NULL\0after'); }); it('should handle various line ending combinations', () => { const original = 'unix\nwindows\r\nmac\rmixed\n\rweird'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,5 +1,5 @@ unix windows mac -mixed +MIXED weird`; const result = applyDiff(original, diff); expect(result).toContain('MIXED'); }); it('should handle control characters', () => { const original = 'normal\x1B[31mred\x1B[0m\nnext'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,2 +1,2 @@ -normal\x1B[31mred\x1B[0m +normal\x1B[32mgreen\x1B[0m next`; const result = applyDiff(original, diff); expect(result).toBe('normal\x1B[32mgreen\x1B[0m\nnext'); }); }); describe('Fuzzing-like tests', () => { it('should handle random garbage after headers', () => { const original = 'content'; const diff = `--- a/file.txt blah blah +++ b/file.txt more garbage !@#$% @@ -1 +1 @@ trailing stuff here too -content +modified`; try { const result = applyDiff(original, diff); expect(result).toBe('modified'); } catch (e) { // Also OK to reject garbage expect(e).toBeDefined(); } }); it('should handle incomplete diffs', () => { const original = 'line1\nline2\nline3'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,3 +1,3 @@ line1 -line2`; // Diff is cut off mid-hunk // The resilient parser applies what it can (LLM-friendly behavior) const result = applyDiff(original, diff); expect(result).toBe('line1\nline3'); // Applies the deletion }); it('should handle diffs with only additions', () => { const original = 'line1\nline2'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,2 +1,5 @@ line1 +added1 +added2 +added3 line2`; const result = applyDiff(original, diff); expect(result).toBe('line1\nadded1\nadded2\nadded3\nline2'); }); it('should handle diffs with only deletions', () => { const original = 'line1\ndelete1\ndelete2\nline2'; const diff = `--- a/file.txt +++ b/file.txt @@ -1,4 +1,2 @@ line1 -delete1 -delete2 line2`; const result = applyDiff(original, diff); expect(result).toBe('line1\nline2'); }); }); });