""" Tests for PR Reviewer Tool """ import pytest import os import sys sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) from services.router.tool_manager import ToolManager, ToolResult class TestPRReviewerToolSecurity: """Test security features of PR Reviewer Tool""" @pytest.mark.asyncio async def test_diff_too_large_rejected(self): """Test that diff exceeding max_chars is rejected""" tool_mgr = ToolManager({}) large_diff = "x" * 500000 # 500KB result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": { "text": large_diff, "max_chars": 400000 } }) assert result.success is False assert "too large" in result.error.lower() @pytest.mark.asyncio async def test_too_many_files_rejected(self): """Test that diff with too many files is rejected""" tool_mgr = ToolManager({}) # Create diff with 201 files diff_text = "" for i in range(201): diff_text += f"""diff --git a/file{i}.py b/file{i}.py --- a/file{i}.py +++ b/file{i}.py @@ -1,1 +1,2 @@ -old +new """ result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": { "text": diff_text, "max_files": 200 } }) assert result.success is False assert "too many files" in result.error.lower() class TestPRReviewerToolBlocking: """Test blocking issue detection""" @pytest.mark.asyncio async def test_secrets_detected(self): """Test that secrets in diff are detected""" tool_mgr = ToolManager({}) diff = """diff --git a/config.py b/config.py --- a/config.py +++ b/config.py @@ -1,2 +1,3 @@ # config -API_KEY = "sk-live-abc123def456" +API_KEY = "sk-live-xyz789" """ result = await tool_mgr._pr_reviewer_tool({ "mode": "blocking_only", "diff": {"text": diff} }) assert result.success is True assert len(result.result["blocking_issues"]) > 0 # Evidence should be masked for issue in result.result["blocking_issues"]: assert "sk-live" not in issue.get("evidence", "") @pytest.mark.asyncio async def test_rce_detected(self): """Test that RCE patterns are detected""" tool_mgr = ToolManager({}) diff = """diff --git a/app.py b/app.py --- a/app.py +++ b/app.py @@ -1,3 +1,4 @@ import os +os.system("rm -rf /") """ result = await tool_mgr._pr_reviewer_tool({ "mode": "blocking_only", "diff": {"text": diff} }) assert result.success is True blocking = result.result["blocking_issues"] assert any("RCE" in i.get("title", "") for i in blocking) @pytest.mark.asyncio async def test_sql_injection_detected(self): """Test that SQL injection patterns are detected""" tool_mgr = ToolManager({}) diff = """diff --git a/db.py b/db.py --- a/db.py +++ b/db.py @@ -1,3 +1,4 @@ query = "SELECT * FROM users WHERE id = " + user_input """ result = await tool_mgr._pr_reviewer_tool({ "mode": "blocking_only", "diff": {"text": diff} }) assert result.success is True blocking = result.result["blocking_issues"] assert any("SQL" in i.get("title", "") for i in blocking) @pytest.mark.asyncio async def test_auth_bypass_detected(self): """Test that auth bypass patterns are detected""" tool_mgr = ToolManager({}) diff = """diff --git a/auth.py b/auth.py --- a/auth.py +++ b/auth.py @@ -1,3 +1,4 @@ +# Skip auth for testing +if os.getenv("TESTING"): + return True """ result = await tool_mgr._pr_reviewer_tool({ "mode": "blocking_only", "diff": {"text": diff} }) assert result.success is True blocking = result.result["blocking_issues"] assert any("AUTH" in i.get("title", "") or "SECURITY" in i.get("title", "") for i in blocking) class TestPRReviewerToolModes: """Test different modes of PR Reviewer""" @pytest.mark.asyncio async def test_blocking_only_mode(self): """Test blocking_only mode returns only blocking issues""" tool_mgr = ToolManager({}) diff = """diff --git a/test.py b/test.py --- a/test.py +++ b/test.py @@ -1,3 +1,4 @@ +print("debug") +# TODO: fix this """ result = await tool_mgr._pr_reviewer_tool({ "mode": "blocking_only", "diff": {"text": diff} }) assert result.success is True # Should have no issues in blocking_only for non-critical assert "issues" in result.result # Should have summary assert "summary" in result.result @pytest.mark.asyncio async def test_full_review_mode(self): """Test full_review mode returns all issues""" tool_mgr = ToolManager({}) diff = """diff --git a/test.py b/test.py --- a/test.py +++ b/test.py @@ -1,3 +1,4 @@ +print("debug") +except: pass """ result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": {"text": diff} }) assert result.success is True # Should have issues in full_review assert len(result.result["issues"]) > 0 class TestPRReviewerToolScoring: """Test scoring functionality""" @pytest.mark.asyncio async def test_risk_scoring(self): """Test that risk score reflects issues""" tool_mgr = ToolManager({}) # Diff with secrets - high risk diff = """diff --git a/config.py b/config.py --- a/config.py +++ b/config.py @@ -1,2 +1,3 @@ +API_KEY = "sk-live-secret123" """ result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": {"text": diff} }) assert result.success is True assert "score" in result.result assert result.result["score"]["risk"] > 0 @pytest.mark.asyncio async def test_security_scoring(self): """Test security score decreases with findings""" tool_mgr = ToolManager({}) # Clean diff - no issues diff = """diff --git a/good.py b/good.py --- a/good.py +++ b/good.py @@ -1,2 +1,3 @@ +def hello(): + return "world" """ result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": {"text": diff} }) assert result.success is True # Should have high security score (no findings) assert result.result["score"]["security"] >= 70 class TestPRReviewerToolChecklists: """Test checklist generation""" @pytest.mark.asyncio async def test_tests_checklist(self): """Test tests checklist is generated""" tool_mgr = ToolManager({}) diff = """diff --git a/new.py b/new.py --- /dev/null +++ b/new.py @@ -0,0 +1,3 @@ +def new_func(): + pass """ result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": {"text": diff}, "options": {"include_tests_checklist": True} }) assert result.success is True assert len(result.result["tests_checklist"]) > 0 @pytest.mark.asyncio async def test_deploy_checklist(self): """Test deploy checklist is generated""" tool_mgr = ToolManager({}) diff = """diff --git a/app.py b/app.py --- a/app.py +++ b/app.py @@ -1,2 +1,3 @@ +print("hello") """ result = await tool_mgr._pr_reviewer_tool({ "mode": "full_review", "diff": {"text": diff}, "options": {"include_deploy_risks": True} }) assert result.success is True assert len(result.result["deploy_checklist"]) > 0 if __name__ == "__main__": pytest.main([__file__, "-v"])