- ① 관 옮김 = 편집 키도 새 자리로: B06 movePipeTo 가 resetStationEdit(옛, 새) · B05 측점 갱신이 사라진 것 하나 · 새것 하나(또는 같은 id · 0.006 m 안)를 짝으로 옮김(followedStation) — 세션 초안도 새 키라 옛 키가 다시 안 나감 - ② [저장](PUT profile-alignment)에서 서버가 측점 · 관 · 구조물 자리 어디에도 없는 편집 키를 버림(drop_unplaced_edits · 저장 차례가 관 · 구조물 뒤라 최신) · 응답 dropped_edit_chainages · 화면 알림 「계획고 편집 N곳을 지웠습니다」 · 자리 못 읽으면 안 버림 - ③ test_192_1 = 고정 사본(fixtures/b05_192_1_moved_pipe_longitudinal.json · 226fc805 460~840 m) · 새 시험 test_213_2 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3mdb91125mPoC6tpsQx4P
122 lines
5.4 KiB
Python
122 lines
5.4 KiB
Python
"""192-1 — 종단 계획선 숨은 꺾임(검증_랩탑메인 226fc805 · No.38+9).
|
||
|
||
까닭: 관을 769.28 → 769.38(0.1m) 옮김 → 「측점 있음」 판정 폭이 0.5m 라 측점이 옛 자리
|
||
769.28 에 남음 → B06(측점 769.28)과 B05(관 769.38) 두 자리에 계획고 편집이 남아 0.1m 사이
|
||
−465% 계단 · 769.38 은 측점선 없는 꼭짓점.
|
||
고침: 판정 폭 0.005m(측점 스냅 폭) · 측점을 다시 뜨면 사라진 측점의 꼭짓점 · 편집을 지움
|
||
(반올림 차이 0.006m 안은 옮김). 자료 = 고정 사본(fixtures/b05_192_1_moved_pipe_longitudinal.json ·
|
||
226fc805 종단 460~840m · 측점 769.28 인 192 이전 상태 · 213-2) — 사용자 storage 에 기대지 않음.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import copy
|
||
import json
|
||
import shutil
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||
if str(PROJECT_ROOT) not in sys.path:
|
||
sys.path.insert(0, str(PROJECT_ROOT))
|
||
|
||
import B05_Profile.B05_Profile_Engine_Sections as sections # noqa: E402
|
||
from B05_Profile.B05_Profile_Engine_Grade_Profile import rebuild_alignment_profile # noqa: E402
|
||
from B05_Profile.B05_Profile_Router_Confirm import _merge_irregular_into_longitudinal # noqa: E402
|
||
from B06_Section.B06_Section_Router_Stations import _missing_marks # noqa: E402
|
||
|
||
SOURCE = PROJECT_ROOT / "resources/tester/fixtures/b05_192_1_moved_pipe_longitudinal.json"
|
||
REL = "B06_Section/longitudinal/longitudinal.json"
|
||
|
||
|
||
def _copy(tmp_path: Path) -> Path:
|
||
target = tmp_path / REL
|
||
target.parent.mkdir(parents=True)
|
||
shutil.copyfile(SOURCE, target)
|
||
return target
|
||
|
||
|
||
def _load(path: Path) -> dict:
|
||
return json.loads(path.read_text(encoding="utf-8"))
|
||
|
||
|
||
def _irregular(data: dict, old: float, new: float) -> list[dict]:
|
||
"""다시 뜬 비정규 측점 목록 흉내 — `old` 측점만 `new` 로 옮김."""
|
||
result = []
|
||
for station in data["stations"]:
|
||
if station.get("kind") != "irregular":
|
||
continue
|
||
item = copy.deepcopy(station)
|
||
if abs(item["chainage_m"] - old) < 1e-6:
|
||
item["chainage_m"] = new
|
||
result.append(item)
|
||
return result
|
||
|
||
|
||
def test_원본_자료가_숨은_꼭짓점을_가짐() -> None:
|
||
"""까닭 근거 — 769.38 꼭짓점은 측점 목록에 없고 두 편집 사이 −465%."""
|
||
data = _load(SOURCE)
|
||
alignment, _ = rebuild_alignment_profile(data, data["profile_alignment"]["edits"])
|
||
stations = [s["chainage_m"] for s in data["stations"]]
|
||
pvis = [p["chainage_m"] for p in alignment["pvi"]]
|
||
assert 769.38 in pvis and 769.38 not in stations and 769.28 in stations
|
||
assert {"769.280", "769.380"} <= set(alignment["edits"]["station_offsets"])
|
||
step = next(s for s in alignment["segments"] if s["from_m"] == 769.28)
|
||
assert step["to_m"] == 769.38 and step["grade_percent"] < -400
|
||
|
||
|
||
def test_관_옮긴_측점_다시_뜨면_숨은_꼭짓점_사라짐(tmp_path: Path) -> None:
|
||
path = _copy(tmp_path)
|
||
data = _load(path)
|
||
summary = _merge_irregular_into_longitudinal(tmp_path, REL, _irregular(data, 769.28, 769.38))
|
||
after = _load(path)
|
||
alignment = after["profile_alignment"]
|
||
stations = {round(s["chainage_m"], 3) for s in after["stations"]}
|
||
near = [p for p in alignment["pvi"] if 760 < p["chainage_m"] < 780]
|
||
assert [p["chainage_m"] for p in near] == [769.38] # 꼭짓점 하나 · 측점선 있음
|
||
assert 769.38 in stations and 769.28 not in stations
|
||
assert alignment["edits"]["station_offsets"] == {"769.380": pytest.approx(0.051428)}
|
||
assert all(abs(b["chainage_m"] - 769.28) > 1e-6 for b in alignment["base_pvi"])
|
||
grades = [s["grade_percent"] for s in alignment["segments"] if 760 <= s["from_m"] < 780]
|
||
assert max(abs(g) for g in grades) < 20
|
||
assert summary is not None and summary["max_grade_pct"] < 20
|
||
assert after["design_profiles"][0]["summary"]["max_grade_pct"] == summary["max_grade_pct"]
|
||
|
||
|
||
def test_반올림_차이만_다르면_편집을_옮김(tmp_path: Path) -> None:
|
||
path = _copy(tmp_path)
|
||
data = _load(path)
|
||
data["profile_alignment"]["edits"]["station_offsets"]["484.210"] = 0.3
|
||
path.write_text(json.dumps(data), encoding="utf-8")
|
||
_merge_irregular_into_longitudinal(tmp_path, REL, _irregular(data, 484.21, 484.213))
|
||
offsets = _load(path)["profile_alignment"]["edits"]["station_offsets"]
|
||
assert "484.210" not in offsets and offsets["484.213"] == pytest.approx(0.3)
|
||
|
||
|
||
def test_측점이_그대로면_계획선을_건드리지_않음(tmp_path: Path) -> None:
|
||
path = _copy(tmp_path)
|
||
data = _load(path)
|
||
before = data["profile_alignment"]
|
||
assert _merge_irregular_into_longitudinal(tmp_path, REL, _irregular(data, -1, -1)) is None
|
||
assert _load(path)["profile_alignment"] == before
|
||
|
||
|
||
def test_조금_옮긴_관도_측점_없음으로_셈(tmp_path: Path, monkeypatch) -> None:
|
||
"""0.1m 옮긴 관 — 옛 판정(0.5m)은 「측점 있음」으로 놓쳤음."""
|
||
target = tmp_path / REL
|
||
target.parent.mkdir(parents=True)
|
||
target.write_text(
|
||
json.dumps({"stations": [{"chainage_m": 760.0}, {"chainage_m": 769.28}]}),
|
||
encoding="utf-8",
|
||
)
|
||
monkeypatch.setattr(sections, "_load_route_polyline", lambda *_: [])
|
||
monkeypatch.setattr(sections, "_load_pipe_points", lambda *_: [])
|
||
monkeypatch.setattr(
|
||
sections, "resolve_extra_stations", lambda *_: [(769.38, "파형강관 D800"), (760.0, "옹벽")]
|
||
)
|
||
assert _missing_marks(tmp_path, "route.csv") == [
|
||
{"chainage_m": 769.38, "label": "파형강관 D800"}
|
||
]
|