"""Helpers shared by the web tests (2026-10-19).""" from pathlib import Path _APP = Path(__file__).resolve().parents[1] def pier_dir(name): """data/, or the newest recycle-bin copy of a pier that was deleted in real use (운곡교p2 on 2026-10-19). Read only; exact case (Windows globbing is case-insensitive: 운곡교p2 must not pick 운곡교P2_*).""" d = _APP / "data" / name if (d / "params.json").exists(): return d trash = sorted(t for t in (_APP / "data" / "_휴지통").glob(f"{name}_*") if t.name.startswith(name + "_") and (t / "params.json").exists()) return trash[-1] if trash else d def confirm_required(client, webapp, store, pier, by, why="시험"): """Confirm every required, unconfirmed bundle of a pier one by one through the section confirm route. The old one-click bulk route is gone; a bundle the app check does not report as 일치 needs a reason, so one is always sent.""" for f, k in webapp.unconfirmed(store.Pier(pier)): client.post(f"/pier/{pier}/section/{f}/{k}/confirm", data={"by": by, "why": why}) def _approved(d): """the pier folder has a recorded G5 result AND an approval of G5 (the whole gate chain was approved)""" import json try: ap = json.loads((d / "runs" / "current" / "approvals.json").read_text(encoding="utf-8")) except (OSError, ValueError): return False return (d / "runs" / "current" / "G5_gate.json").exists() and ap.get("G5", {}).get("decision") == "승인" def verified_pier(name): """The copy of a pier whose gates G1..G5 were all approved (data/, else the newest recycle-bin copy): a pier may be deleted and made again from the template in real use (운곡교P4 on 2026-10-20) and is half-updated while it is being modelled. None when there is none.""" d = _APP / "data" / name if _approved(d): return d trash = sorted(t for t in (_APP / "data" / "_휴지통").glob(f"{name}_*") if t.name.startswith(name + "_") and _approved(t)) return trash[-1] if trash else None