audit-labs/control-coverage
Control coverage and blind-spot analysis for audit evidence.
clone: git clone https://gitbay.org/audit-labs/control-coverage.git
v0.1.0: 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