Files
Aislo/resources/tester/test_449_save_pipes.py
T
eomsangdonandClaude Opus 5.5 63f994972c feat(472-6 6-A 브라우저 한 본문): 노선 안 바뀐 [저장] · [확정] = 차례 조각을 모아 /design/save 한 요청
- design_save: 노선 초안이 없으면 saveOneBody — 차례마다 조각(본문 · 저장 뒤 정리) · 서버가 다 저장한 뒤에만 정리(초안 비움 · 화면 맞춤) · 실패 = 차례 이름째 「아무것도 저장 안 함」 · 되돌림 실패 = 「복구 확인 필요」 · 기다림 알림
- 조각 함수: 페이지 설정 · 관 · 구조물(보기 목록) · 추가 측점 · 계획고(한 벌 종단도) · 관 옵션 · 상단측 · 횡단(횡단 보기) — 옛 flush 함수는 노선 바뀐 저장 길에 그대로(조각을 써서 같은 본문)
- 보기 몫 steps · cross → parts · crossPart · 노선 바뀐 저장 길은 옛 차례 그대로(6-B)
- 옛 9 차례를 단언하던 시험 10 개를 한 본문 꼴로(옛 PUT 줄 → SAVE 줄 · 조각 정리 차례) · 위키 [저장] 표

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

107 lines
4.0 KiB
Python

"""PLAN 449 — B05 [저장] 관 목록 · 노선 저장 뒤 실패(433-7 · 433-12 · 433-6).
· 관 정본 PUT = 세션 관 초안 한 번(B06 고침 직후 [저장] = 초안 값) · 배수판 메모리는 안 보냄
· 초안 없음 = [저장] PUT 없음(판이 안 서도 관 수 그대로) · [확정] = 칸 없이(서버 저장분) · 빈 초안 = 다 지움
· 노선 저장 뒤 구조물 차례 실패 → 0 칸 계획고가 새 노선 칸에 · 횡단 초안 넘김(저장 없이) · 다시 세움 →
다음 [저장]이 새 노선으로 계획고를 보냄 · 넘김 자체가 실패하면 두 번 안 넘김
· 관 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):
case = out["routeFail"]
assert not case["ok"] and "구조물" in case["failure"]
assert case["calls"] == [["ROUTE"], ["CARRY", 7, 8, False], ["REBUILD"]]
assert out["alignAfterFail"] == {"8": {"pvi": {"120": 1.5}}} # 0 칸 비움 · 새 노선 칸
align = [call for call in out["nextSave"]["calls"] if call[0] == "ALIGN"]
assert align == [["ALIGN", 8, {"pvi": {"120": 1.5}}]]
def test_넘김_실패면_두_번_안_넘김(out):
case = out["carryFail"]
assert not case["ok"]
assert [call for call in case["calls"] if call[0] == "CARRY"] == [["CARRY", 7, 8, True]]
assert case["calls"][-1] == ["REBUILD"]
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