"""PLAN 472-4 ① — 측점 열쇠 · 시설 따라감 · 추가 측점 · 꼭짓점 세움 = 서버도 같은 TS(Node 입구). 파이썬 짝을 걷고 서버 부름 자리는 `common_util_calc_runner.run_calc`(등록부 id) 한 길 — profile.seat `Router_Confirm._seat_born_nodes`(측점 병합 · 노선 저장 `_carry_profile`) facility.plus_follow `Plus_Stations.follow_plus_stations`(노선 저장) 고정 자료 = `fixtures/golden_472/detail.json`(472-1 · 읽기만). ./venv/Scripts/python.exe -m pytest resources/tester/test_472_4_follow_node.py -q """ from __future__ import annotations import json import sys from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] sys.path.insert(0, str(ROOT)) from B05_Profile import B05_Profile_Router_Confirm as confirm # noqa: E402 from common_util import common_util_calc_runner as runner # noqa: E402 pytestmark = pytest.mark.skipif( not (ROOT / "config" / "node_modules" / "vite").is_dir(), reason="프론트엔드 의존성 미설치", ) DETAIL = json.loads((HERE / "fixtures" / "golden_472" / "detail.json").read_text(encoding="utf-8")) @pytest.fixture def calls(monkeypatch) -> list[list[str]]: seen: list[list[str]] = [] def spy(ids, inputs): seen.append(list(ids)) return runner.run_calc(ids, inputs) monkeypatch.setattr(confirm, "run_calc", spy) return seen def test_꼭짓점_세움은_Node_입구(calls) -> None: stored = DETAIL["longitudinal"]["profile_alignment"] base = [node for node in stored["base_pvi"] if abs(node["chainage_m"] - 351.0) > 0.5] got = confirm._seat_born_nodes(stored, base, [351.0]) assert calls == [["profile.seat"]] assert len(got) == len(base) + 1 seated = next(node for node in got if abs(node["chainage_m"] - 351.0) < 1e-9) old = next(node for node in stored["base_pvi"] if abs(node["chainage_m"] - 351.0) < 1e-9) assert seated["elevation_m"] == pytest.approx(old["elevation_m"], abs=0.5) def test_세울_자리_없으면_Node_안_부름(calls) -> None: stored = DETAIL["longitudinal"]["profile_alignment"] assert confirm._seat_born_nodes(stored, stored["base_pvi"], [351.0]) == stored["base_pvi"] assert calls == [] def test_추가_측점_따라감은_Node_입구(tmp_path, monkeypatch) -> None: from B05_Profile import B05_Profile_Plus_Stations as plus seen: list[list[str]] = [] def spy(ids, inputs): seen.append(list(ids)) return runner.run_calc(ids, inputs) monkeypatch.setattr(plus, "run_calc", spy) old = [(0.0, 0.0), (100.0, 0.0)] plus.save_plus_stations(tmp_path, [30.0, 60.0], old) new = [(0.0, 5.0), (40.0, 5.0), (100.0, 25.0)] # 땅 위 자리를 새 노선에 투영 moved = plus.follow_plus_stations(tmp_path, new) assert seen == [["facility.plus_follow"]] rows = plus.load_plus_stations(tmp_path) assert moved == 1 and [row["chainage_m"] for row in rows] == [30.0, 57.0] # 옛 파이썬 값 assert [(row["x"], row["y"]) for row in rows] == [(30.0, 0.0), (60.0, 0.0)] # 좌표 그대로 def test_구조물_따라감은_Node_입구(monkeypatch) -> None: from types import SimpleNamespace from common_util import common_util_route_follow as follow seen: list[list[str]] = [] def spy(ids, inputs): seen.append(list(ids)) return runner.run_calc(ids, inputs) monkeypatch.setattr(follow, "run_calc", spy) items = [ SimpleNamespace(chainage_m=60.0, start_m=55.0, end_m=65.0, x=60.0, y=0.0, placement=None), SimpleNamespace(chainage_m=30.0, start_m=None, end_m=None, x=None, y=None, placement=None), ] result = follow.follow_structures(items, [(0.0, 5.0), (40.0, 5.0), (100.0, 25.0)]) assert seen == [["facility.follow"]] assert (items[0].chainage_m, items[0].start_m, items[0].end_m) == (57.0, 52.0, 62.0) assert items[1].chainage_m == 30.0 and result[1] == (None, False) # 좌표 없는 옛 자료 그대로 assert result[0][0] == pytest.approx(11.068, abs=1e-3) and result[0][1] is False # 옛 파이썬 값