Spaces:
Running
Running
| # tests/test_code_review_agent_extended.py | |
| from unittest.mock import AsyncMock, patch | |
| import pytest | |
| from app.agents import code_review_agent | |
| from app.models.repository import RepositoryMetadata | |
| def _metadata( | |
| base_sha: str | None = None, head_sha: str | None = None | |
| ) -> RepositoryMetadata: | |
| return RepositoryMetadata( | |
| url="https://github.com/test/repo", | |
| name="repo", | |
| local_path="/tmp/fake", | |
| base_sha=base_sha, | |
| head_sha=head_sha, | |
| ) | |
| async def test_code_review_uses_changed_files_when_shas_provided(tmp_path) -> None: | |
| """When base_sha and head_sha are set, get_changed_files is called.""" | |
| src = tmp_path / "main.py" | |
| src.write_text("def foo(): pass\n") | |
| with ( | |
| patch( | |
| "app.agents.code_review_agent.get_changed_files", | |
| return_value=[str(src)], | |
| ) as mock_diff, | |
| patch( | |
| "app.agents.code_review_agent.call_llm_for_json", | |
| new=AsyncMock( | |
| return_value={ | |
| "solid_violations": [], | |
| "duplicate_code": [], | |
| "refactor_suggestions": [], | |
| "overall_score": 7.0, | |
| "summary": "ok", | |
| } | |
| ), | |
| ), | |
| ): | |
| result = await code_review_agent.run( | |
| str(tmp_path), | |
| _metadata(base_sha="abc", head_sha="def"), | |
| ) | |
| mock_diff.assert_called_once() | |
| assert result.overall_score == 7.0 | |
| async def test_code_review_skips_test_files(tmp_path) -> None: | |
| """Files named test_*.py are excluded from the review.""" | |
| test_file = tmp_path / "test_something.py" | |
| test_file.write_text("def test_x(): pass\n") | |
| with patch( | |
| "app.agents.code_review_agent.call_llm_for_json", | |
| new=AsyncMock(return_value={}), | |
| ): | |
| result = await code_review_agent.run(str(tmp_path), _metadata()) | |
| # Only test files present → no source samples → score 0 | |
| assert result.overall_score == 0.0 | |
| async def test_code_review_low_score_triggers_followup(tmp_path) -> None: | |
| """A score below 5.0 triggers a second LLM call for priority fixes.""" | |
| src = tmp_path / "main.py" | |
| src.write_text("def foo(): pass\n") | |
| responses = iter( | |
| [ | |
| # first call: main review — score is 3.0 (triggers second pass) | |
| { | |
| "solid_violations": ["SRP violation"], | |
| "duplicate_code": [], | |
| "refactor_suggestions": [], | |
| "overall_score": 3.0, | |
| "summary": "poor code", | |
| }, | |
| # auto-fix calls (top_findings[:3], but solid_violations has 1 item) | |
| { | |
| "file": "", | |
| "original_snippet": "", | |
| "fixed_snippet": "", | |
| "explanation": "n/a", | |
| }, | |
| # second pass: priority fixes | |
| {"priority_fixes": ["fix SRP first"]}, | |
| ] | |
| ) | |
| with patch( | |
| "app.agents.code_review_agent.call_llm_for_json", | |
| new=AsyncMock(side_effect=lambda *a, **kw: next(responses)), | |
| ): | |
| result = await code_review_agent.run(str(tmp_path), _metadata()) | |
| assert "fix SRP first" in result.refactor_suggestions | |
| async def test_code_review_handles_file_read_error(tmp_path) -> None: | |
| """A file that raises on open is skipped gracefully.""" | |
| src = tmp_path / "main.py" | |
| src.write_text("def foo(): pass\n") | |
| original_open = open | |
| def patched_open(path, *args, **kwargs): # type: ignore[no-untyped-def] | |
| if str(path).endswith("main.py"): | |
| raise OSError("permission denied") | |
| return original_open(path, *args, **kwargs) | |
| with ( | |
| patch("builtins.open", side_effect=patched_open), | |
| patch( | |
| "app.agents.code_review_agent.call_llm_for_json", | |
| new=AsyncMock(return_value={}), | |
| ), | |
| ): | |
| result = await code_review_agent.run(str(tmp_path), _metadata()) | |
| # No readable files → falls back to empty review | |
| assert result.overall_score == 0.0 | |