orquestador/scripts/verify_acceptance.py

78 lines
3.8 KiB
Python

"""Check actual Maven reports against A01-A20; no third-party packages or credentials."""
import argparse
import hashlib
import json
from pathlib import Path
import re
import sys
import xml.etree.ElementTree as ET
def inspect(root, manifest):
failures, found, totals, environments = [], {}, {"unit": 0, "integration": 0}, set()
expected = {f"A{i:02}" for i in range(1, 21)}
if set(manifest) != expected:
failures.append("Manifest must contain exactly A01-A20")
for group, folder in (("unit", "surefire-reports"), ("integration", "failsafe-reports")):
paths = sorted((root / folder).glob("TEST-*.xml"))
if not paths:
failures.append(f"Missing {folder}")
for path in paths:
suite = ET.parse(path).getroot()
for flag in ("failures", "errors", "skipped"):
if int(suite.get(flag, "0")):
failures.append(f"{suite.get('name')}: {flag}={suite.get(flag)}")
tests = suite.findall("testcase")
totals[group] += len(tests)
if group == "integration":
external = any(p.get("name") == "migration.test.external" and p.get("value") == "true"
for p in suite.findall("./properties/property"))
environments.add("external-postgresql" if external else "testcontainers")
for test in tests:
method = re.split(r"[\[(]", test.get("name", ""), maxsplit=1)[0]
key = test.get("classname", "").rsplit(".", 1)[-1] + "#" + method
ok = not any(test.find(tag) is not None for tag in ("failure", "error", "skipped"))
if not ok:
failures.append(f"Unsuccessful testcase: {key}")
found.setdefault(key, []).append(ok)
cases = {}
for case, references in sorted(manifest.items()):
if not references:
failures.append(f"{case}: no test references")
evidence = {}
for reference in references:
runs = found.get(reference, [])
evidence[reference] = {"executed": len(runs), "passed": sum(runs)}
if not runs or not all(runs):
failures.append(f"{case}: missing/failed/skipped {reference}")
cases[case] = evidence
if len(environments) != 1:
failures.append("Missing or mixed integration environments")
return {"passed": not failures, "environment": sorted(environments), "tests": totals,
"cases": cases, "failures": failures}
def main():
parser = argparse.ArgumentParser()
parser.add_argument("--reports", type=Path, default=Path("target"))
parser.add_argument("--manifest", type=Path, default=Path("specs/acceptance-cases.json"))
parser.add_argument("--output", type=Path, default=Path("target/acceptance-report.json"))
parser.add_argument("--require-testcontainers", action="store_true")
args = parser.parse_args()
result = inspect(args.reports, json.loads(args.manifest.read_text(encoding="utf-8-sig")))
if args.require_testcontainers and result["environment"] != ["testcontainers"]:
result["passed"] = False
result["failures"].append("Docker/Testcontainers evidence required; external PostgreSQL is not equivalent")
result["manifestSha256"] = hashlib.sha256(args.manifest.read_bytes()).hexdigest()
jar = args.reports / "orquestador-nmp-0.1.0-SNAPSHOT.jar"
if jar.is_file():
result["jarSha256"] = hashlib.sha256(jar.read_bytes()).hexdigest()
args.output.parent.mkdir(parents=True, exist_ok=True)
args.output.write_text(json.dumps(result, indent=2) + "\n", encoding="utf-8")
print(json.dumps({k: result[k] for k in ("passed", "environment", "tests", "failures")}))
return 0 if result["passed"] else 1
if __name__ == "__main__":
sys.exit(main())