From 8bc07eec40b2915da53e33ecb2144d754a10bbbb Mon Sep 17 00:00:00 2001 From: 7000pctAUTO Date: Wed, 4 Feb 2026 16:59:29 +0000 Subject: [PATCH] fix: Add Gitea Actions CI workflow and fix linting issues --- tests/test_analyzer.py | 189 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 tests/test_analyzer.py diff --git a/tests/test_analyzer.py b/tests/test_analyzer.py new file mode 100644 index 0000000..8c709f1 --- /dev/null +++ b/tests/test_analyzer.py @@ -0,0 +1,189 @@ +"""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