"""455 (433-13 ③) 옛 정책 종단 — 법정 최소 R · L 미달 경고가 초안 · 저장 뒤 같음. 까닭: 옛 저장 정책엔 `min_vertical_radius_m` · `min_curve_length_m` 가 없어 화면 초안(저장 정책 그대로) 경고 0 · 노선 [저장](새 정책)에서야 뜸. 고침: 빈 값은 첫 계획선 설계 기준(criteria = 설계속도 법정값) — TS `readAlignment` ↔ 서버 `rebuild_alignment_profile` 같은 규칙(`helper_455_old_policy.cjs`). """ from __future__ import annotations import copy import json import math import subprocess import sys from pathlib import Path HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) from B05_Profile.B05_Profile_Engine_Grade import GradeDesignOptions # noqa: E402 from B05_Profile.B05_Profile_Engine_Grade_Profile import ( # noqa: E402 design_ground_following_profile, rebuild_alignment_profile, ) # 설계속도 40 — 법정 최소 R 450 · L 40 > 기본 L 20 → 곡선마다 미달 OPTIONS = GradeDesignOptions( max_grade_pct=14.0, max_reverse_grade_pct=5.0, min_vertical_radius_m=450.0, min_curve_length_m=40.0, min_tangent_length_m=20.0, vertical_curve_skip_delta_pct=0.5, design_speed_kph=40, ) def _ground(s: float) -> float: return 100.0 + 0.05 * s + 2.0 * math.sin(s / 30.0) def _fresh() -> dict: samples = [ {"chainage_m": float(s), "elevation_m": round(_ground(s), 6), "valid": True} for s in range(0, 401) ] stations = [{"chainage_m": float(s), "kind": "regular"} for s in range(0, 401, 20)] data = {"length_m": 400.0, "samples": samples, "stations": stations} alignment, profile = design_ground_following_profile(data, OPTIONS, station_interval_m=20.0) data["profile_alignment"], data["design_profiles"] = alignment, [profile] return data def _old() -> dict: data = _fresh() for key in ("min_vertical_radius_m", "min_curve_length_m"): data["profile_alignment"]["policy"].pop(key) return data def _law(warnings: list[str]) -> list[str]: return [text for text in warnings if "법정 최소" in text] def _ts(tmp_path: Path, data: dict) -> dict: source = tmp_path / "in455.json" source.write_text(json.dumps(data, ensure_ascii=False), encoding="utf-8") proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HERE / "helper_455_old_policy.cjs"), str(source)], capture_output=True, text=True, encoding="utf-8", cwd=str(ROOT), check=False, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def test_새_정책_경고_기준(tmp_path: Path) -> None: fresh = _fresh() law = _law(fresh["profile_alignment"]["warnings"]) assert law assert _ts(tmp_path, fresh)["warnings"] == law def test_옛_정책_초안_경고_저장_뒤와_같음(tmp_path: Path) -> None: expected = _law(_fresh()["profile_alignment"]["warnings"]) old = _old() ts = _ts(tmp_path, copy.deepcopy(old)) assert ts["policy"]["min_vertical_radius_m"] == 450.0 assert ts["policy"]["min_curve_length_m"] == 40.0 assert ts["warnings"] == expected alignment, _profile = rebuild_alignment_profile(old, {}) assert _law(alignment["warnings"]) == expected assert alignment["policy"]["min_curve_length_m"] == 40.0 def test_criteria_없는_판은_그대로(tmp_path: Path) -> None: old = _old() old["design_profiles"][0].pop("criteria") assert _ts(tmp_path, copy.deepcopy(old))["warnings"] == []