mirror of
https://github.com/elder-plinius/OBLITERATUS.git
synced 2026-08-17 16:37:30 +02:00
test: constrain risk overlays to measured coverage
This commit is contained in:
+4
-1
@@ -83,7 +83,10 @@ module under `obliteratus/`; every production file must belong to exactly one
|
||||
named contract surface with an owner, contract types, and existing test files.
|
||||
Adding a module without updating that graph fails `check_test_risk_map.py`.
|
||||
Targeted risk-module entries add stricter conditional-gate and critical-path
|
||||
requirements on top of the exhaustive contract ownership layer.
|
||||
requirements on top of the exhaustive contract ownership layer. They are
|
||||
limited to the measured `obliteratus` coverage root so normalized CI evidence
|
||||
can account for every targeted module; top-level `app.py` remains owned and is
|
||||
exercised by its explicit operator-UI contract test.
|
||||
|
||||
Each behavior-changing PR must add a focused regression or contract test,
|
||||
relevant negative/boundary coverage, and propagation/runtime evidence for
|
||||
|
||||
@@ -67,7 +67,6 @@
|
||||
"prerequisites": "locked spaces extra; no public listener",
|
||||
"expected_cost": "under 10 runner-minutes",
|
||||
"coverage_paths": [
|
||||
"app.py",
|
||||
"obliteratus/interactive.py",
|
||||
"obliteratus/local_ui.py",
|
||||
"obliteratus/ui_watchtower.py"
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
"schema_version": 2,
|
||||
"owner": "OBLITERATUS maintainers",
|
||||
"source_inventory": {
|
||||
"roots": ["app.py", "obliteratus"]
|
||||
"roots": ["app.py", "obliteratus"],
|
||||
"coverage_roots": ["obliteratus"]
|
||||
},
|
||||
"contract_surfaces": [
|
||||
{
|
||||
@@ -226,13 +227,6 @@
|
||||
}
|
||||
],
|
||||
"modules": [
|
||||
{
|
||||
"path": "app.py",
|
||||
"risk_class": "conditional-runtime",
|
||||
"risk": "optional application construction, UI wiring, authentication, and listener safety",
|
||||
"required_tests": ["tests/conditional/test_operator_ui.py"],
|
||||
"conditional_gates": ["operator-ui"]
|
||||
},
|
||||
{
|
||||
"path": "obliteratus/cli.py",
|
||||
"risk_class": "cpu-contract",
|
||||
|
||||
@@ -117,6 +117,37 @@ def _inventory_sources(
|
||||
return sources
|
||||
|
||||
|
||||
def _coverage_roots(
|
||||
inventory: object,
|
||||
*,
|
||||
project_root: Path,
|
||||
errors: list[str],
|
||||
) -> tuple[str, ...]:
|
||||
if not isinstance(inventory, dict):
|
||||
return ()
|
||||
values = inventory.get("coverage_roots")
|
||||
if not isinstance(values, list) or not values:
|
||||
errors.append("test risk map source_inventory requires non-empty coverage_roots")
|
||||
return ()
|
||||
if len(values) != len(set(map(str, values))):
|
||||
errors.append("test risk map source_inventory has duplicate coverage_roots")
|
||||
|
||||
roots: list[str] = []
|
||||
for value in values:
|
||||
if not isinstance(value, str) or not value or Path(value).is_absolute():
|
||||
errors.append(f"source_inventory has invalid coverage root: {value!r}")
|
||||
continue
|
||||
if not (project_root / value).exists():
|
||||
errors.append(f"source_inventory coverage root does not exist: {value}")
|
||||
continue
|
||||
roots.append(value.rstrip("/"))
|
||||
return tuple(roots)
|
||||
|
||||
|
||||
def _is_in_coverage_scope(path: str, roots: tuple[str, ...]) -> bool:
|
||||
return any(path == root or path.startswith(f"{root}/") for root in roots)
|
||||
|
||||
|
||||
def _validate_contract_surfaces(
|
||||
surfaces: object,
|
||||
*,
|
||||
@@ -208,8 +239,14 @@ def validate(risk_path: Path, quality_path: Path, conditional_path: Path) -> lis
|
||||
if not isinstance(risk.get("owner"), str) or not risk["owner"].strip():
|
||||
errors.append("test risk map requires a non-empty owner")
|
||||
|
||||
source_inventory = risk.get("source_inventory")
|
||||
inventory_sources = _inventory_sources(
|
||||
risk.get("source_inventory"),
|
||||
source_inventory,
|
||||
project_root=project_root,
|
||||
errors=errors,
|
||||
)
|
||||
coverage_roots = _coverage_roots(
|
||||
source_inventory,
|
||||
project_root=project_root,
|
||||
errors=errors,
|
||||
)
|
||||
@@ -247,6 +284,8 @@ def validate(risk_path: Path, quality_path: Path, conditional_path: Path) -> lis
|
||||
errors.append(f"risk module maps missing source path: {path}")
|
||||
if path not in source_contracts:
|
||||
errors.append(f"risk module is missing a contract owner: {path}")
|
||||
if not _is_in_coverage_scope(path, coverage_roots):
|
||||
errors.append(f"risk module is outside measured coverage roots: {path}")
|
||||
risk_class = module.get("risk_class")
|
||||
if not isinstance(risk_class, str) or risk_class not in RISK_CLASSES:
|
||||
errors.append(f"risk module {path} has invalid risk_class")
|
||||
|
||||
@@ -162,6 +162,12 @@ def test_risk_map_rejects_legacy_schema_and_invalid_inventory_roots(tmp_path):
|
||||
risk = json.loads((ROOT / "ci" / "test-risk-map.json").read_text())
|
||||
risk["schema_version"] = 1
|
||||
risk["source_inventory"]["roots"] = ["missing-source-root", "/absolute/source.py"]
|
||||
risk["source_inventory"]["coverage_roots"] = [
|
||||
"obliteratus",
|
||||
"obliteratus",
|
||||
"missing-coverage-root",
|
||||
"/absolute/coverage",
|
||||
]
|
||||
|
||||
errors = check_test_risk_map.validate(
|
||||
_write(tmp_path / "risk.json", risk),
|
||||
@@ -172,6 +178,30 @@ def test_risk_map_rejects_legacy_schema_and_invalid_inventory_roots(tmp_path):
|
||||
assert "test risk map schema_version must be 2" in errors
|
||||
assert "source_inventory root does not exist: missing-source-root" in errors
|
||||
assert "source_inventory has invalid root: '/absolute/source.py'" in errors
|
||||
assert "test risk map source_inventory has duplicate coverage_roots" in errors
|
||||
assert "source_inventory coverage root does not exist: missing-coverage-root" in errors
|
||||
assert "source_inventory has invalid coverage root: '/absolute/coverage'" in errors
|
||||
|
||||
|
||||
def test_targeted_risk_module_must_be_inside_measured_coverage_roots(tmp_path):
|
||||
risk = json.loads((ROOT / "ci" / "test-risk-map.json").read_text())
|
||||
risk["modules"].append(
|
||||
{
|
||||
"path": "app.py",
|
||||
"risk_class": "conditional-runtime",
|
||||
"risk": "optional application construction",
|
||||
"required_tests": ["tests/conditional/test_operator_ui.py"],
|
||||
"conditional_gates": [],
|
||||
}
|
||||
)
|
||||
|
||||
errors = check_test_risk_map.validate(
|
||||
_write(tmp_path / "risk.json", risk),
|
||||
ROOT / "ci" / "test-quality-policy.json",
|
||||
ROOT / "ci" / "conditional-test-policy.json",
|
||||
)
|
||||
|
||||
assert "risk module is outside measured coverage roots: app.py" in errors
|
||||
|
||||
|
||||
def test_loader_rejects_unreadable_and_nonobject_documents(tmp_path):
|
||||
|
||||
Reference in New Issue
Block a user