"""408-1 R 바꿈 뒤 뒤쪽 측점 맞춤(요구 252-3) — 반지름 · 곡선 길이 입력 = 교점 끌기와 같은 `fitCurveStations` 로 고친 곡선부터 끝 곡선까지 MC 를 측점에 다시 맞춤 · 상태줄 「함께 밀린 교점 N곳 · 못 맞춘 곡선 No.…」(`KeyNudge.fitNote`) · 스냅 끔 = 맞춤 · 보고 없음. TS 셈은 `helper_408_radius_downstream.cjs`(Node · `_CurveBar` 배선 그대로). ./venv/Scripts/python.exe -m pytest resources/tester/test_408_radius_downstream.py -q """ import json import subprocess from pathlib import Path import pytest HELPER = Path(__file__).with_name("helper_408_radius_downstream.cjs") TOL = 0.05 # MC_TOL_M STEPS = ["R60", "L50", "Auto"] @pytest.fixture(scope="module") def got() -> dict: run = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HELPER)], capture_output=True, text=True, encoding="utf-8", timeout=120, ) assert run.returncode == 0, run.stderr return json.loads(run.stdout) def _on_station(mc: float) -> bool: return abs(mc - round(mc / 20) * 20) <= TOL @pytest.mark.parametrize("step", STEPS) def test_스냅_켬_뒤쪽_곡선_MC_가_측점_위(got, step) -> None: row = got[f"on{step}"] assert len(row["mcs"]) == 4 and all(_on_station(mc) for mc in row["mcs"]) assert row["off"] <= got["onStart"]["off"] @pytest.mark.parametrize("step", STEPS) def test_스냅_켬_뒤_교점이_밀림_시점은_그대로(got, step) -> None: before = got["onStart"]["planned"] after = got[f"on{step}"]["planned"] assert after[0] == before[0] and after[-1] == before[-1] assert all(after[k] != before[k] for k in (2, 3, 4)) # 고친 곡선 뒤 교점이 맞춤으로 밀림 def test_상태줄_보고(got) -> None: assert "반지름을 바꿨습니다 · 함께 밀린 교점 4곳." in got["onR60"]["status"] assert "곡선 길이를 바꿨습니다 · 함께 밀린 교점 4곳." in got["onL50"]["status"] assert "반지름을 자동으로 되돌렸습니다 · 함께 밀린 교점 " in got["onAuto"]["status"] assert all("못 맞춘 곡선" not in got[f"on{step}"]["status"] for step in STEPS) @pytest.mark.parametrize("step", STEPS) def test_스냅_끔_맞춤_보고_없음(got, step) -> None: row = got[f"off{step}"] assert "함께 밀린" not in row["status"] assert row["planned"] == got["offStart"]["planned"] # 교점 그대로 assert not all(_on_station(mc) for mc in row["mcs"]) def test_고정_교점_곡선은_못_맞춤으로(got) -> None: row = got["lockR60"] assert row["off"] == got["lockStart"]["off"] == 1 assert row["planned"][3] == got["lockStart"]["planned"][3] # 고정 IP3 그대로 mc = row["mcs"][2] n, rest = int(mc // 20), round(mc - int(mc // 20) * 20) assert f"함께 밀린 교점 3곳 · 못 맞춘 곡선 No.{n}+{rest}." in row["status"] assert all(_on_station(row["mcs"][i]) for i in (0, 1, 3)) # 고정 곡선 뒤도 맞춤