autoscan / tests /test_models.py
Chris4K's picture
Initial commit v5.0.0.
5248e3b verified
"""Tests for core.models — make_finding, sort_findings, dedup_findings, constants."""
import pytest
from core.models import (
CONFIDENCE_RANK,
FORBIDDEN_FILES,
SEVERITY_RANK,
TOOL_DEFAULT_CONFIDENCE,
dedup_findings,
make_finding,
sort_findings,
)
def _f(**kwargs) -> dict:
defaults = dict(
tool="t", rule="r", severity="WARNING", file="f.py",
line=1, message="m", owasp=["A01"], category="security",
confidence="likely", remediation="",
)
defaults.update(kwargs)
return defaults
class TestMakeFinding:
def test_basic_fields(self):
f = make_finding("bandit", "B101", "WARNING", "test.py", 10, "msg", ["A01"])
assert f["tool"] == "bandit"
assert f["rule"] == "B101"
assert f["severity"] == "WARNING"
assert f["file"] == "test.py"
assert f["line"] == 10
assert f["message"] == "msg"
assert f["owasp"] == ["A01"]
assert f["category"] == "security"
def test_string_owasp_converted_to_list(self):
f = make_finding("t", "r", "INFO", "f.py", 1, "m", "A01")
assert isinstance(f["owasp"], list)
assert f["owasp"] == ["A01"]
def test_default_confidence_from_tool_registry(self):
f = make_finding("pip-audit", "CVE-123", "ERROR", "req.txt", 0, "m", [])
assert f["confidence"] == "confirmed"
def test_default_confidence_gitleaks(self):
f = make_finding("gitleaks", "aws-token", "ERROR", "f.py", 1, "m", [])
assert f["confidence"] == "confirmed"
def test_default_confidence_unknown_tool_is_possible(self):
f = make_finding("unknown-xyz", "r", "INFO", "f.py", 1, "m", [])
assert f["confidence"] == "possible"
def test_explicit_confidence_overrides_default(self):
f = make_finding("pip-audit", "r", "INFO", "f.py", 1, "m", [], confidence="likely")
assert f["confidence"] == "likely"
def test_remediation_from_registry_for_known_rule(self):
# A rule that exists in REMEDIATION should return a non-empty string
from report.remediation import REMEDIATION
if REMEDIATION:
known = next(iter(REMEDIATION))
f = make_finding("t", known, "INFO", "f.py", 1, "m", [])
assert f["remediation"] == REMEDIATION[known]
def test_remediation_empty_for_unknown_rule(self):
f = make_finding("t", "nonexistent-rule-xyz-999", "INFO", "f.py", 1, "m", [])
assert f["remediation"] == ""
def test_explicit_remediation_overrides_registry(self):
f = make_finding("t", "r", "INFO", "f.py", 1, "m", [], remediation="fix it now")
assert f["remediation"] == "fix it now"
def test_category_default_security(self):
f = make_finding("t", "r", "INFO", "f.py", 1, "m", [])
assert f["category"] == "security"
def test_custom_category(self):
f = make_finding("t", "r", "INFO", "f.py", 1, "m", [], category="performance")
assert f["category"] == "performance"
class TestSortFindings:
def test_sorts_error_before_warning_before_info(self):
f1 = _f(severity="INFO")
f2 = _f(severity="ERROR")
f3 = _f(severity="WARNING")
result = sort_findings([f1, f2, f3])
assert [r["severity"] for r in result] == ["ERROR", "WARNING", "INFO"]
def test_sorts_confirmed_before_likely_before_possible(self):
f1 = _f(severity="ERROR", confidence="possible")
f2 = _f(severity="ERROR", confidence="confirmed")
f3 = _f(severity="ERROR", confidence="likely")
result = sort_findings([f1, f2, f3])
assert [r["confidence"] for r in result] == ["confirmed", "likely", "possible"]
def test_sorts_by_file_within_same_severity(self):
f1 = _f(severity="ERROR", confidence="likely", file="z.py", line=1)
f2 = _f(severity="ERROR", confidence="likely", file="a.py", line=1)
result = sort_findings([f1, f2])
assert result[0]["file"] == "a.py"
def test_sorts_by_line_within_same_file(self):
f1 = _f(file="a.py", line=20)
f2 = _f(file="a.py", line=5)
result = sort_findings([f1, f2])
assert result[0]["line"] == 5
def test_empty_list(self):
assert sort_findings([]) == []
def test_single_item(self):
f = _f()
assert sort_findings([f]) == [f]
class TestDedupFindings:
def test_removes_exact_duplicate(self):
f = _f()
result = dedup_findings([f, f.copy()])
assert len(result) == 1
def test_keeps_different_lines(self):
f1 = _f(line=1)
f2 = _f(line=2)
assert len(dedup_findings([f1, f2])) == 2
def test_keeps_different_tools(self):
f1 = _f(tool="bandit", line=1)
f2 = _f(tool="semgrep", line=1)
assert len(dedup_findings([f1, f2])) == 2
def test_keeps_different_files(self):
f1 = _f(file="a.py")
f2 = _f(file="b.py")
assert len(dedup_findings([f1, f2])) == 2
def test_dedup_preserves_order(self):
f1 = _f(line=1)
f2 = _f(line=2)
f3 = _f(line=1) # duplicate of f1
result = dedup_findings([f1, f2, f3])
assert len(result) == 2
assert result[0]["line"] == 1
assert result[1]["line"] == 2
def test_empty_input(self):
assert dedup_findings([]) == []
class TestConstants:
def test_severity_rank_ordering(self):
assert SEVERITY_RANK["ERROR"] < SEVERITY_RANK["WARNING"]
assert SEVERITY_RANK["WARNING"] < SEVERITY_RANK["INFO"]
assert SEVERITY_RANK["HIGH"] == SEVERITY_RANK["ERROR"]
assert SEVERITY_RANK["MEDIUM"] == SEVERITY_RANK["WARNING"]
assert SEVERITY_RANK["LOW"] == SEVERITY_RANK["INFO"]
def test_confidence_rank_ordering(self):
assert CONFIDENCE_RANK["confirmed"] < CONFIDENCE_RANK["likely"]
assert CONFIDENCE_RANK["likely"] < CONFIDENCE_RANK["possible"]
def test_forbidden_files_contains_critical(self):
assert ".env" in FORBIDDEN_FILES
assert "id_rsa" in FORBIDDEN_FILES
assert "credentials.json" in FORBIDDEN_FILES
def test_tool_defaults_confirmed(self):
assert TOOL_DEFAULT_CONFIDENCE["pip-audit"] == "confirmed"
assert TOOL_DEFAULT_CONFIDENCE["gitleaks"] == "confirmed"
assert TOOL_DEFAULT_CONFIDENCE["forbidden-file"] == "confirmed"
def test_tool_defaults_possible(self):
assert TOOL_DEFAULT_CONFIDENCE["detect-secrets"] == "possible"