audit-labs/control-coverage

Control coverage and blind-spot analysis for audit evidence.

clone: git clone https://gitbay.org/audit-labs/control-coverage.git

main: tests/test_coverage.py · raw

 1"""Tests for the coverage engine — the heart of the tool."""
 2
 3from pathlib import Path
 4
 5from control_coverage import catalog, corpus, scope
 6from control_coverage.coverage import (
 7    ASSERTED,
 8    FAILING,
 9    OUT_OF_SCOPE,
10    SUPPORTED,
11    UNADDRESSED,
12    evaluate,
13)
14
15FIXTURES = Path(__file__).parent / "fixtures"
16GITHUB = FIXTURES / "github_audit_acme_2026-01-01.json"
17AWS = FIXTURES / "aws_audit_acme_2026-01-01.json"
18
19
20def _state(fc, control_id):
21    return next(r.state for r in fc.results if r.control.id == control_id)
22
23
24def _report(frameworks, scp=None):
25    obs = corpus.load_corpus([GITHUB, AWS])
26    cats = catalog.load_frameworks(frameworks)
27    return evaluate(cats, obs, scope=scp)
28
29
30def test_worst_wins_and_blind_spots_for_soc2():
31    fc = _report(["SOC2"]).frameworks[0]
32    assert _state(fc, "CC6.1") == SUPPORTED  # two passes across github + aws
33    assert _state(fc, "CC6.3") == FAILING  # one fail
34    assert _state(fc, "CC6.6") == FAILING
35    assert _state(fc, "CC8.1") == ASSERTED  # only not_applicable observations
36    assert _state(fc, "CC1.1") == UNADDRESSED  # nothing maps here
37
38
39def test_soc2_rollup_numbers():
40    fc = _report(["SOC2"]).frameworks[0]
41    assert fc.in_scope == 61  # full five-category Trust Services Criteria
42    assert fc.supported == 3
43    assert fc.counts[FAILING] == 2
44    assert fc.counts[ASSERTED] == 1
45    assert fc.addressed == 6
46    assert len(fc.blind_spots) == 55
47    assert fc.coverage_pct == 9.8  # 6 / 61
48    assert fc.assured_pct == 4.9  # 3 / 61
49
50
51def test_scope_marks_controls_out_of_scope_with_reason():
52    scp = scope.load(FIXTURES / "scope.yaml")
53    fc = next(f for f in _report(["ISO"], scp).frameworks if f.catalog.framework == "ISO")
54    assert _state(fc, "A.7.1") == OUT_OF_SCOPE
55    assert _state(fc, "A.5.7") == OUT_OF_SCOPE
56    assert fc.in_scope == 91  # 93 Annex A controls minus 2 exclusions
57    excluded = next(r for r in fc.results if r.control.id == "A.7.1")
58    assert "cloud-hosted" in excluded.exclusion_reason
59
60
61def test_orphan_only_flags_loaded_frameworks():
62    # CC6.99 is a SOC2 typo; NIST codes are cited but NIST is not loaded here.
63    report = _report(["SOC2", "ISO"])
64    assert "SOC2:CC6.99" in report.orphan_codes
65    assert not any(c.startswith("NIST:") for c in report.orphan_codes)
66
67
68def test_family_exclusion_marks_whole_category_out_of_scope():
69    from control_coverage.scope import Scope
70
71    scp = Scope(family_exclusions={("SOC2", "Privacy"): "Not in the SOC 2 audit scope."})
72    fc = _report(["SOC2"], scp).frameworks[0]
73    privacy = [r for r in fc.results if r.control.family == "Privacy"]
74    assert privacy  # the catalog has Privacy controls
75    assert all(r.state == OUT_OF_SCOPE for r in privacy)
76    assert all("audit scope" in r.exclusion_reason for r in privacy)
77    # Security (Common Criteria) controls remain in scope.
78    cc61 = next(r for r in fc.results if r.control.id == "CC6.1")
79    assert cc61.state != OUT_OF_SCOPE
80
81
82def test_owner_is_attached_from_scope():
83    scp = scope.load(FIXTURES / "scope.yaml")
84    fc = _report(["SOC2"], scp).frameworks[0]
85    owner = next(r.owner for r in fc.results if r.control.id == "CC6.1")
86    assert owner == "platform-team"
87
88
89def test_source_count_reflects_distinct_packages():
90    report = _report(["SOC2"])
91    assert report.source_count == 2