Files
Aislo/resources/tester/test_472_save_warnings.py
T
eomsangdonandClaude Opus 5.5 84b683845a fix(472 덧 종단 경고): [저장] · [확정] 재계산이 종단 경고를 저장 입력으로 다시 씀 · 경고 글 TS 한 벌
- 서버 재계산(_recompute) 맨 앞 refresh_alignment — 저장 base_pvi · 편집 · 정책 · 기준으로 경고만 지금 코드 값 · 기하(계획고 · 곡선 · 표본 · 위반)는 저장값 그대로 · 같으면 안 씀 · grade_summary 함께
- 파이썬 경고 글을 TS buildAlignment 꼴로(접두 「chainage 」 뺌 · 기울기 초과 · 불균형 총괄 줄 뺌 — violations · balance 칸이 정본)
- 거울 시험 test_472_save_warnings(옛 코드 실패 확인) · 기준값 경고 칸만 다시 뜸(server_save 5→50 · server_route 51→50 · 브레인 승인) · W0 정리 · W1 지움
- 위키 계산 목록 P8 · [저장] 차례 cross 줄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 08:55:38 +09:00

131 lines
5.8 KiB
Python

"""PLAN 472 덧 (요구 272) — [저장] · [확정] 재계산이 종단 경고를 저장 입력으로 다시 씀.
증상 — 검증_랩탑메인 정본 종단 경고 5 줄(10-17 옛 코드) · 지금 코드로 셈하면 50 줄 ·
[저장] · [확정] 재계산(`B06_Section_Server_Calc_Prebuild._recompute`)이 경고를 다시 안 셈.
고침 — `_recompute` 맨 앞 `_refresh_alignment_file` → `refresh_alignment`(저장 base_pvi · 편집 ·
정책 · 기준 → `rebuild_alignment_profile` 의 경고만) · 기하는 저장값 그대로 · 같으면 안 씀.
경고 글 = TS `buildAlignment` 와 한 벌(거울). 고정 자료 = `fixtures/golden_472/detail.json`.
./venv/Scripts/python.exe -m pytest resources/tester/test_472_save_warnings.py -q
"""
from __future__ import annotations
import copy
import inspect
import json
import subprocess
import sys
from pathlib import Path
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
sys.path.insert(0, str(ROOT))
from B05_Profile.B05_Profile_Engine_Grade import ground_profile # noqa: E402
from B05_Profile.B05_Profile_Engine_Grade_Alignment import chainage_key # noqa: E402
from B05_Profile.B05_Profile_Engine_Grade_Profile import rebuild_alignment_profile # noqa: E402
from B06_Section import B06_Section_Server_Calc_Prebuild as prebuild # noqa: E402
FIXTURE = HERE / "fixtures" / "golden_472"
DETAIL = json.loads((FIXTURE / "detail.json").read_text(encoding="utf-8"))
def _longitudinal() -> dict:
return copy.deepcopy(DETAIL["longitudinal"])
def test_정본_경고는_옛_코드_5줄() -> None:
assert len(DETAIL["longitudinal"]["profile_alignment"]["warnings"]) == 5
def test_다시_셈_경고만_새로_기하는_그대로() -> None:
longitudinal = _longitudinal()
before = copy.deepcopy(longitudinal)
summary = prebuild.refresh_alignment(longitudinal)
warnings = longitudinal["profile_alignment"]["warnings"]
assert len(warnings) == 50
assert sum("법정 최소 R" in w for w in warnings) == 46 # 432 법정 R · L 미달
assert not [w for w in warnings if w.startswith("chainage ")]
assert summary is not None and summary["warnings"] == warnings
assert summary["id"] == before["design_profiles"][0]["id"]
assert longitudinal["design_profiles"][0]["summary"]["warnings"] == warnings
# 경고 밖 = 저장값 그대로(계획고 · 곡선 · 표본 · 위반 · 정책 — 6 자리 반올림 끝자리도)
longitudinal["profile_alignment"]["warnings"] = before["profile_alignment"]["warnings"]
longitudinal["design_profiles"][0]["summary"]["warnings"] = before["design_profiles"][0][
"summary"
]["warnings"]
assert longitudinal == before
def test_선형_없는_옛_종단은_그대로() -> None:
longitudinal = _longitudinal()
longitudinal["profile_alignment"].pop("base_pvi")
keep = copy.deepcopy(longitudinal)
assert prebuild.refresh_alignment(longitudinal) is None
assert longitudinal == keep
def test_파일_다시_씀_같으면_안_씀(tmp_path) -> None:
path = tmp_path / "B06_Section" / "longitudinal" / "longitudinal.json"
path.parent.mkdir(parents=True)
path.write_text(json.dumps(_longitudinal()), encoding="utf-8")
relative = "B06_Section/longitudinal/longitudinal.json"
first = prebuild._refresh_alignment_file(tmp_path, relative)
stored = json.loads(path.read_text(encoding="utf-8"))
assert first is not None and len(stored["profile_alignment"]["warnings"]) == 50
written = path.stat().st_mtime_ns
assert prebuild._refresh_alignment_file(tmp_path, relative) == first
assert path.stat().st_mtime_ns == written # 같은 값 — 다시 안 씀
assert prebuild._refresh_alignment_file(tmp_path, "없음/longitudinal.json") is None
def test_재계산_맨_앞에서_선형_다시_셈() -> None:
source = inspect.getsource(prebuild._recompute)
assert "_refresh_alignment_file" in source and "update_longitudinal_grade_summary" in source
assert source.index("_refresh_alignment_file") < source.index("get_section_detail(")
def test_TS_거울_같은_경고_목록(tmp_path) -> None:
longitudinal = _longitudinal()
alignment = longitudinal["profile_alignment"]
# 옛 정책엔 법정 R · L 이 없음 — 두 쪽 다 기준으로 채운 정책(433-13 · TS `readAlignment`)
policy = rebuild_alignment_profile(longitudinal, alignment["edits"])[0]["policy"]
assert (
policy["min_vertical_radius_m"] == 100.0
and "min_vertical_radius_m" not in alignment["policy"]
)
chainage, ground = ground_profile(longitudinal)
curve = alignment["curves"][1]
station = longitudinal["stations"][5]
cases = [
alignment["edits"],
{"station_offsets": {}, "curve_radii": {chainage_key(curve["chainage_m"]): 500.0}},
{"station_offsets": {chainage_key(station["chainage_m"]): 3.0}, "curve_radii": {}},
]
base = {
"policy": policy,
"onlyExplicitCurves": bool(alignment.get("only_explicit_curves")),
"basePvi": alignment["base_pvi"],
"chainage": chainage.tolist(),
"ground": ground.tolist(),
"stations": [
{"station_id": s.get("station_id"), "chainage_m": s["chainage_m"]}
for s in longitudinal["stations"]
],
}
source = tmp_path / "in.json"
source.write_text(json.dumps({"base": base, "cases": cases}), encoding="utf-8")
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(HERE / "helper_472_save_warnings.cjs"), str(source)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
for edits, got in zip(cases, json.loads(proc.stdout), strict=True):
py, _profile = rebuild_alignment_profile(copy.deepcopy(longitudinal), edits)
assert got["warnings"] == py["warnings"]
assert got["violations"] == len(py["violations"])