Files
Aislo/resources/tester/test_429_save_path.py
T
eomsangdonandClaude Opus 5.5 4b6800e387 feat(472-6 6-B 노선 바뀐 [저장] 한 본문): 노선 저장 · 관 · 구조물 다시 얹기 · 횡단 고침 넘김을 서버 한 창구로 — 전부 아니면 없음
- POST /design/save 에 route(노선 초안) · carry(옛 노선 세션 칸) 칸 · 서버 = 노선 저장 → Node facility.rebase(rebasePipeDraft · rebaseStructureDraft) → 6-A 차례(새 노선) → Node stations.carry(routeCarryPatches) → 횡단 저장
- 되돌림 범위 = 노선 행 · 자식 표 · 단계 · 배수 폴더 · 노선 파일 · 지표면 확정(설계 지형 바꿈)
- 손 상단측은 안 넘김(요구 274) · 노선 바뀐 [확정] = [저장](새 노선 본 뒤 다시)
- 브라우저 = 노선 초안 + 사용자 칸만 한 본문 · 옛 차례 길(saveRouteDraft 뒤 flush · carryAfterRouteSave · keepDraftsOnNewRoute) · 부름 0 된 flush 창구 여섯 · saveCachedCrossPatches 걷음
- 등록부 R9 stations.carry · F10 facility.rebase · 옛 차례 단언 시험 한 본문 꼴로 · test_472_6_route_save(옛 길 = 새 길 · 실패 되돌림)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:07:12 +09:00

154 lines
7.1 KiB
Python

"""PLAN 429 — [저장] 길 정합(분석 424 9장 ④⑤⑧⑨).
- ④ 노선 [저장] 뒤 횡단 저장 = 고침이 없어도 한 번(서버가 지금 종단으로 횡단을 다시 셈 · 뒤에
나간 계획고 초안이 횡단 설계 높이에 실림) · 서버 [저장]이 횡단 설계 높이를 저장 종단에서 냄.
- ⑤ 상단측 차례(`sync_uphill_overrides_into_designs`)가 소단 · 깎기 경사 · 조절 값을 남김.
- ⑨ 추가 측점 = 계산 묶음 입력(stations 부터 다시) · 가게 초안 바뀜 · 되돌리기 → 묶음에 넣음.
[저장] 차례 실패 = 그 차례에서 멈춤(`test_design_store_save.py`).
Node 헬퍼 = `helper_429_save_path.cjs`.
"""
import asyncio
import json
import subprocess
from pathlib import Path
import pytest
from B06_Section.B06_Section_Engine_Design import compute_cross_design
from B06_Section.B06_Section_Router_Design import enforce_design_lines
from config.config_system import STANDARD_CROSS_SECTION
HERE = Path(__file__).parent
ROOT = HERE.parents[1]
@pytest.fixture(scope="module")
def out():
proc = subprocess.run(
["node", str(HERE / "helper_429_save_path.cjs")],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def _ground() -> list[dict]:
"""왼쪽이 높은 비탈 — 왼쪽 깎기 · 오른쪽 쌓기(편절)."""
return [
{"offset_m": o / 2, "elevation_m": round(100.0 + 0.25 * o / 2, 4), "valid": True}
for o in range(-40, 41)
]
# ── ④ ────────────────────────────────────────────────────────────────────────
def test_노선_저장_뒤_고침이_없어도_횡단_저장_한_번(out):
"""472-6 6-B — 노선 바뀐 [저장]은 서버가 횡단 차례를 늘 한 번(넘긴 patch 가 없어도 · 뒤에 나간 계획고로
다시 셈) · 브라우저 횡단 조각 = 풍선 자리 늘 실음(445 H5 — 횡단 보기 [저장]과 같은 본문)."""
server = (ROOT / "B06_Section" / "B06_Section_Router_DesignSave.py").read_text(encoding="utf-8")
moved = server[server.index("carry_cross(project_id, route_id") :]
moved = moved[: moved.index(" elif request.cross is not None:")]
assert 'body["cross_patches"] = carried.get("patches") or None' in moved
assert "crossing = save_sections(project_id, route_id" in moved
assert 'cross = await step("cross", crossing)' in server.replace("\n", " ").replace(" ", "")
apply = (ROOT / "B05_Profile" / "B05_Profile_UI_RouteEdit_Apply.ts").read_text(encoding="utf-8")
part = apply[apply.index("export function routeCarryPart") :]
assert "balloon_offsets: balloonOffsetsPayload()," in part
def test_서버_저장이_횡단_높이를_저장_종단에서(tmp_path):
"""초안 계획고(101.2)가 종단 정본에 나간 뒤 [저장] 다시 셈 = 그 높이 · 옛 설계(100.0) 아님."""
old = compute_cross_design(
_ground(), 100.0, standard=STANDARD_CROSS_SECTION, ground_type="soil",
section_mode="left_cut", ditch_side="left",
) # fmt: skip
longitudinal = {
"design_profiles": [
{"samples": [{"chainage_m": 0.0, "elevation_m": 101.2},
{"chainage_m": 40.0, "elevation_m": 101.2}]}
]
} # fmt: skip
sections = [{"chainage_m": 20.0, "samples": _ground(), "design": old}]
assert enforce_design_lines(longitudinal, sections, tmp_path, STANDARD_CROSS_SECTION) == 1
assert sections[0]["design"]["design_elevation_m"] == pytest.approx(101.2, abs=1e-6)
# ── ⑤ ────────────────────────────────────────────────────────────────────────
def test_상단측_차례가_소단_깎기경사_조절값_남김(tmp_path, monkeypatch):
import B05_Profile.B05_Profile_Router_Confirm as confirm
import B06_Section.B06_Section_Repository as repo
import B06_Section.B06_Section_Repository_Bulk as bulk
import B06_Section.B06_Section_Router_Design as design_mod
user = {
"cut_slope_ratio_user": 0.7,
"berm": {"width_m": 1.0, "interval_m": 3.0, "slope_deg": 0.0, "depth_m": 1.0},
"berm_depth_left_m": 2.0,
"basin_adjust": {"w": 1.5},
"structure_note": "글",
"shoulder_extend_right_m": 0.3,
}
before = compute_cross_design(
_ground(), 100.0, standard=STANDARD_CROSS_SECTION, ground_type="soil",
section_mode="right_cut", ditch_side="right",
) # fmt: skip
stored = {**before, **user}
long_file = tmp_path / "long.json"
long_file.write_text("{}", encoding="utf-8")
async def designs(_connection, _route_id):
return [{"chainage_m": 20.0, "design": stored}]
async def longitudinal(*_args):
return {"data": {"options": {"standard_cross_section": STANDARD_CROSS_SECTION}}}
written: list = []
async def merge(_connection, *, route_id, entries, replace, project_id=None):
written.extend(entries)
monkeypatch.setattr(repo, "get_cross_section_designs", designs)
monkeypatch.setattr(confirm, "get_longitudinal_section", longitudinal)
monkeypatch.setattr(bulk, "merge_cross_section_designs", merge)
monkeypatch.setattr(design_mod, "resolve_longitudinal_path", lambda *_: long_file)
monkeypatch.setattr(
design_mod, "read_cross_design_inputs", lambda *_a: (_ground(), 100.0, False, {})
)
monkeypatch.setattr(design_mod, "ford_surface_drops", lambda _root: [])
asyncio.run(
confirm.sync_uphill_overrides_into_designs(
None, "p", 1, tmp_path, "long.json", [{"chainage_m": 20.0, "side": "left"}]
)
)
assert len(written) == 1
chainage, after = written[0]
assert chainage == 20.0 and before["ditch_type"] is None # 측구 끔 저장분도 셈
for key, value in user.items():
assert after[key] == value, key # 값 남음
assert after["cut_slope_ratio"] == pytest.approx(0.7) # 깎기 경사가 기하에 실림(키만 아님)
assert after["shoulder_extend_right_m"] == pytest.approx(0.3)
# ── ⑨ ────────────────────────────────────────────────────────────────────────
def test_추가_측점이_묶음_입력(out):
assert "input.plusStations" in out["stationsDeps"]
assert out["plusRan"][0] == "stations" and "route" not in out["plusRan"] # stations 부터
assert "cross" in out["plusRan"] # 뒤 단계까지 이어짐
assert out["plusSeen"] == [40]
assert out["sameValue"] is None and out["sameRan"] == [] # 같은 값은 안 셈
def test_가게_추가_측점_초안이_묶음에(out):
assert out["fed"] == [["plus", [12, 30]], ["plus", [12]]] # 초안 · 비우면 저장분
assert out["restoreFed"] == [["plus", [12, 55]]] # 되돌리기 = 입력과 다르면 넣음