- 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>
111 lines
4.5 KiB
Python
111 lines
4.5 KiB
Python
"""PLAN 449 — B05 [저장] 관 목록 · 노선 저장 뒤 실패(433-7 · 433-12 · 433-6).
|
|
|
|
· 관 정본 PUT = 세션 관 초안 한 번(B06 고침 직후 [저장] = 초안 값) · 배수판 메모리는 안 보냄
|
|
· 초안 없음 = [저장] PUT 없음(판이 안 서도 관 수 그대로) · [확정] = 칸 없이(서버 저장분) · 빈 초안 = 다 지움
|
|
· 노선 바뀐 [저장] 중 구조물 · 횡단 넘김 차례 실패(472-6 6-B) → 서버가 노선까지 되돌림 · 초안 그대로(0 칸
|
|
계획고 그대로 · 다시 안 세움) → 다음 [저장]이 같은 초안을 그대로 다시 보냄(옛 433-12 「새 노선 칸으로 옮김」 갈음)
|
|
· 관 PUT 끝 측점 맞춤 실패 = 응답 `stations_failed`
|
|
짝: `helper_449_save_pipes.cjs`(실제 `design_save.ts` 를 Node 로 · 서버 창구만 가짜).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import json
|
|
import subprocess
|
|
import sys
|
|
from pathlib import Path
|
|
from uuid import uuid4
|
|
|
|
import pytest
|
|
|
|
HERE = Path(__file__).resolve().parent
|
|
ROOT = HERE.parents[1]
|
|
if str(ROOT) not in sys.path:
|
|
sys.path.insert(0, str(ROOT))
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def out() -> dict:
|
|
if not (ROOT / "config" / "node_modules" / "typescript").is_dir():
|
|
pytest.skip("프론트엔드 의존성 미설치")
|
|
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
|
|
["node", str(HERE / "helper_449_save_pipes.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=120,
|
|
)
|
|
assert proc.returncode == 0, proc.stderr
|
|
return json.loads(proc.stdout)
|
|
|
|
|
|
def _saves(case: dict) -> list:
|
|
"""472-6 — 노선 안 바뀐 저장 = 한 본문 창구 한 번 · ["SAVE", 관 칸(points · "none"), 확정]."""
|
|
return [call for call in case["calls"] if call[0] == "SAVE"]
|
|
|
|
|
|
def test_관_PUT_은_초안_한_번(out):
|
|
case = out["draft"]
|
|
assert case["ok"] and _saves(case) == [
|
|
["SAVE", [{"chainage_m": 15, "source": "user", "id": "p1"}], False]
|
|
]
|
|
assert case["calls"].index(["RELOAD"]) > case["calls"].index(
|
|
_saves(case)[0]
|
|
) # 화면은 뒤에 받기만
|
|
assert case["left"] is None
|
|
|
|
|
|
def test_초안_없으면_PUT_없음(out):
|
|
assert out["noDraft"]["ok"] and _saves(out["noDraft"]) == [["SAVE", "none", False]]
|
|
|
|
|
|
def test_확정은_칸_없이_다시_셈(out):
|
|
assert _saves(out["confirmNoDraft"]) == [["SAVE", None, True]]
|
|
|
|
|
|
def test_빈_초안은_다_지움(out):
|
|
assert _saves(out["emptyDraft"]) == [["SAVE", [], False]]
|
|
|
|
|
|
def test_노선_저장_뒤_실패_초안_새_노선(out):
|
|
"""472-6 6-B — 한 본문 · 서버가 구조물 차례에서 실패하면 노선까지 되돌림 → 초안 그대로 · 다시 안 세움."""
|
|
case = out["routeFail"]
|
|
assert not case["ok"] and "구조물 차례에서 실패" in case["failure"]
|
|
assert case["calls"] == [["SAVE", "none", False, "ROUTE"], ["ALIGN", 7, {"pvi": {"120": 1.5}}]]
|
|
assert out["alignAfterFail"] == {"0": {"pvi": {"120": 1.5}}} # 노선 초안 칸 그대로
|
|
align = [call for call in out["nextSave"]["calls"] if call[0] == "ALIGN"]
|
|
assert align == [["ALIGN", 7, {"pvi": {"120": 1.5}}]] # 다음 [저장]이 그대로 다시
|
|
assert (
|
|
out["nextSave"]["calls"][-1] == ["REBUILD"] and ["ROUTE_SAVED"] in out["nextSave"]["calls"]
|
|
)
|
|
|
|
|
|
def test_넘김_실패면_두_번_안_넘김(out):
|
|
"""472-6 6-B — 넘김은 서버 한 본문 안(Node) · 실패 = 노선까지 되돌림 · 화면 넘김 · 다시 세움 없음."""
|
|
case = out["carryFail"]
|
|
assert not case["ok"] and "횡단 차례에서 실패" in case["failure"]
|
|
assert case["calls"] == [["SAVE", "none", False, "ROUTE"]]
|
|
|
|
|
|
def test_공용_저장이_판_메모리를_안_보냄():
|
|
shared = (ROOT / "A00_Common/design/design_shared.ts").read_text(encoding="utf-8")
|
|
panel = (ROOT / "B05_Profile/B05_Profile_UI_Drainage_Panel.ts").read_text(encoding="utf-8")
|
|
assert "savePipes" not in shared and "saveDetailPipePoints" not in panel
|
|
|
|
|
|
def test_측점_맞춤_실패는_응답에(monkeypatch):
|
|
from B04_PreProcess import B04_PreProcess_Router_Basins as basins
|
|
from B06_Section import B06_Section_Router_Stations as stations
|
|
|
|
async def boom(_project_id):
|
|
raise RuntimeError("샘플링 없음")
|
|
|
|
async def fine(_project_id):
|
|
return 1
|
|
|
|
monkeypatch.setattr(stations, "ensure_structure_stations", boom)
|
|
assert asyncio.run(basins._stand_pipe_stations(uuid4())) is False
|
|
monkeypatch.setattr(stations, "ensure_structure_stations", fine)
|
|
assert asyncio.run(basins._stand_pipe_stations(uuid4())) is True
|