mirror of
https://github.com/rookiestar28/ComfyUI-OpenClaw.git
synced 2026-08-14 00:48:07 +00:00
test(governance): validate test debt metadata
This commit is contained in:
@@ -383,33 +383,36 @@ cleanup_precommit_snapshots
|
||||
# edits cannot hide until deep in the pre-push unit suite.
|
||||
"$VENV_PY" scripts/check_openapi_sync.py
|
||||
|
||||
echo "[pre-push] 3/8 coverage governance check"
|
||||
echo "[pre-push] 3/9 coverage governance check"
|
||||
"$VENV_PY" scripts/verify_quality_governance.py
|
||||
|
||||
echo "[pre-push] 4/8 backend unit tests"
|
||||
echo "[pre-push] 4/9 test debt governance check"
|
||||
"$VENV_PY" scripts/verify_test_debt_governance.py
|
||||
|
||||
echo "[pre-push] 5/9 backend unit tests"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_pre_push_unit" \
|
||||
"$VENV_PY" scripts/run_unittests.py --start-dir tests --pattern "test_*.py" --enforce-skip-policy tests/skip_policy.json
|
||||
|
||||
if [ -n "${OPENCLAW_IMPL_RECORD_PATH:-}" ]; then
|
||||
echo "[pre-push] 4.5/8 implementation record lint (strict)"
|
||||
echo "[pre-push] 5.5/9 implementation record lint (strict)"
|
||||
"$VENV_PY" scripts/lint_implementation_record.py --path "$OPENCLAW_IMPL_RECORD_PATH" --strict
|
||||
fi
|
||||
|
||||
echo "[pre-push] 5/8 backend real E2E lanes (R122/R123)"
|
||||
echo "[pre-push] 6/9 backend real E2E lanes (R122/R123)"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_pre_push_backend_e2e_real" \
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r122_real_backend_lane --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_pre_push_backend_e2e_real" \
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r123_real_backend_model_list_lane --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
|
||||
echo "[pre-push] 6/8 R121 retry partition contract"
|
||||
echo "[pre-push] 7/9 R121 retry partition contract"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_pre_push_retry_partition" \
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r121_retry_partition_contract --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
|
||||
echo "[pre-push] 7/8 R118 adversarial gate (adaptive: smoke/extended)"
|
||||
echo "[pre-push] 8/9 R118 adversarial gate (adaptive: smoke/extended)"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_pre_push_adversarial" \
|
||||
"$VENV_PY" scripts/run_adversarial_gate.py --profile auto --seed 42 --artifact-dir .tmp/adversarial
|
||||
|
||||
echo "[pre-push] 8/8 npm test (Playwright)"
|
||||
echo "[pre-push] 9/9 npm test (Playwright)"
|
||||
npm test
|
||||
|
||||
echo "[pre-push] PASS"
|
||||
|
||||
@@ -7,7 +7,6 @@ from fnmatch import fnmatch
|
||||
from pathlib import Path
|
||||
from typing import Any, Iterable
|
||||
|
||||
|
||||
REQUIRED_HOTSPOT_FAMILIES = (
|
||||
"safe_io",
|
||||
"security_boundary",
|
||||
@@ -42,8 +41,10 @@ def _validate_hotspot_family(
|
||||
seen_ids.add(family_id)
|
||||
|
||||
paths = family.get("paths")
|
||||
if not isinstance(paths, list) or not paths or not all(
|
||||
isinstance(path, str) and path.strip() for path in paths
|
||||
if (
|
||||
not isinstance(paths, list)
|
||||
or not paths
|
||||
or not all(isinstance(path, str) and path.strip() for path in paths)
|
||||
):
|
||||
failures.append(
|
||||
f"coverage policy: hotspot family {family_id} must define a non-empty paths list"
|
||||
@@ -93,7 +94,9 @@ def load_and_validate_policy(path: Path) -> tuple[dict[str, Any] | None, list[st
|
||||
f"coverage policy: stage {stage_id} missing numeric min_fail_under"
|
||||
)
|
||||
continue
|
||||
stages.append(CoverageStage(stage_id=stage_id, min_fail_under=float(min_fail_under)))
|
||||
stages.append(
|
||||
CoverageStage(stage_id=stage_id, min_fail_under=float(min_fail_under))
|
||||
)
|
||||
|
||||
for previous, current in zip(stages, stages[1:]):
|
||||
if current.min_fail_under <= previous.min_fail_under:
|
||||
@@ -243,7 +246,9 @@ def summarize_coverage(
|
||||
summary = files[file_path].get("summary", {})
|
||||
covered_lines += int(summary.get("covered_lines", 0))
|
||||
num_statements += int(summary.get("num_statements", 0))
|
||||
percent = round((covered_lines / num_statements) * 100, 2) if num_statements else 0.0
|
||||
percent = (
|
||||
round((covered_lines / num_statements) * 100, 2) if num_statements else 0.0
|
||||
)
|
||||
hotspot_summary[family_id] = {
|
||||
"matched_files": matched_files,
|
||||
"missing_paths": missing_paths,
|
||||
@@ -263,7 +268,9 @@ def summarize_coverage(
|
||||
"current_stage_fail_under": current_stage_threshold(policy),
|
||||
"next_stage": next_policy_stage["id"] if next_policy_stage else None,
|
||||
"next_stage_fail_under": (
|
||||
float(next_policy_stage["min_fail_under"]) if next_policy_stage else None
|
||||
float(next_policy_stage["min_fail_under"])
|
||||
if next_policy_stage
|
||||
else None
|
||||
),
|
||||
},
|
||||
"overall": {
|
||||
|
||||
@@ -10,7 +10,11 @@ import json
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
from quality_governance_common import load_and_validate_policy, read_json, summarize_coverage
|
||||
from quality_governance_common import (
|
||||
load_and_validate_policy,
|
||||
read_json,
|
||||
summarize_coverage,
|
||||
)
|
||||
|
||||
|
||||
def _render_text(summary: dict[str, object]) -> str:
|
||||
|
||||
@@ -210,28 +210,31 @@ if precommit_changed_repo_state; then
|
||||
fi
|
||||
cleanup_precommit_snapshots
|
||||
|
||||
echo "[tests] 3/9 coverage governance check"
|
||||
echo "[tests] 3/10 coverage governance check"
|
||||
"$VENV_PY" scripts/verify_quality_governance.py
|
||||
|
||||
echo "[tests] 4/9 backend unit tests"
|
||||
echo "[tests] 4/10 test debt governance check"
|
||||
"$VENV_PY" scripts/verify_test_debt_governance.py
|
||||
|
||||
echo "[tests] 5/10 backend unit tests"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_local_unit" "$VENV_PY" scripts/run_unittests.py --start-dir tests --pattern "test_*.py" --enforce-skip-policy tests/skip_policy.json
|
||||
|
||||
if [ -n "${OPENCLAW_IMPL_RECORD_PATH:-}" ]; then
|
||||
echo "[tests] 4.5/9 implementation record lint (strict)"
|
||||
echo "[tests] 5.5/10 implementation record lint (strict)"
|
||||
# IMPORTANT: strict mode is opt-in via OPENCLAW_IMPL_RECORD_PATH to avoid retroactive legacy record failures.
|
||||
"$VENV_PY" scripts/lint_implementation_record.py --path "$OPENCLAW_IMPL_RECORD_PATH" --strict
|
||||
fi
|
||||
|
||||
echo "[tests] 5/9 backend real E2E lanes (R122/R123)"
|
||||
echo "[tests] 6/10 backend real E2E lanes (R122/R123)"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_local_backend_e2e_real" \
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r122_real_backend_lane --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_local_backend_e2e_real" \
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r123_real_backend_model_list_lane --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
|
||||
echo "[tests] 6/9 R121 retry partition contract"
|
||||
echo "[tests] 7/10 R121 retry partition contract"
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r121_retry_partition_contract --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
|
||||
echo "[tests] 7/9 Slack integration gates (R124/R125/R117/F57)"
|
||||
echo "[tests] 8/10 Slack integration gates (R124/R125/R117/F57)"
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r124_slack_ingress_contract --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r125_slack_real_backend_lane --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_r117_observability_redaction_e2e --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
@@ -239,11 +242,11 @@ echo "[tests] 7/9 Slack integration gates (R124/R125/R117/F57)"
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_f57_slack_transport_parity --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
"$VENV_PY" scripts/run_unittests.py --module tests.test_f57_slack_socket_mode_startup --enforce-skip-policy tests/skip_policy.json --max-skipped 0
|
||||
|
||||
echo "[tests] 8/9 R118 adversarial gate (adaptive: smoke/extended)"
|
||||
echo "[tests] 9/10 R118 adversarial gate (adaptive: smoke/extended)"
|
||||
MOLTBOT_STATE_DIR="$ROOT_DIR/moltbot_state/_local_adversarial" \
|
||||
"$VENV_PY" scripts/run_adversarial_gate.py --profile auto --seed 42 --artifact-dir .tmp/adversarial
|
||||
|
||||
echo "[tests] 9/9 frontend E2E"
|
||||
echo "[tests] 10/10 frontend E2E"
|
||||
# IMPORTANT: full-gate acceptance must provision Playwright browsers itself; do
|
||||
# not assume a warmed local browser cache when running on fresh WSL/Linux hosts.
|
||||
OPENCLAW_PLAYWRIGHT_INSTALL=1 OPENCLAW_PLAYWRIGHT_BROWSERS=chromium npm test
|
||||
|
||||
@@ -266,26 +266,31 @@ if ($LASTEXITCODE -ne 0) {
|
||||
}
|
||||
Assert-PreCommitDidNotMutateRepo -BeforeWorktree $preCommitWorktreeBefore -BeforeIndex $preCommitIndexBefore
|
||||
|
||||
Write-Host "[tests] 3/9 coverage governance check"
|
||||
Write-Host "[tests] 3/10 coverage governance check"
|
||||
Invoke-Checked "coverage governance check" {
|
||||
& $venvPython scripts\verify_quality_governance.py
|
||||
}
|
||||
|
||||
Write-Host "[tests] 4/9 backend unit tests"
|
||||
Write-Host "[tests] 4/10 test debt governance check"
|
||||
Invoke-Checked "test debt governance check" {
|
||||
& $venvPython scripts\verify_test_debt_governance.py
|
||||
}
|
||||
|
||||
Write-Host "[tests] 5/10 backend unit tests"
|
||||
$env:MOLTBOT_STATE_DIR = "$root\moltbot_state\_local_unit"
|
||||
Invoke-Checked "backend unit tests" {
|
||||
& $venvPython scripts\run_unittests.py --start-dir tests --pattern "test_*.py" --enforce-skip-policy tests\skip_policy.json
|
||||
}
|
||||
|
||||
if ($env:OPENCLAW_IMPL_RECORD_PATH) {
|
||||
Write-Host "[tests] 4.5/9 implementation record lint (strict)"
|
||||
Write-Host "[tests] 5.5/10 implementation record lint (strict)"
|
||||
# IMPORTANT: strict mode is opt-in via OPENCLAW_IMPL_RECORD_PATH to avoid retroactive legacy record failures.
|
||||
Invoke-Checked "implementation record lint" {
|
||||
& $venvPython scripts\lint_implementation_record.py --path $env:OPENCLAW_IMPL_RECORD_PATH --strict
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host "[tests] 5/9 backend real E2E lanes (R122/R123)"
|
||||
Write-Host "[tests] 6/10 backend real E2E lanes (R122/R123)"
|
||||
$env:MOLTBOT_STATE_DIR = "$root\moltbot_state\_local_backend_e2e_real"
|
||||
Invoke-Checked "backend real E2E lane R122" {
|
||||
& $venvPython scripts\run_unittests.py --module tests.test_r122_real_backend_lane --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
||||
@@ -294,12 +299,12 @@ Invoke-Checked "backend real E2E lane R123" {
|
||||
& $venvPython scripts\run_unittests.py --module tests.test_r123_real_backend_model_list_lane --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
||||
}
|
||||
|
||||
Write-Host "[tests] 6/9 R121 retry partition contract"
|
||||
Write-Host "[tests] 7/10 R121 retry partition contract"
|
||||
Invoke-Checked "R121 retry partition contract" {
|
||||
& $venvPython scripts\run_unittests.py --module tests.test_r121_retry_partition_contract --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
||||
}
|
||||
|
||||
Write-Host "[tests] 7/9 Slack integration gates (R124/R125/R117/F57)"
|
||||
Write-Host "[tests] 8/10 Slack integration gates (R124/R125/R117/F57)"
|
||||
Invoke-Checked "Slack integration gates" {
|
||||
& $venvPython scripts\run_unittests.py --module tests.test_r124_slack_ingress_contract --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
||||
& $venvPython scripts\run_unittests.py --module tests.test_r125_slack_real_backend_lane --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
||||
@@ -309,13 +314,13 @@ Invoke-Checked "Slack integration gates" {
|
||||
& $venvPython scripts\run_unittests.py --module tests.test_f57_slack_socket_mode_startup --enforce-skip-policy tests\skip_policy.json --max-skipped 0
|
||||
}
|
||||
|
||||
Write-Host "[tests] 8/9 R118 adversarial gate (adaptive: smoke/extended)"
|
||||
Write-Host "[tests] 9/10 R118 adversarial gate (adaptive: smoke/extended)"
|
||||
$env:MOLTBOT_STATE_DIR = "$root\moltbot_state\_local_adversarial"
|
||||
Invoke-Checked "R118 adversarial adaptive" {
|
||||
& $venvPython scripts\run_adversarial_gate.py --profile auto --seed 42 --artifact-dir .tmp\adversarial
|
||||
}
|
||||
|
||||
Write-Host "[tests] 9/9 frontend E2E"
|
||||
Write-Host "[tests] 10/10 frontend E2E"
|
||||
$env:OPENCLAW_PLAYWRIGHT_INSTALL = "1"
|
||||
$env:OPENCLAW_PLAYWRIGHT_BROWSERS = "chromium"
|
||||
# IMPORTANT: full-gate acceptance must provision Playwright browsers itself; do
|
||||
|
||||
@@ -0,0 +1,229 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
R171: validate skip-policy and mutation-survivor debt metadata.
|
||||
|
||||
This script is intentionally stdlib-only so it can run early in local/full-test
|
||||
gates before optional dependencies are installed.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
from datetime import date
|
||||
from pathlib import Path, PurePosixPath
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
|
||||
def _read_json_object(path: Path) -> Dict[str, Any]:
|
||||
payload = json.loads(path.read_text(encoding="utf-8-sig"))
|
||||
if not isinstance(payload, dict):
|
||||
raise ValueError(f"{path}: expected a JSON object")
|
||||
return payload
|
||||
|
||||
|
||||
def _normalize_repo_rel_path(path: str) -> str:
|
||||
return PurePosixPath(path.replace("\\", "/")).as_posix().lstrip("./")
|
||||
|
||||
|
||||
def _parse_review_after(value: Any, *, label: str, failures: List[str]) -> None:
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
failures.append(f"{label}: missing review_after")
|
||||
return
|
||||
try:
|
||||
review_after = date.fromisoformat(value)
|
||||
except ValueError:
|
||||
failures.append(f"{label}: invalid review_after '{value}'")
|
||||
return
|
||||
if review_after < date.today():
|
||||
failures.append(f"{label}: review_after {value} is in the past")
|
||||
|
||||
|
||||
def _validate_reason(value: Any, *, label: str, failures: List[str]) -> None:
|
||||
if not isinstance(value, str) or not value.strip():
|
||||
failures.append(f"{label}: missing non-empty reason")
|
||||
|
||||
|
||||
def _resolve_test_module_path(repo_root: Path, module_name: str) -> Path:
|
||||
return repo_root / Path(module_name.replace(".", "/")).with_suffix(".py")
|
||||
|
||||
|
||||
def _validate_skip_policy(repo_root: Path, path: Path) -> List[str]:
|
||||
failures: List[str] = []
|
||||
payload = _read_json_object(path)
|
||||
|
||||
max_skipped = payload.get("max_skipped")
|
||||
if not isinstance(max_skipped, int) or max_skipped < 0:
|
||||
failures.append("skip policy: max_skipped must be a non-negative integer")
|
||||
|
||||
modules = payload.get("no_skip_modules", [])
|
||||
if not isinstance(modules, list) or any(
|
||||
not isinstance(item, str) or not item.strip() for item in modules
|
||||
):
|
||||
failures.append("skip policy: no_skip_modules must be a list of non-empty strings")
|
||||
return failures
|
||||
|
||||
seen = set()
|
||||
duplicates = set()
|
||||
normalized_modules: List[str] = []
|
||||
for module in modules:
|
||||
normalized = module.strip()
|
||||
normalized_modules.append(normalized)
|
||||
if normalized in seen:
|
||||
duplicates.add(normalized)
|
||||
seen.add(normalized)
|
||||
module_path = _resolve_test_module_path(repo_root, normalized)
|
||||
if not module_path.is_file():
|
||||
failures.append(
|
||||
f"skip policy: module path does not exist for {normalized} -> {module_path.relative_to(repo_root)}"
|
||||
)
|
||||
if duplicates:
|
||||
failures.append(
|
||||
"skip policy: duplicate no-skip modules: " + ", ".join(sorted(duplicates))
|
||||
)
|
||||
|
||||
metadata = payload.get("no_skip_module_metadata")
|
||||
if not isinstance(metadata, dict):
|
||||
failures.append(
|
||||
"skip policy: no_skip_module_metadata must be an object keyed by module name"
|
||||
)
|
||||
return failures
|
||||
|
||||
metadata_keys = {str(key).strip() for key in metadata.keys()}
|
||||
missing_metadata = [module for module in normalized_modules if module not in metadata_keys]
|
||||
extra_metadata = sorted(
|
||||
key for key in metadata_keys if key and key not in set(normalized_modules)
|
||||
)
|
||||
if missing_metadata:
|
||||
failures.append(
|
||||
"skip policy: missing metadata for no-skip modules: "
|
||||
+ ", ".join(sorted(missing_metadata))
|
||||
)
|
||||
if extra_metadata:
|
||||
failures.append(
|
||||
"skip policy: stale metadata without matching no-skip module: "
|
||||
+ ", ".join(extra_metadata)
|
||||
)
|
||||
|
||||
for module_name in normalized_modules:
|
||||
raw_meta = metadata.get(module_name)
|
||||
label = f"skip policy metadata[{module_name}]"
|
||||
if not isinstance(raw_meta, dict):
|
||||
failures.append(f"{label}: metadata entry must be an object")
|
||||
continue
|
||||
_validate_reason(raw_meta.get("reason"), label=label, failures=failures)
|
||||
_parse_review_after(
|
||||
raw_meta.get("review_after"), label=label, failures=failures
|
||||
)
|
||||
return failures
|
||||
|
||||
|
||||
def _validate_mutation_allowlist(repo_root: Path, path: Path) -> List[str]:
|
||||
failures: List[str] = []
|
||||
payload = _read_json_object(path)
|
||||
entries = payload.get("entries", [])
|
||||
if not isinstance(entries, list):
|
||||
return ["mutation allowlist: entries must be a list"]
|
||||
|
||||
seen: set[Tuple[str, int]] = set()
|
||||
duplicates: set[Tuple[str, int]] = set()
|
||||
for index, raw_entry in enumerate(entries):
|
||||
label = f"mutation allowlist entry[{index}]"
|
||||
if not isinstance(raw_entry, dict):
|
||||
failures.append(f"{label}: entry must be an object")
|
||||
continue
|
||||
file_path = _normalize_repo_rel_path(str(raw_entry.get("file", "")))
|
||||
mutation_index = raw_entry.get("mutation_index")
|
||||
if not file_path:
|
||||
failures.append(f"{label}: missing file")
|
||||
elif not (repo_root / Path(file_path)).is_file():
|
||||
failures.append(f"{label}: file does not exist in repo: {file_path}")
|
||||
if not isinstance(mutation_index, int) or mutation_index < 0:
|
||||
failures.append(f"{label}: mutation_index must be a non-negative integer")
|
||||
else:
|
||||
key = (file_path, mutation_index)
|
||||
if key in seen:
|
||||
duplicates.add(key)
|
||||
seen.add(key)
|
||||
_validate_reason(raw_entry.get("reason"), label=label, failures=failures)
|
||||
_parse_review_after(
|
||||
raw_entry.get("review_after"), label=label, failures=failures
|
||||
)
|
||||
|
||||
if duplicates:
|
||||
failures.append(
|
||||
"mutation allowlist: duplicate (file, mutation_index) entries: "
|
||||
+ ", ".join(f"{file}@{mutation_index}" for file, mutation_index in sorted(duplicates))
|
||||
)
|
||||
return failures
|
||||
|
||||
|
||||
def verify_test_debt_governance(
|
||||
*,
|
||||
repo_root: Path,
|
||||
skip_policy_path: Path,
|
||||
mutation_allowlist_path: Path,
|
||||
) -> List[str]:
|
||||
failures: List[str] = []
|
||||
if not skip_policy_path.is_file():
|
||||
failures.append(f"missing skip policy: {skip_policy_path}")
|
||||
else:
|
||||
try:
|
||||
failures.extend(_validate_skip_policy(repo_root, skip_policy_path))
|
||||
except Exception as exc:
|
||||
failures.append(f"skip policy: failed to validate {skip_policy_path}: {exc}")
|
||||
|
||||
if not mutation_allowlist_path.is_file():
|
||||
failures.append(f"missing mutation survivor allowlist: {mutation_allowlist_path}")
|
||||
else:
|
||||
try:
|
||||
failures.extend(
|
||||
_validate_mutation_allowlist(repo_root, mutation_allowlist_path)
|
||||
)
|
||||
except Exception as exc:
|
||||
failures.append(
|
||||
f"mutation allowlist: failed to validate {mutation_allowlist_path}: {exc}"
|
||||
)
|
||||
return failures
|
||||
|
||||
|
||||
def main() -> int:
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Validate skip-policy and mutation-survivor debt metadata."
|
||||
)
|
||||
parser.add_argument(
|
||||
"--repo-root",
|
||||
default=".",
|
||||
help="Repository root used to resolve test modules and file paths.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-policy",
|
||||
default="tests/skip_policy.json",
|
||||
help="Path to tests/skip_policy.json",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--mutation-survivor-allowlist",
|
||||
default="tests/mutation_survivor_allowlist.json",
|
||||
help="Path to tests/mutation_survivor_allowlist.json",
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
repo_root = Path(args.repo_root).resolve()
|
||||
failures = verify_test_debt_governance(
|
||||
repo_root=repo_root,
|
||||
skip_policy_path=Path(args.skip_policy),
|
||||
mutation_allowlist_path=Path(args.mutation_survivor_allowlist),
|
||||
)
|
||||
if failures:
|
||||
for failure in failures:
|
||||
print(f"TEST-DEBT-GOVERNANCE-FAIL: {failure}")
|
||||
return 1
|
||||
|
||||
print(
|
||||
"TEST-DEBT-GOVERNANCE-PASS: skip-policy and mutation-survivor debt metadata are current."
|
||||
)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user