- 브라우저 — 노선 바뀐 [저장]의 계획고 본문 = 묶음 계획고 대 저장분 옮김(같은 carryEdits · 이 초안 첫 기준)의 다른 열쇠 · 지운 열쇠만(`gradeTouch` · `routeGradeTouch`) · 못 가르면 옛 길(노선 0 칸 통째) - 서버 — `alignment_cleared` 가 오면 관 · 구조물 · 추가 측점 차례 뒤 새 종단 편집(서버 옮김) 위에 손댐만 얹어 한 번 저장(`touched_alignment` · 얹기 실패도 되돌림 안) - follow 옮김 측점 = stations 단계와 같은 추가 측점(`plus` 넘김) - 재현: 꼬리 추가 측점 ▲ 뒤 앞 교점 끌기 [저장] = 990 m 편집 버림 · ▲ 잃음 · 최대 −0.50 m → 고친 뒤 버림 0 · 끌 때 = 저장 뒤 - 시험: test_498_2_grade_touch 새로 · helper_design_store_save 에 묶음 창구 가짜 하나(옛 길) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
133 lines
5.1 KiB
Python
133 lines
5.1 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""498-2 노선 바뀐 [저장] 계획고 — 브라우저는 손댐(입력)만 · 서버가 자기 옮김 위에 한 번.
|
|
|
|
- 브라우저(`B05_Profile_Pipeline_Grade` follow `touch` · `gradeTouch`) — 노선만 끌었으면 손댐 0 ·
|
|
끈 뒤 ▲ = 그 열쇠만 · 끄는 중 ▲ = 그 열쇠 포함 묶음 값 그대로 · 지운 열쇠 = `cleared` ·
|
|
첫 기준 없으면 null(옛 길 · 0 칸 통째).
|
|
- 서버(`B06_Section_Router_DesignSave_Route.overlay_edits` · `touched_alignment`) — 손대지 않은 옮김
|
|
열쇠는 서버 측점 그대로 · 고친 · 지운 열쇠 자리(0.006 m 안)만 걷고 손댐 값을 얹음 · 브라우저 측점
|
|
열쇠로 서버 열쇠를 덮지 않음(옛 흠 = 두 쪽 측점이 다르면 어긋난 편집을 지움).
|
|
"""
|
|
|
|
import asyncio
|
|
import json
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from B06_Section import B06_Section_Router_DesignSave_Route as moving
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def ts() -> dict:
|
|
proc = subprocess.run(
|
|
["node", str(HERE / "helper_498_2_grade_touch.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=60,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def _empty(touch: dict) -> bool:
|
|
return not any(touch["edits"].values()) and not any(touch["cleared"].values())
|
|
|
|
|
|
def test_노선만_끌면_손댐_0(ts):
|
|
assert ts["plain"]["now"]["station_offsets"], "옮긴 편집이 있어야 시험이 뜻을 가짐"
|
|
assert _empty(ts["plain"]["touch"])
|
|
|
|
|
|
def test_끈_뒤_올림은_그_열쇠만(ts):
|
|
touch = ts["lateBump"]["touch"]
|
|
assert touch["edits"]["station_offsets"] == {"60.000": 0.3}
|
|
assert not any(touch["cleared"].values())
|
|
|
|
|
|
def test_끄는_중_올림은_묶음_값_그대로(ts):
|
|
now, touch = ts["midBump"]["now"], ts["midBump"]["touch"]
|
|
assert touch["edits"]["station_offsets"]["40.000"] == pytest.approx(0.2)
|
|
for key, value in touch["edits"]["station_offsets"].items():
|
|
assert now["station_offsets"][key] == value
|
|
|
|
|
|
def test_지운_열쇠는_cleared(ts):
|
|
touch = ts["removed"]["touch"]
|
|
assert touch["cleared"]["station_offsets"] == ["200.000"]
|
|
assert not any(touch["edits"].values())
|
|
|
|
|
|
def test_첫_기준_없으면_null_옛_길(ts):
|
|
assert ts["noFirst"] is None
|
|
|
|
|
|
def test_gradeTouch_순수(ts):
|
|
assert ts["pure"] == {
|
|
"edits": {"station_offsets": {"20.000": 2.5}, "curve_radii": {"30.000": 80}},
|
|
"cleared": {"station_offsets": ["40.000"], "curve_radii": []},
|
|
}
|
|
|
|
|
|
def test_서버_옮김_위에_손댐만():
|
|
carried = {
|
|
"station_offsets": {"960.000": 0.005, "970.000": 0.64, "980.000": 0.014},
|
|
"curve_radii": {"500.000": 120.0},
|
|
}
|
|
touch = {"station_offsets": {"970.003": 0.7, "1000.000": 0.2}, "curve_radii": {}}
|
|
cleared = moving.AlignmentCleared(station_offsets=["980.000"], curve_radii=["500.004"])
|
|
assert moving.overlay_edits(carried, touch, cleared) == {
|
|
# 960 = 손 안 댐(서버 값) · 970 = 0.006 m 안 손댐이 갈음 · 980 지움 · 1000 새 손댐
|
|
"station_offsets": {"960.000": 0.005, "970.003": 0.7, "1000.000": 0.2},
|
|
"curve_radii": {},
|
|
}
|
|
|
|
|
|
def test_손댐_0이면_서버_옮김_그대로():
|
|
carried = {"station_offsets": {"960.000": 0.005, "970.000": 0.64}, "curve_radii": {}}
|
|
out = moving.overlay_edits(carried, {}, moving.AlignmentCleared())
|
|
assert out == {"station_offsets": carried["station_offsets"], "curve_radii": {}}
|
|
|
|
|
|
def test_계획고_본문은_새_종단_편집에서(tmp_path: Path, monkeypatch: pytest.MonkeyPatch):
|
|
from B03_FileInput import B03_FileInput_Repository as files
|
|
from B05_Profile.B05_Profile_Schema import ProfileAlignmentSaveRequest
|
|
from B06_Section import B06_Section_Repository as repo
|
|
from common_util import common_util_storage as storage
|
|
from config import config_db
|
|
|
|
(tmp_path / "long.json").write_text(
|
|
json.dumps({"profile_alignment": {"edits": {"station_offsets": {"970.000": 0.64}}}}),
|
|
encoding="utf-8",
|
|
)
|
|
|
|
class Pool:
|
|
def acquire(self):
|
|
return self
|
|
|
|
async def __aenter__(self):
|
|
return None
|
|
|
|
async def __aexit__(self, *_):
|
|
return False
|
|
|
|
async def longitudinal(_connection, _project, route_id):
|
|
assert route_id == 820, "새 노선 번호로 읽어야 함"
|
|
return {"longitudinal_file_path": "long.json"}
|
|
|
|
async def stored(_connection, _project):
|
|
return "p"
|
|
|
|
monkeypatch.setattr(config_db, "get_db_pool", lambda: Pool())
|
|
monkeypatch.setattr(repo, "get_longitudinal_section", longitudinal)
|
|
monkeypatch.setattr(files, "get_project_storage_relative_path", stored)
|
|
monkeypatch.setattr(storage, "resolve_stored_project_path", lambda _p: str(tmp_path))
|
|
touch = ProfileAlignmentSaveRequest(route_id=819, station_offsets={"1000.000": 0.2})
|
|
body = asyncio.run(moving.touched_alignment("pid", 820, touch, moving.AlignmentCleared()))
|
|
assert body.route_id == 820
|
|
assert body.station_offsets == {"970.000": 0.64, "1000.000": 0.2}
|