#!/usr/bin/env python3 """ Unit tests for diff_applier.py, covering various scenarios such as new file creation, modification of existing files, conflict handling, and error cases. """ import os import sys import tempfile import shutil import pytest import git # GitPython is required; assumed installed via project dependencies # Add src directory to path for module import sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src')) from llm_codegen.diff_applier import parse_diff_files, apply_diff def test_parse_diff_files(): """Test parsing unified diff strings to extract file paths.""" # Diff with modification and new file diff = """--- a/file1.txt +++ b/file1.txt @@ -1 +1 @@ -old content +new content --- a/new_file.txt +++ b/new_file.txt @@ -0,0 +1 @@ +new file content """ files = parse_diff_files(diff) assert set(files) == {'file1.txt', 'new_file.txt'} # Diff with file deletion diff_del = """--- a/deleted.txt +++ /dev/null @@ -1 +0,0 @@ -content to delete """ files = parse_diff_files(diff_del) assert files == ['deleted.txt'] # Old file path is extracted # Empty diff assert parse_diff_files('') == [] assert parse_diff_files('\n') == [] # Diff with only new file (no old file) diff_new_only = """--- /dev/null +++ b/only_new.txt @@ -0,0 +1 @@ +only new """ files = parse_diff_files(diff_new_only) assert files == [] /dev/null is ignored # Invalid diff format (should still handle gracefully) diff_invalid = "invalid diff string" files = parse_diff_files(diff_invalid) assert files == [] # No valid file paths found @pytest.fixture def temp_git_repo(): """Create a temporary git repository for testing apply_diff.""" temp_dir = tempfile.mkdtemp() repo = git.Repo.init(temp_dir) # Make an initial commit to have a clean state init_file = os.path.join(temp_dir, 'README.md') with open(init_file, 'w') as f: f.write('Initial commit content') repo.git.add('README.md') repo.git.commit('-m', 'initial commit') yield temp_dir, repo # Cleanup after test shutil.rmtree(temp_dir, ignore_errors=True) def test_apply_diff_new_file(temp_git_repo): """Test applying a diff that creates a new file.""" temp_dir, repo = temp_git_repo diff = """--- /dev/null +++ b/test_new.txt @@ -0,0 +1 @@ +This is a newly created file. """ result = apply_diff(diff, temp_dir) assert result['success'] == True assert 'test_new.txt' in result['applied_files'] new_file_path = os.path.join(temp_dir, 'test_new.txt') assert os.path.exists(new_file_path) with open(new_file_path, 'r') as f: content = f.read() assert content.strip() == 'This is a newly created file.' def test_apply_diff_modify_existing_file(temp_git_repo): """Test applying a diff that modifies an existing file.""" temp_dir, repo = temp_git_repo # Create an existing file and commit it existing_file = os.path.join(temp_dir, 'existing.txt') with open(existing_file, 'w') as f: f.write('Original line 1\nOriginal line 2') repo.git.add('existing.txt') repo.git.commit('-m', 'add existing file') diff = """--- a/existing.txt +++ b/existing.txt @@ -1,2 +1,2 @@ -Original line 1 +Modified line 1 Original line 2 """ result = apply_diff(diff, temp_dir) assert result['success'] == True assert 'existing.txt' in result['applied_files'] with open(existing_file, 'r') as f: content = f.read() assert content == 'Modified line 1\nOriginal line 2' def test_apply_diff_conflict_handling(temp_git_repo): """Test applying a diff that causes a conflict with uncommitted changes.""" temp_dir, repo = temp_git_repo # Create and commit a file conflict_file = os.path.join(temp_dir, 'conflict.txt') with open(conflict_file, 'w') as f: f.write('Initial line 1\nInitial line 2') repo.git.add('conflict.txt') repo.git.commit('-m', 'add conflict file') # Modify the file without committing to create conflict with open(conflict_file, 'w') as f: f.write('Changed line 1\nInitial line 2') # Change first line diff = """--- a/conflict.txt +++ b/conflict.txt @@ -1,2 +1,2 @@ -Initial line 1 +Diff line 1 Initial line 2 """ result = apply_diff(diff, temp_dir) assert result['success'] == False # Check for conflict or error in message assert 'conflict' in result['message'].lower() or 'error' in result['message'].lower() assert result['error_details'] != '' def test_apply_diff_empty_diff(): """Test applying an empty diff string.""" result = apply_diff('', '.') assert result['success'] == False assert 'empty' in result['message'].lower() def test_apply_diff_invalid_directory(): """Test applying a diff to a non-existent directory.""" non_existent_dir = '/tmp/non_existent_dir_12345' diff = """--- a/dummy.txt +++ b/dummy.txt @@ -1 +1 @@ -old +new """ result = apply_diff(diff, non_existent_dir) assert result['success'] == False assert 'does not exist' in result['message'].lower() def test_apply_diff_no_git_repo_initialization(): """Test applying a diff to a non-git directory, which should initialize a repo.""" temp_dir = tempfile.mkdtemp() try: # Create a non-git directory with a file non_git_file = os.path.join(temp_dir, 'non_git.txt') with open(non_git_file, 'w') as f: f.write('Pre-existing content') diff = """--- a/non_git.txt +++ b/non_git.txt @@ -1 +1 @@ -Pre-existing content +Updated content """ result = apply_diff(diff, temp_dir) assert result['success'] == True assert 'non_git.txt' in result['applied_files'] with open(non_git_file, 'r') as f: content = f.read() assert content == 'Updated content' finally: shutil.rmtree(temp_dir, ignore_errors=True) if __name__ == "__main__": # Run tests if executed as script pytest.main([__file__])