- 반사경 → 가드레일(길이 빈칸)이 옛 것을 먼저 지우고 새 것은 못 놓아 반사경이 사라지던 것 — 패널 addAt 이 놓였는지 돌려줌 · Events replaceAt = 먼저 놓고 확인 뒤 옛 것 지움 · 못 놓으면 옛 기록 폼(관은 관 폼) + 「반사경 그대로 — 가드레일(으)로 못 바꿈」 - commit 의 조용한 return 여섯 곳 = 막힌 칸으로 커서 + [추가] 때 「「길이」 칸이 비었거나 맞지 않아 못 놓음」(실시간 반영은 알림 없음) - 패널 줄 수 지킴(setter 한 줄로 · 697) - 시험: test_319 바꿈 막힘 둘(구조물 · 관) · 비동기 바꿈 · test_323_6 막힌 칸 알림 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
142 lines
5.7 KiB
Python
142 lines
5.7 KiB
Python
"""PLAN 319 — 횡단배수 종류 바꿈이 맹암거 · 개거처럼 정본이 다른 종류에서도 먹음(요구 217).
|
|
|
|
- 패널: 관 지점 ↔ 구조물로 바꿈 = 옛 것 지움(「없음」) → 같은 측점에 새 종류(우클릭 추가 길 `addAt`)
|
|
· 그 측점 옛 조정값 버림 · 처음 종류로 되돌리면 비울 자리 기록이 빠짐 · 같은 정본끼리는 종전 그대로
|
|
- 횡단 초안 맞춤: 일반 측점 자리(grid)도 종류가 바뀌거나 빠지면 구조물만 뺀 사본(측점 남음 · 고친 값
|
|
그대로)에 새 종류 · 저장분 종류로 되돌리면 저장분 그대로
|
|
|
|
실제 TS 를 `helper_319_kind_swap.cjs` 가 Node 로 돌린다(서버 부르는 곳 흉내).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
import subprocess
|
|
|
|
import pytest
|
|
|
|
HERE = os.path.dirname(__file__)
|
|
ROOT = os.path.join(HERE, "..", "..")
|
|
|
|
pytestmark = pytest.mark.skipif(
|
|
not os.path.isdir(os.path.join(ROOT, "config", "node_modules")),
|
|
reason="프론트엔드 의존성 미설치",
|
|
)
|
|
|
|
|
|
@pytest.fixture(scope="module")
|
|
def result() -> dict:
|
|
out = subprocess.run(
|
|
["node", os.path.join(HERE, "helper_319_kind_swap.cjs")],
|
|
capture_output=True,
|
|
text=True,
|
|
encoding="utf-8",
|
|
timeout=120,
|
|
check=True,
|
|
)
|
|
return json.loads(out.stdout)
|
|
|
|
|
|
def _src(*parts: str) -> str:
|
|
with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle:
|
|
return handle.read()
|
|
|
|
|
|
def test_pipe_to_underdrain_removes_pipe_and_places_at_same_station(result: dict) -> None:
|
|
swap = result["pipe_to_underdrain"]
|
|
assert swap["removed"] == [80]
|
|
assert swap["pipeSet"] == [] # 새 것이 놓인 뒤 옛 관만 지움(고름은 새 것 · 323-6 고침)
|
|
assert swap["addAt"] == [[80, "cross_underdrain"]]
|
|
assert swap["reset"] == 0 and swap["live"] == 0 # 새로 놓기 폼으로 빠지지 않음
|
|
assert swap["kindreset"] == {"80.00": "pipe"} # [저장] 때 그 측점 옛 조정값 비움(318-4)
|
|
assert swap["culvertopt"] == {"300.00": {"b": 2}} # 그 측점 세션 조정값만 버림
|
|
|
|
|
|
def test_back_to_first_kind_drops_reset_mark(result: dict) -> None:
|
|
back = result["underdrain_back_to_pipe"]
|
|
assert back["left"] == [] and back["emit"] == 1 and back["editingSet"] == []
|
|
assert back["addAt"] == [[80, "pipe"]]
|
|
assert back["kindreset"] is None
|
|
|
|
|
|
def test_structure_to_pipe_kind(result: dict) -> None:
|
|
swap = result["open_ditch_to_box"]
|
|
assert swap["left"] == ["sign"] # 고른 개거만 빠짐
|
|
assert swap["removed"] == []
|
|
assert swap["addAt"] == [[120, "box_culvert"]]
|
|
assert swap["kindreset"]["120.00"] == "open_ditch"
|
|
|
|
|
|
def test_same_owner_switch_unchanged(result: dict) -> None:
|
|
for case in ("pipe_to_box", "underdrain_to_open_ditch"):
|
|
same = result[case]
|
|
assert same["addAt"] == [] and same["removed"] == [] and same["live"] == 1
|
|
assert result["to_blank"]["reset"] == 1 and result["to_blank"]["addAt"] == []
|
|
|
|
|
|
def test_point_interval_switch_replaces_at_same_anchor(result: dict) -> None:
|
|
# 323-6 — 표 규칙(`swapsInPlace`) · 구간 = 기본 길이(`addAt`)
|
|
up = result["point_to_interval"]
|
|
assert up["left"] == [] and up["addAt"] == [[140, "guardrail"]] and up["live"] == 0
|
|
down = result["interval_to_point"]
|
|
assert down["left"] == [] and down["addAt"] == [[105, "mirror"]]
|
|
same = result["interval_same"]
|
|
assert same["addAt"] == [] and same["live"] == 1 and same["left"] == ["work_yard"]
|
|
|
|
|
|
def test_replace_keeps_old_when_new_cannot_be_placed(result: dict) -> None:
|
|
# 반사경 → 가드레일(빈 칸 때문에 바로 못 놓음) = 반사경 · 반사경 폼 그대로 + 알림 한 줄(조용히 사라지던 것)
|
|
kept = result["replace_blocked"]
|
|
assert kept["left"] == ["mirror"] and kept["addAt"] == [[300, "guardrail"]]
|
|
assert kept["loaded"] == ["mirror"] and kept["emit"] == 0
|
|
assert len(kept["toasts"]) == 1 and "반사경" in kept["toasts"][0][0]
|
|
pipe = result["replace_blocked_pipe"]
|
|
assert pipe["removed"] == [] and pipe["loaded"] == ["pipe@200"] and len(pipe["toasts"]) == 1
|
|
|
|
|
|
def test_blocked_switch_restores_with_notice(result: dict) -> None:
|
|
blocked = result["blocked"]
|
|
assert blocked["left"] == ["mirror"] and blocked["loaded"] == ["mirror"]
|
|
assert len(blocked["toasts"]) == 1 and blocked["toasts"][0][1] == "error"
|
|
|
|
|
|
def test_grid_station_kind_change_uses_bare_copy(result: dict) -> None:
|
|
# 일반 측점 자리 = 측점이 남음 — 저장분에서 구조물만 뺀 사본에 새 종류(지형 쪽 고친 값 그대로)
|
|
grid = result["grid"]
|
|
assert grid["pipe_to_underdrain"] == {
|
|
"count": 3,
|
|
"keys": [],
|
|
"structure": "맹암거",
|
|
"preview": True,
|
|
"fresh": False,
|
|
"keep": 1,
|
|
}
|
|
assert grid["pipe_to_box"]["keys"] == ["box"] and grid["pipe_to_box"]["keep"] == 1
|
|
assert grid["underdrain_to_pipe"]["keys"] == ["culvert"]
|
|
assert grid["underdrain_to_pipe"]["structure"] == "파형강관 D800"
|
|
assert grid["underdrain_to_pipe"]["keep"] == 1
|
|
|
|
|
|
def test_grid_station_emptied_keeps_station_without_facility(result: dict) -> None:
|
|
assert result["grid"]["pipe_deleted"] == {
|
|
"count": 3,
|
|
"keys": [],
|
|
"structure": None,
|
|
"preview": True,
|
|
"fresh": False,
|
|
"keep": 1,
|
|
}
|
|
|
|
|
|
def test_grid_station_back_to_saved_kind_restores_saved(result: dict) -> None:
|
|
back = result["grid"]["pipe_back"]
|
|
assert back["keys"] == ["culvert"] and not back["preview"] and back["keep"] == 1
|
|
|
|
|
|
def test_panel_wires_add_at() -> None:
|
|
panel = _src("B05_Profile", "B05_Profile_UI_Structures_Panel.ts")
|
|
assert "addAt: (at, typeId) => api.addAt(at, typeId)" in panel
|
|
assert "return api;" in panel
|
|
assert len(panel.splitlines()) <= 700
|