Files
Aislo/resources/tester/test_498_2_grade_touch.py
T
eomsangdonandClaude Opus 5.5 b92fa06f34 fix(B05·B06): 노선 고친 [저장] 계획고 = 서버 옮김 위에 사용자 손댐만 (PLAN 498-2 · 요구 411~417)
- 브라우저 — 노선 바뀐 [저장]의 계획고 본문 = 묶음 계획고 대 저장분 옮김(같은 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>
2026-10-28 21:51:29 +09:00

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}