|
2 | 2 |
|
3 | 3 | The contract runs in both directions: every non-deferred entry in :data:`REQUIREMENTS` must be |
4 | 4 | exercised by at least one test, and every test in the suite must carry at least one |
5 | | -`@requirement(...)` mark referencing a manifest entry. Test modules are imported directly |
| 5 | +`@requirement(...)` mark referencing a manifest entry. Deferral reasons that point at coverage |
| 6 | +elsewhere in the repo must point at paths that exist. Test modules are imported directly |
6 | 7 | (rather than relying on pytest collection) so the check holds even when only this file is run. |
7 | 8 | """ |
8 | 9 |
|
9 | 10 | import importlib |
| 11 | +import re |
10 | 12 | from pathlib import Path |
11 | 13 | from types import ModuleType |
12 | 14 |
|
|
15 | 17 | from tests.interaction._requirements import REQUIREMENTS, Requirement, covered_by, requirement |
16 | 18 |
|
17 | 19 | _SUITE_ROOT = Path(__file__).parent |
| 20 | +_REPO_ROOT = _SUITE_ROOT.parent.parent |
| 21 | + |
| 22 | +# Repo paths cited inside deferral reasons ("Covered by tests/... "). |
| 23 | +_CITED_PATH = re.compile(r"(?:tests|src)/[\w./-]*\w") |
18 | 24 |
|
19 | 25 | # Tests that exercise the suite's own helpers rather than an interaction-model behaviour. |
20 | 26 | # Anything listed here is exempt from the every-test-has-a-requirement check. |
@@ -70,6 +76,18 @@ def test_every_test_exercises_a_requirement() -> None: |
70 | 76 | assert not stale_exemptions, f"Harness self-test exemptions that no longer exist: {stale_exemptions}" |
71 | 77 |
|
72 | 78 |
|
| 79 | +def test_deferral_reasons_cite_existing_paths() -> None: |
| 80 | + """Every repo path named in a deferral reason exists, so coverage pointers cannot rot.""" |
| 81 | + missing = sorted( |
| 82 | + f"{requirement_id}: {cited}" |
| 83 | + for requirement_id, spec in REQUIREMENTS.items() |
| 84 | + if spec.deferred is not None |
| 85 | + for cited in _CITED_PATH.findall(spec.deferred) |
| 86 | + if not (_REPO_ROOT / cited).exists() |
| 87 | + ) |
| 88 | + assert not missing, f"Deferral reasons citing paths that do not exist: {missing}" |
| 89 | + |
| 90 | + |
73 | 91 | def test_unknown_requirement_id_is_rejected() -> None: |
74 | 92 | """Marking a test with an ID that is not in the manifest fails at decoration time.""" |
75 | 93 | with pytest.raises(KeyError, match="Unknown requirement id 'tools:call:does-not-exist'"): |
|
0 commit comments