feat(ldd): property-based, mutation и браузерный E2E

This commit is contained in:
2026-10-28 00:01:56 +03:00
parent 80ccf26b61
commit 905feb440b
18 changed files with 1432 additions and 2 deletions

68
scripts/ldd-mutation.sh Executable file
View File

@@ -0,0 +1,68 @@
#!/usr/bin/env bash
# LDD mutation evidence: PIT для критичных P0-компонентов.
# JUnit 4 harness используется потому, что JUnit5-плагин PIT несовместим с JUnit Platform 6.
set -euo pipefail
cd "$(dirname "$0")/.."
./gradlew \
:services:academic-service:pitestCourseAccess \
:services:identity-service:pitestPromotionPlanner \
--console=plain
mkdir -p build/evidence
python3 - <<'PY'
import json
import os
import xml.etree.ElementTree as ET
checks = []
def parse(path):
root = ET.parse(path).getroot()
total = killed = no_coverage = 0
for mutation in root.findall("mutation"):
total += 1
status = mutation.attrib.get("status")
if status == "KILLED":
killed += 1
elif status == "NO_COVERAGE":
no_coverage += 1
covered = total - no_coverage
strength = (killed / covered * 100) if covered else 0.0
return total, killed, no_coverage, strength
def check(check_id, path, threshold):
if not os.path.isfile(path):
checks.append({"id": check_id, "status": "failed", "detail": "нет отчёта " + path})
return
total, killed, no_coverage, strength = parse(path)
status = "passed" if strength >= threshold else "failed"
detail = (
f"mutations={total}, killed={killed}, no_coverage={no_coverage}, "
f"test_strength={strength:.1f}% (threshold {threshold:.0f}%)"
)
checks.append({"id": check_id, "status": status, "detail": detail})
print(("PASS " if status == "passed" else "FAIL ") + check_id + ": " + detail)
check(
"mutation.course_access",
"services/academic-service/build/reports/pitest-course-access/mutations.xml",
70.0,
)
check(
"mutation.promotion_planner",
"services/identity-service/build/reports/pitest-promotion-planner/mutations.xml",
60.0,
)
with open("build/evidence/mutation.json", "w", encoding="utf-8") as fh:
json.dump({"generatedAt": __import__("datetime").datetime.now(__import__("datetime").UTC).isoformat(), "checks": checks}, fh, ensure_ascii=False, indent=2)
if any(c["status"] != "passed" for c in checks):
raise SystemExit(1)
PY