"""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())