Skip to main content
Glama

Adversary MCP Server

by brettbergin
test_scan_result_focused.py17.4 kB
"""Focused tests to improve coverage for ScanResult entity methods.""" import pytest from adversary_mcp_server.domain.entities.scan_request import ScanRequest from adversary_mcp_server.domain.entities.scan_result import ScanResult from adversary_mcp_server.domain.entities.threat_match import ThreatMatch from adversary_mcp_server.domain.value_objects.confidence_score import ConfidenceScore from adversary_mcp_server.domain.value_objects.file_path import FilePath from adversary_mcp_server.domain.value_objects.scan_context import ScanContext from adversary_mcp_server.domain.value_objects.scan_metadata import ScanMetadata from adversary_mcp_server.domain.value_objects.severity_level import SeverityLevel class TestScanResultFocused: """Tests targeting uncovered code paths in ScanResult.""" def test_add_threat_method(self): """Test add_threat method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Start with empty result result = ScanResult.create_empty(request) assert len(result.threats) == 0 # Add a threat threat = ThreatMatch( rule_id="new-threat", rule_name="New Threat", description="Newly added threat", category="disclosure", severity=SeverityLevel.from_string("low"), file_path=file_path, line_number=15, column_number=3, code_snippet="password = 'secret'", confidence=ConfidenceScore(0.6), ) new_result = result.add_threat(threat) # Original should be unchanged assert len(result.threats) == 0 # New result should have the threat assert len(new_result.threats) == 1 assert new_result.threats[0] == threat def test_add_threats_method(self): """Test add_threats method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Start with empty result result = ScanResult.create_empty(request) # Add multiple threats threats = [ ThreatMatch( rule_id="threat1", rule_name="Threat 1", description="First threat", category="injection", severity=SeverityLevel.from_string("high"), file_path=file_path, line_number=10, column_number=1, code_snippet="sql injection", confidence=ConfidenceScore(0.8), ), ThreatMatch( rule_id="threat2", rule_name="Threat 2", description="Second threat", category="xss", severity=SeverityLevel.from_string("medium"), file_path=file_path, line_number=20, column_number=5, code_snippet="xss vulnerability", confidence=ConfidenceScore(0.7), ), ] new_result = result.add_threats(threats) # Original should be unchanged assert len(result.threats) == 0 # New result should have both threats assert len(new_result.threats) == 2 def test_add_threats_empty_list(self): """Test add_threats method with empty list.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) result = ScanResult.create_empty(request) # Adding empty list should return same result new_result = result.add_threats([]) assert new_result is result # Should return same instance def test_apply_validation_results_method(self): """Test apply_validation_results method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Original threat original_threat = ThreatMatch( rule_id="original", rule_name="Original Threat", description="Original threat", category="injection", severity=SeverityLevel.from_string("high"), file_path=file_path, line_number=10, column_number=1, code_snippet="original code", confidence=ConfidenceScore(0.7), ) result = ScanResult.create_from_threats(request, [original_threat]) # Validated threat with same fingerprint validated_threat = ThreatMatch( rule_id="validated", rule_name="Validated Threat", description="Validated threat", category="injection", severity=SeverityLevel.from_string("high"), file_path=file_path, line_number=10, # Same location = same fingerprint column_number=1, code_snippet="validated code", confidence=ConfidenceScore(0.9), # Higher confidence ) validated_result = result.apply_validation_results([validated_threat]) assert validated_result.validation_applied is True assert len(validated_result.threats) == 1 assert validated_result.threats[0].rule_id == "validated" def test_merge_with_method(self): """Test merge_with method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # First result threat1 = ThreatMatch( rule_id="threat1", rule_name="Threat 1", description="First threat", category="injection", severity=SeverityLevel.from_string("high"), file_path=file_path, line_number=10, column_number=1, code_snippet="threat 1 code", confidence=ConfidenceScore(0.8), ) result1 = ScanResult.create_from_threats(request, [threat1]) # Second result with different threat threat2 = ThreatMatch( rule_id="threat2", rule_name="Threat 2", description="Second threat", category="xss", severity=SeverityLevel.from_string("medium"), file_path=file_path, line_number=20, column_number=1, code_snippet="threat 2 code", confidence=ConfidenceScore(0.7), ) result2 = ScanResult.create_from_threats(request, [threat2]) # Merge results merged = result1.merge_with(result2) assert len(merged.threats) == 2 threat_ids = {t.rule_id for t in merged.threats} assert "threat1" in threat_ids assert "threat2" in threat_ids def test_merge_with_same_scan_id_error(self): """Test merge_with method with different scan IDs.""" file_path = FilePath.from_string("examples/vulnerable_python.py") # Different metadata with different scan IDs metadata1 = ScanMetadata.for_file_scan(requester="user1") metadata2 = ScanMetadata.for_file_scan(requester="user2") context1 = ScanContext(target_path=file_path, metadata=metadata1) context2 = ScanContext(target_path=file_path, metadata=metadata2) request1 = ScanRequest(context=context1) request2 = ScanRequest(context=context2) result1 = ScanResult.create_empty(request1) result2 = ScanResult.create_empty(request2) # Should raise error for different scan IDs with pytest.raises( ValueError, match="Cannot merge scan results from different scan operations" ): result1.merge_with(result2) def test_get_overall_risk_score_method(self): """Test get_overall_risk_score method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Empty result empty_result = ScanResult.create_empty(request) assert empty_result.get_overall_risk_score() == 0.0 # Result with threats threats = [ ThreatMatch( rule_id="high-risk", rule_name="High Risk", description="High risk threat", category="injection", severity=SeverityLevel.from_string("critical"), file_path=file_path, line_number=10, column_number=1, code_snippet="critical code", confidence=ConfidenceScore(0.9), ), ThreatMatch( rule_id="low-risk", rule_name="Low Risk", description="Low risk threat", category="info", severity=SeverityLevel.from_string("low"), file_path=file_path, line_number=20, column_number=1, code_snippet="low priority code", confidence=ConfidenceScore(0.5), ), ] result = ScanResult.create_from_threats(request, threats) risk_score = result.get_overall_risk_score() assert 0.0 <= risk_score <= 10.0 assert risk_score > 0 # Should be positive with threats def test_get_security_posture_method(self): """Test get_security_posture method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Clean result clean_result = ScanResult.create_empty(request) posture = clean_result.get_security_posture() assert posture in ["Excellent", "Good", "Fair", "Poor", "Critical"] # Critical threat critical_threat = ThreatMatch( rule_id="critical", rule_name="Critical Threat", description="Critical issue", category="injection", severity=SeverityLevel.from_string("critical"), file_path=file_path, line_number=10, column_number=1, code_snippet="critical code", confidence=ConfidenceScore(0.9), ) critical_result = ScanResult.create_from_threats(request, [critical_threat]) critical_posture = critical_result.get_security_posture() assert critical_posture == "Critical" def test_needs_immediate_attention_method(self): """Test needs_immediate_attention method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Clean result clean_result = ScanResult.create_empty(request) assert clean_result.needs_immediate_attention() is False # Critical threat critical_threat = ThreatMatch( rule_id="critical", rule_name="Critical Threat", description="Critical issue", category="injection", severity=SeverityLevel.from_string("critical"), file_path=file_path, line_number=10, column_number=1, code_snippet="critical code", confidence=ConfidenceScore(0.9), ) critical_result = ScanResult.create_from_threats(request, [critical_threat]) assert critical_result.needs_immediate_attention() is True def test_clear_caches_method(self): """Test clear_caches method.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) threat = ThreatMatch( rule_id="cache-test", rule_name="Cache Test", description="Testing cache clearing", category="injection", severity=SeverityLevel.from_string("high"), file_path=file_path, line_number=10, column_number=1, code_snippet="cache test code", confidence=ConfidenceScore(0.8), ) result = ScanResult.create_from_threats(request, [threat]) # Access cached properties to populate caches stats = result.get_statistics() threats_by_file = result.get_threats_by_file() high_priority = result.get_high_priority_threats() # Clear caches result.clear_caches() # Accessing again should recompute (no way to directly test cache state, # but this ensures the method doesn't crash) new_stats = result.get_statistics() assert new_stats is not None def test_get_threats_by_severity_enum_type_safety(self): """Test that get_threats_by_severity only accepts SeverityLevel enums, not strings.""" file_path = FilePath.from_string("examples/vulnerable_python.py") metadata = ScanMetadata.for_file_scan(requester="test-user") context = ScanContext(target_path=file_path, metadata=metadata) request = ScanRequest(context=context) # Create threats with different severities threats = [ ThreatMatch( rule_id="critical-threat", rule_name="Critical Threat", description="Critical issue", category="injection", severity=SeverityLevel.from_string("critical"), file_path=file_path, line_number=10, column_number=1, code_snippet="critical code", confidence=ConfidenceScore(0.9), ), ThreatMatch( rule_id="high-threat", rule_name="High Threat", description="High priority issue", category="xss", severity=SeverityLevel.from_string("high"), file_path=file_path, line_number=20, column_number=1, code_snippet="high priority code", confidence=ConfidenceScore(0.8), ), ThreatMatch( rule_id="medium-threat", rule_name="Medium Threat", description="Medium priority issue", category="csrf", severity=SeverityLevel.from_string("medium"), file_path=file_path, line_number=30, column_number=1, code_snippet="medium priority code", confidence=ConfidenceScore(0.7), ), ] result = ScanResult.create_from_threats(request, threats) # Test that passing proper SeverityLevel enum works critical_threats = result.get_threats_by_severity( SeverityLevel.from_string("critical") ) assert len(critical_threats) == 1 assert critical_threats[0].rule_id == "critical-threat" high_threats = result.get_threats_by_severity(SeverityLevel.from_string("high")) assert len(high_threats) == 1 assert high_threats[0].rule_id == "high-threat" medium_threats = result.get_threats_by_severity( SeverityLevel.from_string("medium") ) assert len(medium_threats) == 1 assert medium_threats[0].rule_id == "medium-threat" # Test that passing strings (the bug we fixed) returns empty list # because string comparison with SeverityLevel enum will always fail string_critical_threats = result.get_threats_by_severity("critical") # type: ignore assert len(string_critical_threats) == 0 # This was the bug - no matches! string_high_threats = result.get_threats_by_severity("high") # type: ignore assert len(string_high_threats) == 0 string_medium_threats = result.get_threats_by_severity("medium") # type: ignore assert len(string_medium_threats) == 0 # Verify that SeverityLevel.CRITICAL.value (string) also fails value_critical_threats = result.get_threats_by_severity(SeverityLevel.CRITICAL.value) # type: ignore assert len(value_critical_threats) == 0 # This was the exact bug in MCP server!

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/brettbergin/adversary-mcp-server'

If you have feedback or need assistance with the MCP directory API, please join our Discord server