"""PLAN 483-3(요구 280-4) 「유지」 — 손대지 않은 곡선은 MC 꼴(N+0 · N+10) 그대로 · 손으로 고친 곡선만 고른 간격. `logic/B05_Profile_RouteEdit_StationFit.ts`(`fitAfter` · `fitDrag` · `offStationCount`)를 `helper_280_4_fit.cjs` 로 돌림 — 분석 D 재현 2 노선. 파이썬 짝 아님(서버는 저장 IP 그대로). """ from __future__ import annotations import json import subprocess from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] B05 = ROOT / "B05_Profile" pytestmark = pytest.mark.skipif( not (ROOT / "config" / "node_modules" / "typescript").is_dir(), reason="프론트엔드 의존성 미설치", ) @pytest.fixture(scope="module") def got() -> dict: run = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HERE / "helper_280_4_fit.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=120, ) assert run.returncode == 0, run.stderr return json.loads(run.stdout) def _near(values: list[float], want: list[float]) -> bool: return values == pytest.approx(want, abs=0.05) def test_측점_No_맞춤(got) -> None: assert _near(got["mc"]["s1"], [100, 180, 280, 380, 460]) def test_10m_손으로_N_10(got) -> None: assert got["pushed"] is not None assert _near(got["mc"]["s2"], [100, 190, 280, 380, 460]) def test_간격만_측점_No_로_바꿔도_남음(got) -> None: assert _near(got["mc"]["s3"], [100, 190, 280, 380, 460]) def test_앞_교점_옮긴_뒤에도_남음(got) -> None: assert _near(got["mc"]["s4"], [100, 190, 280, 380, 460]) # 측점 No. assert _near(got["mc"]["s5"], [100, 190, 280, 380, 460]) # 10 m · N+0 곡선은 N+10 으로 안 감 def test_기준이_고침을_잼(got) -> None: """before 없이(고치기 전 동작) 같은 편집 = 특별측점 사라짐 → 위 시험이 새 길을 거침.""" assert got["mc"]["s7"][1] == pytest.approx(180, abs=0.05) def test_그_곡선을_손으로_밀면_고른_간격(got) -> None: assert _near(got["mc"]["s6"], [100, 200, 280, 380, 460]) # N+0 = 특별측점 없어짐 def test_N_10_은_측점_밖이_아님(got) -> None: assert got["off"] == {"s2no": 0, "s2ten": 0, "s4": 0} def test_끄는_중_미리보기_놓을_때_같음(got) -> None: assert got["dragGap"] < 1e-6 assert _near(got["previewMc"], [100, 190, 280, 380, 460]) def test_편집기_이음() -> None: edit = (B05 / "B05_Profile_UI_RouteEdit.ts").read_text(encoding="utf-8") # 511-A 계산 자리 대조(같은 인자) call = ( '"route.station_fit", fit.fitAfter, [\n' " before,\n snapshotNow(),\n fitLimits()," ) assert call in edit assert "fit.fitDrag(snapshotNow(), base.planned, index, fitLimits(), base)" in edit assert "stationM: stationIntervalM" in edit assert len(edit.splitlines()) <= 695