"""Tests for confidence scoring module.""" import pytest from src.core.models import ScanResult, Issue, IssueCategory, SeverityLevel from src.reporting.confidence import ConfidenceScorer class TestConfidenceScorer: """Tests for ConfidenceScorer.""" def test_calculate_perfect_score(self, clean_python_code): """Test that clean code gets a high score.""" scorer = ConfidenceScorer() result = ScanResult(files_scanned=1, target_path="/test") score = scorer.calculate(result) assert score == 100 def test_calculate_with_issues(self, mock_scan_result): """Test score calculation with issues.""" scorer = ConfidenceScorer() score = scorer.calculate(mock_scan_result) assert score < 100 assert score >= 0 def test_security_issues_reduce_score_more(self): """Test that security issues reduce score more than other issues.""" scorer = ConfidenceScorer() result_security = ScanResult(files_scanned=1, target_path="/test") result_security.add_issue(Issue( severity=SeverityLevel.HIGH, category=IssueCategory.SECURITY, file_path="/test.py", line_number=1, message="Security issue", scanner_name="test", )) result_quality = ScanResult(files_scanned=1, target_path="/test") result_quality.add_issue(Issue( severity=SeverityLevel.HIGH, category=IssueCategory.CODE_QUALITY, file_path="/test.py", line_number=1, message="Quality issue", scanner_name="test", )) security_score = scorer.calculate(result_security) quality_score = scorer.calculate(result_quality) assert security_score < quality_score def test_critical_issues_reduce_score_more(self): """Test that critical issues reduce score more.""" scorer = ConfidenceScorer() result_critical = ScanResult(files_scanned=1, target_path="/test") result_critical.add_issue(Issue( severity=SeverityLevel.CRITICAL, category=IssueCategory.SECURITY, file_path="/test.py", line_number=1, message="Critical issue", scanner_name="test", )) result_high = ScanResult(files_scanned=1, target_path="/test") result_high.add_issue(Issue( severity=SeverityLevel.HIGH, category=IssueCategory.SECURITY, file_path="/test.py", line_number=1, message="High issue", scanner_name="test", )) critical_score = scorer.calculate(result_critical) high_score = scorer.calculate(result_high) assert critical_score < high_score def test_get_score_breakdown(self, mock_scan_result): """Test score breakdown generation.""" scorer = ConfidenceScorer() breakdown = scorer.get_score_breakdown(mock_scan_result) assert "base_score" in breakdown assert "total_deductions" in breakdown assert "final_score" in breakdown assert "issues_by_category" in breakdown assert "issues_by_severity" in breakdown def test_get_score_grade(self): """Test score grade calculation.""" scorer = ConfidenceScorer() assert scorer.get_score_grade(95) == "A+" assert scorer.get_score_grade(90) == "A" assert scorer.get_score_grade(85) == "A-" assert scorer.get_score_grade(80) == "B+" assert scorer.get_score_grade(75) == "B" assert scorer.get_score_grade(70) == "B-" assert scorer.get_score_grade(65) == "C+" assert scorer.get_score_grade(60) == "C" assert scorer.get_score_grade(55) == "C-" assert scorer.get_score_grade(50) == "D+" assert scorer.get_score_grade(45) == "D" assert scorer.get_score_grade(40) == "D-" assert scorer.get_score_grade(30) == "F" def test_get_score_description(self): """Test score description generation.""" scorer = ConfidenceScorer() desc_90 = scorer.get_score_description(90) assert "Excellent" in desc_90 desc_75 = scorer.get_score_description(75) assert "Good" in desc_75 desc_50 = scorer.get_score_description(50) assert "Poor" in desc_50 desc_25 = scorer.get_score_description(25) assert "Critical" in desc_25 def test_empty_result_gets_100(self): """Test that empty scan result gets 100 score.""" scorer = ConfidenceScorer() result = ScanResult(files_scanned=0, target_path="/test") score = scorer.calculate(result) assert score == 100 def test_score_never_negative(self): """Test that score never goes below 0.""" scorer = ConfidenceScorer() result = ScanResult(files_scanned=1, target_path="/test") for _ in range(100): result.add_issue(Issue( severity=SeverityLevel.CRITICAL, category=IssueCategory.SECURITY, file_path="/test.py", line_number=1, message="Critical issue", scanner_name="test", )) score = scorer.calculate(result) assert score >= 0 def test_score_never_exceeds_100(self): """Test that score never goes above 100.""" scorer = ConfidenceScorer() result = ScanResult(files_scanned=10, target_path="/test") score = scorer.calculate(result) assert score <= 100 class TestIssueModel: """Tests for Issue data model.""" def test_issue_to_dict(self): """Test issue serialization to dictionary.""" issue = Issue( severity=SeverityLevel.HIGH, category=IssueCategory.SECURITY, file_path="/test.py", line_number=10, message="Test issue", suggestion="Fix this", scanner_name="test", ) data = issue.to_dict() assert data["severity"] == "high" assert data["category"] == "security" assert data["file_path"] == "/test.py" assert data["line_number"] == 10 assert data["message"] == "Test issue" assert data["suggestion"] == "Fix this" assert data["scanner_name"] == "test" class TestScanResultModel: """Tests for ScanResult data model.""" def test_add_issue(self): """Test adding issues to scan result.""" result = ScanResult(files_scanned=1, target_path="/test") issue = Issue( severity=SeverityLevel.LOW, category=IssueCategory.STYLE, file_path="/test.py", line_number=1, message="Style issue", scanner_name="test", ) result.add_issue(issue) assert len(result.issues) == 1 assert result.issues[0] == issue def test_add_warning(self): """Test adding warnings to scan result.""" result = ScanResult(files_scanned=1, target_path="/test") result.add_warning("Test warning") assert len(result.warnings) == 1 assert result.warnings[0] == "Test warning" def test_filter_by_severity(self): """Test filtering issues by severity.""" result = ScanResult(files_scanned=1, target_path="/test") result.add_issue(Issue( severity=SeverityLevel.LOW, category=IssueCategory.STYLE, file_path="/test.py", line_number=1, message="Low issue", scanner_name="test", )) result.add_issue(Issue( severity=SeverityLevel.CRITICAL, category=IssueCategory.SECURITY, file_path="/test.py", line_number=2, message="Critical issue", scanner_name="test", )) filtered = result.filter_by_severity(SeverityLevel.HIGH) assert len(filtered.issues) == 1 assert filtered.issues[0].severity == SeverityLevel.CRITICAL def test_get_summary(self): """Test getting scan summary.""" result = ScanResult(files_scanned=2, target_path="/test") result.add_issue(Issue( severity=SeverityLevel.HIGH, category=IssueCategory.SECURITY, file_path="/test.py", line_number=1, message="Issue 1", scanner_name="test", )) result.add_issue(Issue( severity=SeverityLevel.LOW, category=IssueCategory.STYLE, file_path="/test.py", line_number=2, message="Issue 2", scanner_name="test", )) summary = result.get_summary() assert summary["files_scanned"] == 2 assert summary["total_issues"] == 2 assert "high" in summary["issues_by_severity"] assert "low" in summary["issues_by_severity"] assert "security" in summary["issues_by_category"] assert "style" in summary["issues_by_category"] def test_to_dict(self): """Test scan result serialization to dictionary.""" result = ScanResult(files_scanned=1, target_path="/test") result.add_issue(Issue( severity=SeverityLevel.MEDIUM, category=IssueCategory.CODE_QUALITY, file_path="/test.py", line_number=5, message="Test issue", scanner_name="test", )) data = result.to_dict() assert data["files_scanned"] == 1 assert data["target_path"] == "/test" assert len(data["issues"]) == 1 assert data["issues"][0]["severity"] == "medium"