diff --git a/tests/test_client.py b/tests/test_client.py index 90a7201..63b4496 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -287,6 +287,41 @@ class TestGetWithRetry429: assert mock_sleep.call_count == 2 +class TestGetPaginatedEdgeCases: + """Test edge cases for API responses.""" + + def _make_client(self): + return GiteaClient("http://gitea.local:3000", "tok") + + def test_get_paginated_malformed_json(self): + """Response with invalid JSON raises JSONDecodeError.""" + import json + + client = self._make_client() + mock_resp = MagicMock() + mock_resp.raise_for_status = MagicMock() + mock_resp.json.side_effect = json.JSONDecodeError("Expecting value", "", 0) + + with patch.object(client.session, "get", return_value=mock_resp): + with pytest.raises(json.JSONDecodeError): + client._get_paginated("/api/v1/user/repos") + + def test_get_repos_html_response(self): + """HTML response (status 200 but HTML content) raises on json parsing.""" + import json + + client = self._make_client() + mock_resp = MagicMock() + mock_resp.raise_for_status = MagicMock() + mock_resp.json.side_effect = json.JSONDecodeError( + "Expecting value", "Maintenance", 0 + ) + + with patch.object(client.session, "get", return_value=mock_resp): + with pytest.raises(json.JSONDecodeError): + client.get_repos() + + class TestGetLatestCommit: """Test get_latest_commit method.""" diff --git a/tests/test_collector.py b/tests/test_collector.py index a8e1f8c..3e4f9e5 100644 --- a/tests/test_collector.py +++ b/tests/test_collector.py @@ -178,6 +178,40 @@ class TestCollectAllLastCommit: assert result[0].last_commit_date is None +class TestRepoDataEdgeCases: + """Test RepoData with edge case data.""" + + def test_repo_data_unicode_description(self): + """RepoData with full unicode description (accents, CJK, emojis).""" + repo = RepoData( + name="unicode-test", + full_name="admin/unicode-test", + description="Projet avec accents : e, a, u, CJK: δΈ­ζ–‡, emojis: πŸš€πŸŽ‰", + open_issues=0, + is_fork=False, + is_archived=False, + is_mirror=False, + latest_release=None, + milestones=[], + last_commit_date=None, + ) + assert "πŸš€" in repo.description + assert "δΈ­ζ–‡" in repo.description + + def test_collect_all_repo_zero_commits_and_no_release(self): + """Repo with no commits AND no release produces valid RepoData.""" + client = MagicMock() + client.get_repos.return_value = [_make_repo()] + client.get_latest_release.return_value = None + client.get_milestones.return_value = [] + client.get_latest_commit.return_value = None + + result = collect_all(client) + + assert result[0].last_commit_date is None + assert result[0].latest_release is None + + class TestCollectAllFiltering: """Test collect_all filtering (include/exclude).""" diff --git a/tests/test_display.py b/tests/test_display.py index 3dd15de..b48e69a 100644 --- a/tests/test_display.py +++ b/tests/test_display.py @@ -230,6 +230,31 @@ class TestRenderDashboardEmpty: assert "Aucun repo" in output +class TestRenderDashboardEdgeCases: + """Test edge cases for dashboard rendering.""" + + def test_render_dashboard_unicode_description(self): + """Repo with unicode description renders without crash.""" + console, buf = _make_console() + repos = [_make_repo(name="unicode", description="Projet πŸš€ avec accents eaiu δΈ­ζ–‡")] + + render_dashboard(repos, console=console) + output = buf.getvalue() + + assert "unicode" in output + + def test_render_dashboard_control_chars_in_name(self): + """Repo with control characters in name renders without crash.""" + console, buf = _make_console() + repos = [_make_repo(name="test\x00repo")] + + render_dashboard(repos, console=console) + output = buf.getvalue() + + # Rich may strip or display the control char, but must not crash + assert "test" in output + + class TestColorizeMilestoneDue: """Test _colorize_milestone_due function.""" diff --git a/tests/test_exporter.py b/tests/test_exporter.py index 506c0a7..0a40bc9 100644 --- a/tests/test_exporter.py +++ b/tests/test_exporter.py @@ -93,6 +93,26 @@ class TestSanitizeControlChars: assert result[0]["full_name"] == "admin/testrepo" +class TestExportJsonEdgeCases: + """Test edge cases for JSON export.""" + + def test_export_json_empty_description(self): + """Empty description produces valid JSON.""" + repo = _make_repo(description="") + output = export_json([repo]) + + parsed = json.loads(output) + assert parsed[0]["description"] == "" + + def test_export_json_very_long_description(self): + """Very long description (10000 chars) produces valid JSON.""" + repo = _make_repo(description="x" * 10000) + output = export_json([repo]) + + parsed = json.loads(output) + assert len(parsed[0]["description"]) == 10000 + + class TestExportJson: """Test export_json function."""