190 lines
6.1 KiB
Python
190 lines
6.1 KiB
Python
"""Tests for change analyzer module."""
|
|
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
|
|
from src.analyzer import (
|
|
ChangeAnalyzer,
|
|
ChangeSet,
|
|
ChangeType,
|
|
StagedChange,
|
|
)
|
|
|
|
|
|
class TestStagedChange:
|
|
"""Tests for StagedChange class."""
|
|
|
|
def test_filename_property(self):
|
|
"""Test extracting filename from path."""
|
|
change = StagedChange(
|
|
path="src/cli.py",
|
|
change_type=ChangeType.MODIFIED
|
|
)
|
|
assert change.filename == "cli.py"
|
|
|
|
def test_is_new_true_for_added(self):
|
|
"""Test is_new returns True for added files."""
|
|
change = StagedChange(
|
|
path="new_file.py",
|
|
change_type=ChangeType.ADDED
|
|
)
|
|
assert change.is_new is True
|
|
|
|
def test_is_new_false_for_modified(self):
|
|
"""Test is_new returns False for modified files."""
|
|
change = StagedChange(
|
|
path="existing.py",
|
|
change_type=ChangeType.MODIFIED
|
|
)
|
|
assert change.is_new is False
|
|
|
|
def test_is_deleted_true_for_deleted(self):
|
|
"""Test is_deleted returns True for deleted files."""
|
|
change = StagedChange(
|
|
path="deleted.py",
|
|
change_type=ChangeType.DELETED
|
|
)
|
|
assert change.is_deleted is True
|
|
|
|
|
|
class TestChangeSet:
|
|
"""Tests for ChangeSet class."""
|
|
|
|
def test_added_property(self):
|
|
"""Test filtering added files."""
|
|
changes = ChangeSet([
|
|
StagedChange("a.py", ChangeType.ADDED),
|
|
StagedChange("b.py", ChangeType.MODIFIED),
|
|
StagedChange("c.py", ChangeType.ADDED),
|
|
])
|
|
assert len(changes.added) == 2
|
|
assert all(c.change_type == ChangeType.ADDED for c in changes.added)
|
|
|
|
def test_deleted_property(self):
|
|
"""Test filtering deleted files."""
|
|
changes = ChangeSet([
|
|
StagedChange("a.py", ChangeType.DELETED),
|
|
StagedChange("b.py", ChangeType.MODIFIED),
|
|
])
|
|
assert len(changes.deleted) == 1
|
|
|
|
def test_modified_property(self):
|
|
"""Test filtering modified files."""
|
|
changes = ChangeSet([
|
|
StagedChange("a.py", ChangeType.MODIFIED),
|
|
StagedChange("b.py", ChangeType.MODIFIED),
|
|
])
|
|
assert len(changes.modified) == 2
|
|
|
|
def test_total_count(self):
|
|
"""Test counting total changes."""
|
|
changes = ChangeSet([
|
|
StagedChange("a.py", ChangeType.ADDED),
|
|
StagedChange("b.py", ChangeType.DELETED),
|
|
StagedChange("c.py", ChangeType.MODIFIED),
|
|
])
|
|
assert changes.total_count == 3
|
|
|
|
def test_file_paths(self):
|
|
"""Test getting all file paths."""
|
|
changes = ChangeSet([
|
|
StagedChange("a.py", ChangeType.ADDED),
|
|
StagedChange("b.py", ChangeType.MODIFIED),
|
|
])
|
|
assert changes.file_paths == ["a.py", "b.py"]
|
|
|
|
def test_has_changes_true(self):
|
|
"""Test has_changes returns True when changes exist."""
|
|
changes = ChangeSet([
|
|
StagedChange("a.py", ChangeType.ADDED),
|
|
])
|
|
assert changes.has_changes is True
|
|
|
|
def test_has_changes_false_for_empty(self):
|
|
"""Test has_changes returns False for empty ChangeSet."""
|
|
changes = ChangeSet([])
|
|
assert changes.has_changes is False
|
|
|
|
|
|
class TestChangeAnalyzer:
|
|
"""Tests for ChangeAnalyzer class."""
|
|
|
|
def test_init_with_repo_path(self):
|
|
"""Test initialization with repository path."""
|
|
analyzer = ChangeAnalyzer("/path/to/repo")
|
|
assert analyzer.repo_path == "/path/to/repo"
|
|
|
|
def test_init_without_repo_path(self):
|
|
"""Test initialization without repository path."""
|
|
analyzer = ChangeAnalyzer()
|
|
assert analyzer.repo_path is None
|
|
|
|
@patch("src.analyzer.Repo")
|
|
def test_get_staged_changes_empty(self, mock_repo_class):
|
|
"""Test getting staged changes when empty."""
|
|
mock_repo = MagicMock()
|
|
mock_repo.index.diff.return_value = []
|
|
mock_repo.index.diff.return_value = []
|
|
mock_repo.index.unmerged_blobs.return_value = {}
|
|
mock_repo_class.return_value = mock_repo
|
|
|
|
analyzer = ChangeAnalyzer("/tmp")
|
|
result = analyzer.get_staged_changes()
|
|
|
|
assert result.has_changes is False
|
|
|
|
@patch("src.analyzer.Repo")
|
|
def test_get_staged_changes_with_changes(self, mock_repo_class):
|
|
"""Test getting staged changes - verify method is called."""
|
|
mock_repo = MagicMock()
|
|
mock_repo.index.diff.return_value = []
|
|
mock_repo.index.unmerged_blobs.return_value = {}
|
|
mock_repo_class.return_value = mock_repo
|
|
|
|
analyzer = ChangeAnalyzer("/tmp")
|
|
analyzer.get_staged_changes()
|
|
|
|
assert mock_repo.index.diff.called
|
|
|
|
@patch("src.analyzer.Repo")
|
|
def test_get_staged_changes_not_in_git_repo(self, mock_repo_class):
|
|
"""Test error when not in git repository."""
|
|
from git.exc import InvalidGitRepositoryError
|
|
|
|
mock_repo_class.side_effect = InvalidGitRepositoryError()
|
|
|
|
analyzer = ChangeAnalyzer("/tmp")
|
|
|
|
with pytest.raises(ValueError, match="Not a git repository"):
|
|
analyzer.get_staged_changes()
|
|
|
|
@patch("src.analyzer.Repo")
|
|
def test_get_changed_extensions(self, mock_repo_class):
|
|
"""Test getting file extensions from changes."""
|
|
mock_diff1 = MagicMock()
|
|
mock_diff1.b_path = "src/cli.py"
|
|
mock_diff1.a_path = "src/cli.py"
|
|
mock_diff1.new_file = False
|
|
mock_diff1.deleted_file = False
|
|
mock_diff1.renamed_file = False
|
|
mock_diff1.type_changed = False
|
|
|
|
mock_diff2 = MagicMock()
|
|
mock_diff2.b_path = "tests/test_cli.py"
|
|
mock_diff2.a_path = "tests/test_cli.py"
|
|
mock_diff2.new_file = False
|
|
mock_diff2.deleted_file = False
|
|
mock_diff2.renamed_file = False
|
|
mock_diff2.type_changed = False
|
|
|
|
mock_repo = MagicMock()
|
|
mock_repo.index.diff.return_value = [mock_diff1, mock_diff2]
|
|
mock_repo.index.unmerged_blobs.return_value = {}
|
|
mock_repo_class.return_value = mock_repo
|
|
|
|
analyzer = ChangeAnalyzer("/tmp")
|
|
extensions = analyzer.get_changed_extensions()
|
|
|
|
assert ".py" in extensions
|