Files
QuantEngineByItz/tools/validate_golden_coverage_100.py
T

159 lines
5.5 KiB
Python

"""validate_golden_coverage_100.py — P1-016 Golden Case Coverage Acceptance Test
formula_golden_cases_v4.yaml(및 이전 버전)까지 포함해 golden_coverage_ratio가
목표치를 초과하는지 검증.
수락 기준:
- golden_coverage_ratio >= 0.95 (95%+): PASS
- decision_critical_min_cases >= 3: PASS for all 28 decision-critical formulas
Exit:
0 = PASS (목표 커버리지 달성)
1 = FAIL (커버리지 미달 또는 decision-critical 케이스 부족)
"""
from __future__ import annotations
import json
import subprocess
import sys
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
COVERAGE_JSON = ROOT / "Temp" / "yaml_code_coverage_v1.json"
COVERAGE_TARGET = 0.95
DECISION_CRITICAL = {
"STOP_BREACH_V1",
"SMART_CASH_RECOVERY_V4",
"SMART_CASH_RECOVERY_V7",
"ANTI_LATE_ENTRY_PULLBACK_GATE_V4",
"REGIME_CONDITIONAL_MACRO_FACTOR_V1",
"MACRO_REGIME_ALIGNMENT_GATE_V2",
"DISTRIBUTION_EXIT_PRESIGNAL_V2",
"SELL_EXECUTION_TIMING_LOCK_V2",
"SELL_EXECUTION_QUALITY_GATE_V1",
"PORTFOLIO_HEALTH_V1",
"INDEX_RELATIVE_HEALTH_GATE_V1",
"CASH_RAISE_PARETO_EXECUTOR_V2",
"CASH_RAISE_VALUE_OPTIMIZER_V3",
"CASH_RECOVERY_OPTIMIZER_V4",
"CASH_RECOVERY_V1",
"DATA_MATURITY_TRUTH_GATE_V1",
"DATA_MATURITY_TRUTH_GATE_VALIDATOR_V1",
"DATA_QUALITY_GATE_V2_PY",
"DATA_QUALITY_GATE_V3",
"IMPUTED_DATA_EXPOSURE_GATE_V2",
"OPERATIONAL_ALPHA_CALIBRATION_V2",
"PREDICTIVE_ALPHA_DIALECTIC_ENGINE_V1_BRIDGE",
"REBOUND_SELL_EFFICIENCY_V1",
"SELL_ENGINE_AUDIT_V1",
"SELL_SLIPPAGE_BUDGET_FACTOR_V1",
"ENTRY_TIMING_DECILE_FACTOR_V1",
"DYNAMIC_VALUE_PRESERVATION_SELL_V3_BRIDGE",
"ARTIFACT_FRESHNESS_GATE_V1",
}
MIN_CASES_PER_CRITICAL = 3
def _rebuild_coverage() -> dict:
"""coverage JSON이 없으면 빌더를 실행해 갱신."""
if not COVERAGE_JSON.exists():
subprocess.run(
[sys.executable, str(ROOT / "tools" / "build_yaml_code_coverage_v1.py")],
check=True,
)
with COVERAGE_JSON.open(encoding="utf-8") as fh:
return json.load(fh)
def _count_cases_per_formula() -> dict[str, int]:
"""golden_cases_v2/v3/v4 에서 formula_id별 케이스 수 집계."""
import yaml # type: ignore
spec_dir = ROOT / "spec"
files = [
spec_dir / "formula_golden_cases_v2.yaml",
spec_dir / "formula_golden_cases_v3.yaml",
spec_dir / "formula_golden_cases_v4.yaml",
]
counts: dict[str, int] = {}
for f in files:
if not f.exists():
continue
try:
doc = yaml.safe_load(f.read_text(encoding="utf-8")) or {}
except Exception:
continue
for entry in doc.get("golden_cases", []):
fid = entry.get("formula_id")
if fid:
counts[fid] = counts.get(fid, 0) + 1
return counts
def main() -> int:
cov = _rebuild_coverage()
ratio = cov.get("golden_coverage_ratio", 0.0)
total = cov.get("yaml_formula_count", 0)
golden = cov.get("golden_test_count", 0)
uncovered = cov.get("golden_uncovered_rules", [])
case_counts = _count_cases_per_formula()
critical_missing: list[str] = []
for fid in DECISION_CRITICAL:
if case_counts.get(fid, 0) < MIN_CASES_PER_CRITICAL:
critical_missing.append(fid)
ok_ratio = ratio >= COVERAGE_TARGET
ok_critical = len(critical_missing) == 0
# 정직성 표기 (QE-M0-06): 이 게이트는 golden 케이스/파일의 "존재 수"만 집계한다.
# tests/golden/generated/ 의 174개 파일은 실행되지 않는 placeholder이며, 수치
# 실행 검증은 M3의 factor parity 게이트(validate_factor_parity_v1)로 대체된다.
payload = {
"formula_id": "GOLDEN_COVERAGE_100_V1",
"gate": "PASS" if (ok_ratio and ok_critical) else "FAIL",
"coverage_basis": "FILE_COUNT_ONLY",
"golden_coverage_ratio": ratio,
"yaml_formula_count": total,
"golden_test_count": golden,
"critical_missing": sorted(critical_missing),
"uncovered_count": len(uncovered),
"note": "coverage counts YAML golden-case entries/files only; generated golden test stubs are not executed",
}
out_path = ROOT / "Temp" / "golden_coverage_100_v1.json"
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
print(f"[GOLDEN_COVERAGE_100] total={total} golden={golden} ratio={ratio:.4f} "
f"({'≥' if ok_ratio else '<'}{COVERAGE_TARGET}) "
f"critical_missing={len(critical_missing)}")
if critical_missing:
print(f" [WARN] decision-critical with <{MIN_CASES_PER_CRITICAL} cases:")
for fid in critical_missing:
print(f" - {fid}: {case_counts.get(fid, 0)} case(s)")
if uncovered:
print(f" [WARN] still uncovered ({len(uncovered)}): "
+ ", ".join(uncovered[:20])
+ ("..." if len(uncovered) > 20 else ""))
if ok_ratio and ok_critical:
print(" [PASS] golden_coverage_ratio >= 95% and all decision-critical formulas covered")
return 0
else:
issues = []
if not ok_ratio:
issues.append(f"golden_coverage_ratio={ratio:.4f} < {COVERAGE_TARGET}")
if not ok_critical:
issues.append(f"{len(critical_missing)} critical formula(s) have <{MIN_CASES_PER_CRITICAL} cases")
print(f" [FAIL] {'; '.join(issues)}")
return 1
if __name__ == "__main__":
raise SystemExit(main())