diff --git a/tests/test_models.py b/tests/test_models.py new file mode 100644 index 0000000..f0b99a1 --- /dev/null +++ b/tests/test_models.py @@ -0,0 +1,205 @@ +"""Tests for data models.""" + +import pytest +from datetime import datetime + +from repohealth.models.file_stats import FileAnalysis +from repohealth.models.author import AuthorStats +from repohealth.models.result import RepositoryResult, RiskLevel + + +class TestFileAnalysis: + """Tests for FileAnalysis model.""" + + def test_file_analysis_creation(self): + """Test creating a FileAnalysis instance.""" + analysis = FileAnalysis( + path="src/main.py", + total_commits=10, + author_commits={"author1@example.com": 6, "author2@example.com": 4} + ) + + assert analysis.path == "src/main.py" + assert analysis.total_commits == 10 + assert analysis.num_authors == 2 + assert analysis.bus_factor == 1.0 + + def test_num_authors(self): + """Test num_authors property.""" + analysis = FileAnalysis( + path="test.py", + total_commits=5, + author_commits={"a@x.com": 3, "b@x.com": 2} + ) + + assert analysis.num_authors == 2 + + def test_num_authors_empty(self): + """Test num_authors with empty commits.""" + analysis = FileAnalysis( + path="test.py", + total_commits=0, + author_commits={} + ) + + assert analysis.num_authors == 0 + + def test_top_author(self): + """Test top_author property.""" + analysis = FileAnalysis( + path="test.py", + total_commits=10, + author_commits={"a@x.com": 7, "b@x.com": 3} + ) + + top_author, count = analysis.top_author + assert top_author == "a@x.com" + assert count == 7 + + def test_top_author_empty(self): + """Test top_author with empty commits.""" + analysis = FileAnalysis( + path="test.py", + total_commits=0, + author_commits={} + ) + + assert analysis.top_author is None + + def test_top_author_share(self): + """Test top_author_share property.""" + analysis = FileAnalysis( + path="test.py", + total_commits=10, + author_commits={"a@x.com": 8, "b@x.com": 2} + ) + + assert analysis.top_author_share == 0.8 + + def test_top_author_share_empty(self): + """Test top_author_share with no commits.""" + analysis = FileAnalysis( + path="test.py", + total_commits=0, + author_commits={} + ) + + assert analysis.top_author_share == 0.0 + + def test_get_author_share(self): + """Test get_author_share method.""" + analysis = FileAnalysis( + path="test.py", + total_commits=10, + author_commits={"a@x.com": 5, "b@x.com": 5} + ) + + assert analysis.get_author_share("a@x.com") == 0.5 + assert analysis.get_author_share("b@x.com") == 0.50 + assert analysis.get_author_share("c@x.com") == 0.0 + + def test_module_and_extension(self): + """Test module and extension extraction.""" + analysis = FileAnalysis( + path="src/core/main.py", + total_commits=5, + author_commits={}, + module="src/core", + extension="py" + ) + + assert analysis.module == "src/core" + assert analysis.extension == "py" + + +class TestAuthorStats: + """Tests for AuthorStats model.""" + + def test_author_stats_creation(self): + """Test creating an AuthorStats instance.""" + stats = AuthorStats( + name="Test Author", + email="test@example.com", + total_commits=100 + ) + + assert stats.name == "Test Author" + assert stats.email == "test@example.com" + assert stats.total_commits == 100 + assert len(stats.files_touched) == 0 + + def test_add_file(self): + """Test adding a file contribution.""" + stats = AuthorStats(name="Test", email="test@test.com") + stats.add_file("src/main.py", "src") + + assert "src/main.py" in stats.files_touched + assert "src" in stats.modules_contributed + assert stats.total_contributions == 1 + + def test_merge(self): + """Test merging two AuthorStats.""" + stats1 = AuthorStats(name="Test", email="test@test.com") + stats1.total_commits = 10 + stats1.files_touched = {"file1.py"} + + stats2 = AuthorStats(name="Test", email="test@test.com") + stats2.total_commits = 5 + stats2.files_touched = {"file2.py"} + + stats1.merge(stats2) + + assert stats1.total_commits == 15 + assert "file1.py" in stats1.files_touched + assert "file2.py" in stats1.files_touched + + +class TestRepositoryResult: + """Tests for RepositoryResult model.""" + + def test_repository_result_creation(self): + """Test creating a RepositoryResult instance.""" + result = RepositoryResult( + repository_path="/test/repo", + files_analyzed=100, + total_commits=500, + unique_authors=5 + ) + + assert result.repository_path == "/test/repo" + assert result.files_analyzed == 100 + assert result.total_commits == 500 + assert result.unique_authors == 5 + + def test_risk_count_properties(self): + """Test risk count properties.""" + result = RepositoryResult( + repository_path="/test/repo", + files=[ + {"risk_level": "critical"}, + {"risk_level": "critical"}, + {"risk_level": "high"}, + {"risk_level": "high"}, + {"risk_level": "medium"}, + {"risk_level": "low"} + ] + ) + + assert result.high_risk_count == 2 + assert result.medium_risk_count == 1 + assert result.low_risk_count == 1 + + def test_to_dict(self): + """Test to_dict serialization.""" + result = RepositoryResult( + repository_path="/test/repo", + files_analyzed=10, + total_commits=50, + unique_authors=3 + ) + + result_dict = result.to_dict() + + assert result_dict["repository"] == "/test/repo" + assert result_dict["files_analyzed"] == 10 + assert "analyzed_at" in result_dict