PRIX / src /__tests__ /patch-utils.test.ts
Rachit-Tw's picture
Upload 169 files
9284ad7 verified
Raw
History Blame Contribute Delete
9.33 kB
import { jest, expect, describe, it } from '@jest/globals'
import {
splitPatch,
patchStartEndLine,
parsePatch,
checkIfDocumentationOnly,
parseReview,
extractPatternType,
HunkInfo,
} from '../utils/patch-utils'
describe('patch-utils', () => {
describe('splitPatch', () => {
it('should split patch with proper @@ headers into hunks', () => {
const patch = `@@ -10,5 +10,6 @@
line1
line2
-old line
+new line
+added line
@@ -20,3 +20,4 @@
context
-modified
+added
another`
const result = splitPatch(patch)
expect(result).toHaveLength(2)
expect(result[0]).toContain('line1')
expect(result[1]).toContain('context')
})
it('should return entire patch when no @@ headers found (new files)', () => {
const newFilePatch = `+function updateSettings(user, newTheme) {
+ const updatedUser = user;
+ updatedUser.settings.theme = newTheme;
+ return updatedUser;
+}`
const result = splitPatch(newFilePatch)
expect(result).toHaveLength(1)
expect(result[0]).toBe(newFilePatch)
})
it('should return empty array for null/undefined patch', () => {
expect(splitPatch(null)).toEqual([])
expect(splitPatch(undefined)).toEqual([])
})
it('should handle single hunk patch', () => {
const patch = `@@ -1,3 +1,4 @@
const x = 1;
-old
+new
+added`
const result = splitPatch(patch)
expect(result).toHaveLength(1)
})
it('should handle patch with only context lines', () => {
const patch = `@@ -5,3 +5,3 @@
return result;
}
-remove
+add`
const result = splitPatch(patch)
expect(result).toHaveLength(1)
})
})
describe('patchStartEndLine', () => {
it('should parse standard @@ header correctly', () => {
const patch = `@@ -10,5 +10,6 @@
content`
const result = patchStartEndLine(patch)
expect(result).toEqual({
oldStart: 10,
oldLength: 5,
newStart: 10,
newLength: 6,
oldHunk: { startLine: 10, endLine: 14 },
newHunk: { startLine: 10, endLine: 15 },
})
})
it('should handle new file without @@ header (all + lines)', () => {
const newFilePatch = `+function test() {
+ return true;
+}`
const result = patchStartEndLine(newFilePatch)
expect(result).not.toBeNull()
expect(result?.newStart).toBe(1)
expect(result?.newLength).toBe(3)
expect(result?.oldStart).toBe(0)
expect(result?.oldLength).toBe(0)
expect(result?.oldHunk.startLine).toBe(0)
expect(result?.oldHunk.endLine).toBe(0)
expect(result?.newHunk.startLine).toBe(1)
expect(result?.newHunk.endLine).toBe(3)
})
it('should handle single line new file', () => {
const singleLinePatch = `+const x = 1;`
const result = patchStartEndLine(singleLinePatch)
expect(result?.newLength).toBe(1)
expect(result?.newHunk.endLine).toBe(1)
})
it('should return null for patch with no + lines and no header', () => {
const invalidPatch = ` context line
another line`
const result = patchStartEndLine(invalidPatch)
expect(result).toBeNull()
})
it('should handle diff with whitespace before +', () => {
const patch = `@@ -1,2 +1,3 @@
line1
+added line`
const result = patchStartEndLine(patch)
expect(result?.newLength).toBe(3)
expect(result?.oldLength).toBe(2)
})
})
describe('parsePatch', () => {
it('should parse standard diff correctly', () => {
const patch = `@@ -1,4 +1,5 @@
const x = 1;
-old line
+new line
+added line
context line`
const result = parsePatch(patch)
expect(result).not.toBeNull()
expect(result?.oldStart).toBe(1)
expect(result?.newStart).toBe(1)
expect(result?.oldHunk).toContain('old line')
expect(result?.newHunk).toContain('new line')
expect(result?.newHunk).toContain('added line')
})
it('should handle new file (no old lines)', () => {
const newFilePatch = `+function test() {
+ return 1;
+}`
const result = parsePatch(newFilePatch)
expect(result).not.toBeNull()
expect(result?.oldHunk).toBe('')
expect(result?.newHunk).toContain('function test()')
expect(result?.newStart).toBe(1)
})
it('should handle deleted file (no new lines)', () => {
const deletedPatch = `@@ -1,3 +1,0 @@
-old line
-old line 2
-old line 3`
const result = parsePatch(deletedPatch)
expect(result).not.toBeNull()
expect(result?.oldHunk).toContain('old line')
expect(result?.newHunk).toBe('')
})
it('should return null for invalid patch without header and no + lines', () => {
const invalidPatch = `just some text without diff markers`
const result = parsePatch(invalidPatch)
expect(result).toBeNull()
})
})
describe('checkIfDocumentationOnly', () => {
it('should return true for pure comment marker changes (no text content)', () => {
const docOnlyDiff = `-//
+////`
expect(checkIfDocumentationOnly(docOnlyDiff)).toBe(true)
})
it('should return false for comment text changes', () => {
const codeDiff = `-// Old comment
+// New comment`
expect(checkIfDocumentationOnly(codeDiff)).toBe(false)
})
it('should return false for code changes', () => {
const codeDiff = `-const x = 1;
+const x = 2;`
expect(checkIfDocumentationOnly(codeDiff)).toBe(false)
})
it('should return false for Python docstrings', () => {
const pythonDocDiff = `-"""Old doc"""
+"""New doc"""`
expect(checkIfDocumentationOnly(pythonDocDiff)).toBe(false)
})
})
describe('parseReview', () => {
it('should parse review with line numbers', () => {
const response = `[10] | major | bug: Missing null check
Null pointer risk here`
const patches = [
{
newStart: 1,
newLength: 20,
},
]
const result = parseReview(response, patches, 'test.js')
expect(result).toHaveLength(1)
expect(result[0].startLine).toBe(10)
expect(result[0].endLine).toBe(10)
expect(result[0].severity).toBe('major')
})
it('should parse review with line range', () => {
const response = `[10-15] | critical | security: SQL injection risk`
const patches = [
{
newStart: 1,
newLength: 20,
},
]
const result = parseReview(response, patches, 'test.js')
expect(result).toHaveLength(1)
expect(result[0].startLine).toBe(10)
expect(result[0].endLine).toBe(15)
expect(result[0].severity).toBe('critical')
})
it('should skip reviews outside valid line range', () => {
const response = `[100] | major | bug: Out of range`
const patches = [
{
newStart: 1,
newLength: 20,
},
]
const result = parseReview(response, patches, 'test.js')
expect(result).toHaveLength(0)
})
it('should extract remedy from code blocks', () => {
const response = `[10] | major | bug: Fix this
\`\`\`
const fixed = value || defaultValue;
\`\`\``
const patches = [
{
newStart: 1,
newLength: 20,
},
]
const result = parseReview(response, patches, 'test.js')
expect(result).toHaveLength(1)
expect(result[0].remedy).toContain('fixed')
})
})
describe('extractPatternType', () => {
it('should extract Security pattern', () => {
expect(extractPatternType('Security issue found')).toBe('security')
})
it('should extract Bug pattern', () => {
expect(extractPatternType('Bug in loop')).toBe('bug')
})
it('should extract Performance pattern', () => {
expect(extractPatternType('Performance bottleneck detected')).toBe('performance')
})
it('should return general for unknown patterns', () => {
expect(extractPatternType('Some random message')).toBe('general')
})
})
describe('integration: real-world patch scenarios', () => {
it('should handle the actual e.js patch from PR #52', () => {
const actualPatch = `+function updateSettings(user, newTheme) {
+
+ const updatedUser = user;
+
+ updatedUser.settings.theme = newTheme;
+
+ return updatedUser;
+}
+
+
+const account = { id: 1, settings: { theme: 'dark' } };
+const newAccount = updateSettings(account, 'light');
+console.log(account.settings.theme);`
const split = splitPatch(actualPatch)
expect(split).toHaveLength(1)
const hunkInfo = patchStartEndLine(split[0])
expect(hunkInfo).not.toBeNull()
expect(hunkInfo?.newLength).toBe(13)
const parsed = parsePatch(split[0])
expect(parsed).not.toBeNull()
expect(parsed?.newHunk).toContain('updateSettings')
})
it('should handle multi-file diff with multiple hunks', () => {
const multiFileDiff = `diff --git a/calc.py b/calc.py
@@ -1,5 +1,6 @@
if __name__ == "__main__":
print(f"Addition: {add(10, 5)}")
- print(f"Division: {divide(10, 5)}")
+ print(f"Division: {divide(10, 5)}")
+ ded
\\ No newline at end of file`
const split = splitPatch(multiFileDiff)
expect(split.length).toBeGreaterThanOrEqual(1)
const hunkInfo = patchStartEndLine(split[0])
expect(hunkInfo).not.toBeNull()
})
})
})