diff --git a/backend/app/services/agents/tests/__pycache__/test_code_audit_agent.cpython-313-pytest-8.4.2.pyc b/backend/app/services/agents/tests/__pycache__/test_code_audit_agent.cpython-313-pytest-8.4.2.pyc index 434714e..b481344 100644 Binary files a/backend/app/services/agents/tests/__pycache__/test_code_audit_agent.cpython-313-pytest-8.4.2.pyc and b/backend/app/services/agents/tests/__pycache__/test_code_audit_agent.cpython-313-pytest-8.4.2.pyc differ diff --git a/backend/app/services/agents/tests/test_code_audit_agent.py b/backend/app/services/agents/tests/test_code_audit_agent.py index 83e8d27..c033b04 100644 --- a/backend/app/services/agents/tests/test_code_audit_agent.py +++ b/backend/app/services/agents/tests/test_code_audit_agent.py @@ -1,9 +1,10 @@ import pytest import pytest_asyncio import respx -from httpx import Response +from httpx import Response, Request, HTTPStatusError, RequestError from backend.app.services.agents.code_audit_agent import CodeAuditAgent, CodeMetrics, AuditSummary, CodeAuditResult + @pytest_asyncio.fixture async def code_audit_agent(): async with CodeAuditAgent() as agent: @@ -22,7 +23,6 @@ async def test_fetch_github_repo_metrics(code_audit_agent): respx.get(f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'}, json=[])) # Mock latest release respx.get(f"https://api.github.com/repos/{owner}/{repo}/releases/latest").mock(return_value=Response(200, json={'tag_name': 'v1.0.0'})) - # Mock issues count using GitHub Search API respx.get(f"https://api.github.com/search/issues?q=repo%3A{owner}%2F{repo}%2Btype%3Aissue&per_page=1").mock(return_value=Response(200, json={'total_count': 20})) # Mock pull requests count respx.get(f"https://api.github.com/repos/{owner}/{repo}/pulls?state=all&per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'}, json=[])) @@ -102,11 +102,11 @@ async def test_audit_codebase(code_audit_agent): with respx.mock: # Mock GitHub API calls for fetch_repo_metrics - respx.get(f"https://api.github.com/repos/{owner}/{repo}/commits?per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'})) - respx.get(f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'})) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/commits?per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'}, json=[])) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'}, json=[])) respx.get(f"https://api.github.com/repos/{owner}/{repo}/releases/latest").mock(return_value=Response(200, json={'tag_name': 'v1.0.0'})) respx.get(f"https://api.github.com/search/issues?q=repo%3A{owner}%2F{repo}%2Btype%3Aissue&per_page=1").mock(return_value=Response(200, json={'total_count': 20})) - respx.get(f"https://api.github.com/repos/{owner}/{repo}/pulls?state=all&per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'})) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/pulls?state=all&per_page=1").mock(return_value=Response(200, headers={'link': '; rel="next", ; rel="last"'}, json=[])) result = await code_audit_agent.audit_codebase(repo_url, project_name) @@ -115,3 +115,128 @@ async def test_audit_codebase(code_audit_agent): assert len(result.audit_summaries) == 2 assert project_name in result.audit_summaries[0].report_title +@pytest.mark.asyncio +async def test_fetch_github_repo_metrics_http_error(code_audit_agent): + repo_url = "https://github.com/nonexistent/repo" + owner = "nonexistent" + repo = "repo" + + with respx.mock: + # Mock all GitHub API calls to return 404 Not Found + respx.get(f"https://api.github.com/repos/{owner}/{repo}/commits?per_page=1").mock(return_value=Response(404)) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1").mock(return_value=Response(404)) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/releases/latest").mock(return_value=Response(404)) + respx.get(f"https://api.github.com/search/issues?q=repo%3A{owner}%2F{repo}%2Btype%3Aissue&per_page=1").mock(return_value=Response(404)) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/pulls?state=all&per_page=1").mock(return_value=Response(404)) + + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 + +@pytest.mark.asyncio +async def test_fetch_github_repo_metrics_network_error(code_audit_agent): + repo_url = "https://github.com/owner/repo" + owner = "owner" + repo = "repo" + + with respx.mock: + # Mock all GitHub API calls to raise a RequestError + respx.get(f"https://api.github.com/repos/{owner}/{repo}/commits?per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://api.github.com/repos/{owner}/{repo}/commits?per_page=1"))) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://api.github.com/repos/{owner}/{repo}/contributors?per_page=1"))) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/releases/latest").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://api.github.com/repos/{owner}/{repo}/releases/latest"))) + respx.get(f"https://api.github.com/search/issues?q=repo%3A{owner}%2F{repo}%2Btype%3Aissue&per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://api.github.com/search/issues?q=repo%3A{owner}%2F{repo}%2Btype%3Aissue&per_page=1"))) + respx.get(f"https://api.github.com/repos/{owner}/{repo}/pulls?state=all&per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://api.github.com/repos/{owner}/{repo}/pulls?state=all&per_page=1"))) + + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 + +@pytest.mark.asyncio +async def test_fetch_gitlab_repo_metrics_http_error(code_audit_agent): + repo_url = "https://gitlab.com/nonexistent/repo" + project_id = "nonexistent%2Frepo" + + with respx.mock: + # Mock all GitLab API calls to return 404 Not Found + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/repository/commits?per_page=1").mock(return_value=Response(404)) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/repository/contributors?per_page=1").mock(return_value=Response(404)) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/repository/tags?per_page=1").mock(return_value=Response(404)) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/issues?scope=all&per_page=1").mock(return_value=Response(404)) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/merge_requests?scope=all&per_page=1").mock(return_value=Response(404)) + + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 + +@pytest.mark.asyncio +async def test_fetch_gitlab_repo_metrics_network_error(code_audit_agent): + repo_url = "https://gitlab.com/group/project" + project_id = "group%2Fproject" + + with respx.mock: + # Mock all GitLab API calls to raise a RequestError + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/repository/commits?per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://gitlab.com/api/v4/projects/{project_id}/repository/commits?per_page=1"))) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/repository/contributors?per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://gitlab.com/api/v4/projects/{project_id}/repository/contributors?per_page=1"))) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/repository/tags?per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://gitlab.com/api/v4/projects/{project_id}/repository/tags?per_page=1"))) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/issues?scope=all&per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://gitlab.com/api/v4/projects/{project_id}/issues?scope=all&per_page=1"))) + respx.get(f"https://gitlab.com/api/v4/projects/{project_id}/merge_requests?scope=all&per_page=1").mock(side_effect=RequestError("Network error", request=Request("GET", f"https://gitlab.com/api/v4/projects/{project_id}/merge_requests?scope=all&per_page=1"))) + + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 + +@pytest.mark.asyncio +async def test_fetch_repo_metrics_unsupported_url(code_audit_agent): + repo_url = "https://bitbucket.org/some/repo" + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 + +@pytest.mark.asyncio +async def test_fetch_repo_metrics_invalid_github_url_format(code_audit_agent): + repo_url = "https://github.com/owner_only" + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 + +@pytest.mark.asyncio +async def test_fetch_repo_metrics_invalid_gitlab_url_format(code_audit_agent): + repo_url = "https://gitlab.com/group_only" + metrics = await code_audit_agent.fetch_repo_metrics(repo_url) + + assert metrics.repo_url == repo_url + assert metrics.commits_count == 0 + assert metrics.contributors_count == 0 + assert metrics.latest_release == "N/A" + assert metrics.issues_count == 0 + assert metrics.pull_requests_count == 0 \ No newline at end of file