Advertisement

← vibesdk

worker/agents/output-formats/diff-formats/udiff.test.ts

7.3 KB Download file
import { describe, it, expect } from 'vitest';
import { applyDiff } from './udiff';

describe('applyUnifiedDiff', () => {
  const applyUnifiedDiff = applyDiff; // Alias for compatibility
  it('should apply a simple addition', () => {
    const original = `line 1
line 2
line 3`;

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,4 @@
 line 1
 line 2
+inserted line
 line 3`;

    const expected = `line 1
line 2
inserted line
line 3`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should apply a simple deletion', () => {
    const original = `line 1
line 2
line 3
line 4`;

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -1,4 +1,3 @@
 line 1
-line 2
 line 3
 line 4`;

    const expected = `line 1
line 3
line 4`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should apply a modification', () => {
    const original = `function hello() {
  console.log('Hello');
}`;

    const diff = `--- a/file.js
+++ b/file.js
@@ -1,3 +1,3 @@
 function hello() {
-  console.log('Hello');
+  console.log('Hello World');
 }`;

    const expected = `function hello() {
  console.log('Hello World');
}`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should apply multiple hunks', () => {
    const original = `function a() {
  return 1;
}

function b() {
  return 2;
}

function c() {
  return 3;
}`;

    const diff = `--- a/file.js
+++ b/file.js
@@ -1,3 +1,3 @@
 function a() {
-  return 1;
+  return 10;
 }
@@ -7,3 +7,3 @@
 
 function c() {
-  return 3;
+  return 30;
 }`;

    const expected = `function a() {
  return 10;
}

function b() {
  return 2;
}

function c() {
  return 30;
}`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it.skip('should handle additions at the beginning', () => {
    const original = `first line
second line`;

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -0,0 +1,2 @@
+new first line
+new second line
@@ -1,2 +3,2 @@
 first line
 second line`;

    const expected = `new first line
new second line
first line
second line`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should handle additions at the end', () => {
    const original = `first line
second line`;

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -1,2 +1,4 @@
 first line
 second line
+third line
+fourth line`;

    const expected = `first line
second line
third line
fourth line`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should handle empty original file', () => {
    const original = '';

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -0,0 +1,3 @@
+line 1
+line 2
+line 3`;

    // Note: Implementation adds a leading newline for empty files
    const expected = `
line 1
line 2
line 3`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should handle complete file replacement', () => {
    const original = `old content
that will be
completely replaced`;

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,2 @@
-old content
-that will be
-completely replaced
+brand new content
+with different structure`;

    const expected = `brand new content
with different structure`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should preserve whitespace and indentation', () => {
    const original = `def function():
    if True:
        print("indented")
    return None`;

    const diff = `--- a/file.py
+++ b/file.py
@@ -1,4 +1,5 @@
 def function():
     if True:
         print("indented")
+        print("another indented line")
     return None`;

    const expected = `def function():
    if True:
        print("indented")
        print("another indented line")
    return None`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should handle Windows line endings', () => {
    const original = 'line 1\r\nline 2\r\nline 3';

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -1,3 +1,3 @@
 line 1
-line 2
+modified line 2
 line 3`;

    const expected = 'line 1\r\nmodified line 2\r\nline 3';

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should handle context lines correctly', () => {
    const original = `a
b
c
d
e
f
g`;

    const diff = `--- a/file.txt
+++ b/file.txt
@@ -2,5 +2,5 @@ a
 b
 c
-d
+D modified
 e
 f`;

    const expected = `a
b
c
D modified
e
f
g`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('should throw error for invalid diff format', () => {
    const original = 'some content';
    const invalidDiff = 'this is not a valid diff';

    // Resilient parser returns original content for invalid diffs
    // Note: Implementation adds a trailing newline (standard file convention)
    const result = applyUnifiedDiff(original, invalidDiff);
    expect(result).toBe(original + '\n');
  });

  it('should handle diffs with no changes', () => {
    const original = `unchanged content
stays the same`;

    const diff = `--- a/file.txt
+++ b/file.txt`;

    // Note: Implementation adds a trailing newline
    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(original + '\n');
  });

  it('should apply complex real-world diff', () => {
    const original = `export class MyClass {
  constructor() {
    this.value = 0;
  }

  increment() {
    this.value++;
  }

  getValue() {
    return this.value;
  }
}`;

    const diff = `--- a/myclass.js
+++ b/myclass.js
@@ -1,8 +1,12 @@
 export class MyClass {
-  constructor() {
-    this.value = 0;
+  constructor(initialValue = 0) {
+    this.value = initialValue;
+    this.history = [initialValue];
   }
 
   increment() {
     this.value++;
+    this.history.push(this.value);
   }
 
   getValue() {
@@ -10,3 +14,7 @@ export class MyClass {
     return this.value;
   }
+
+  getHistory() {
+    return [...this.history];
+  }
 }`;

    const expected = `export class MyClass {
  constructor(initialValue = 0) {
    this.value = initialValue;
    this.history = [initialValue];
  }

  increment() {
    this.value++;
    this.history.push(this.value);
  }

  getValue() {
    return this.value;
  }

  getHistory() {
    return [...this.history];
  }
}`;

    const result = applyUnifiedDiff(original, diff);
    expect(result).toBe(expected);
  });

  it('inserts $-sequences in replacement content literally ($&)', () => {
    const original = `const a = 1;\nconst label = old;\nconst b = 2;`;
    const diff = `@@ ... @@\n const a = 1;\n-const label = old;\n+const label = text.replace(/x/g, '[$&]');\n const b = 2;`;
    const expected = `const a = 1;\nconst label = text.replace(/x/g, '[$&]');\nconst b = 2;`;
    expect(applyUnifiedDiff(original, diff)).toBe(expected);
  });

  it('does not collapse $$ in replacement content', () => {
    const original = `line1\nTODO\nline3`;
    const diff = `@@ ... @@\n line1\n-TODO\n+const price = "$$5";\n line3`;
    const expected = `line1\nconst price = "$$5";\nline3`;
    expect(applyUnifiedDiff(original, diff)).toBe(expected);
  });
});