Files
Aislo/resources/tester/test_299_ford_span_pipe.py
T
eomsangdonandClaude Opus 5.5 37a738aff4 fix(458 B06 노선 바뀜): 주인 측점 거리 상한 0.51 m · 시종점 밖 시설 안 얹음 · 갈아끼움 grid 지움 · 늘 다시 얹기 · 이름 = 계획 제목
- 관 세트 주인 측점 = 0.51 m 안 가장 가까운 측점(TS attachCulvertSets ↔ 파이썬 attach_culvert_specs) · 넘으면 연동 이웃에도 안 얹고 missed
- 형상 얹기 한 벌(attachShapes ↔ attach_shapes) — 시종점 측점 자리인데 좌표가 노선 진행 반대쪽 0.51 m 넘게 바깥 = 「노선 밖」 · 안 얹음 · 안 얹은 것 돌려줌(서버 = 로그 경고)
- 측점 갈아끼움 — 격자 아닌 자리면 앞 횡단 grid 표시 지움 · 갈아끼움 뒤 가게 초안 없어도 늘 다시 얹음 · 카드 · 종단 측점 이름 = 계획 제목 · 안 얹은 시설 알림(바뀐 때 한 번)
- 시험: test_458_swap_attach(재현 A · D) · test_323_4 거울 moved 경우(상한 · 시종점 밖 · 연동 폭) · test_299 같은 키 견본 = 주인 0.51 m 안으로 · 273_2 흉내 돌려줌

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

147 lines
6.3 KiB
Python

"""299-1 물넘이 연동 범위 안 측점의 자기 관 — 같은 측점에 함께 얹힘 · 그림 · 수량 한 번.
까닭 — `attach_culvert_sets` 가 측점마다 첫 맞는 시설 하나만 얹고 멈춰, 검증 프로젝트 95 m 배관
(관경 800 · 도수로)이 90 m 물넘이포장(폭 10 → 85~95 m 연동)에 가려 횡단도 · 관 연장에서 빠졌다.
못박는 것
- 키가 다른 시설(배관 culvert · 세월교 ford · 박스 box · 물넘이 ford_pavement)은 같은 측점에 함께.
- 같은 키는 소유 측점 것이 옆 측점 연동보다 먼저 · 세월교 · 박스 흙막이(guard)는 자기 관과
부딪히면 관 먼저(흙막이 없는 자리는 그대로 흙막이).
- 수량 = 소유 측점 한 번 — 소비처(B06 `Structure_Layouts` · B08)가 `chainage_m` 의 가장 가까운
측점만 세므로 시설마다 주인 한 곳 · 관 연장은 주인 측점의 `culvert`(흙막이 `body` 아님)만.
- 파이썬 ↔ TS 짝(`common_util_culvert_sets.ts`) 같은 값.
"""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
for path in (ROOT, Path(__file__).resolve().parent):
if str(path) not in sys.path:
sys.path.insert(0, str(path))
import test_b06_culvert_sets_mirror as mirror # noqa: E402
from B06_Section.B06_Section_Engine_Culvert import attach_culvert_sets, pipe_points_file # noqa: E402
# 검증 프로젝트 꼴 — 20 · 90 물넘이(폭 10) · 95 배관(도수로) · 100 배관 + 100.3 박스(유출 흙막이) · 60 세월교(흙막이)
POINTS = [
{"chainage_m": 20.0, "facility": "ford_pavement", "options": {"ford_width_m": 10.0}},
{"chainage_m": 90.0, "facility": "ford_pavement", "options": {"ford_width_m": 10.0}},
{
"chainage_m": 95.0,
"facility": "pipe",
"options": {"pipe_diameter_mm": 800, "chute_add_1_structure": "도수로"},
},
{
"chainage_m": 60.0,
"facility": "ford_bridge",
"options": {
"ford_width_m": 6.0,
"inlet_type": "흙막이",
"inlet_revet_form": "콘크리트",
"inlet_revet_height_m": 1.5,
},
},
{"chainage_m": 100.0, "facility": "pipe", "options": {"pipe_diameter_mm": 600}},
{
"chainage_m": 100.3,
"facility": "box_culvert",
"options": {"outlet_revet_form": "돌쌓기(찰)", "outlet_revet_height_m": 3.5},
},
]
CHAINAGES = [0.0, 20.0, 40.0, 60.0, 80.0, 85.0, 90.0, 95.0, 100.0, 105.0]
# 같은 키 — 옆 측점 연동(89.8 · 폭 10 · 주인 89.7)이 90 m 에 더 가깝지만 소유(90.4 · 주인 90)가 먼저
# (주인 측점 = 0.51 m 안 · 458)
SAME_KEY_POINTS = [
{"chainage_m": 89.8, "facility": "ford_pavement", "options": {"ford_width_m": 10.0}},
{"chainage_m": 90.4, "facility": "ford_pavement", "options": {"ford_width_m": 0.1}},
]
SAME_KEY_CHAINAGES = [85.0, 89.7, 90.0, 95.0]
def _attach(tmp_path: Path, points: list[dict], chainages: list[float]) -> list[dict]:
target = pipe_points_file(tmp_path)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_text(json.dumps({"points": points}, ensure_ascii=False), encoding="utf-8")
sections = [{"chainage_m": value} for value in chainages]
attach_culvert_sets(tmp_path, sections)
return sections
def _at(sections: list[dict], chainage: float) -> dict:
return next(s for s in sections if s["chainage_m"] == chainage)
@pytest.fixture()
def sections(tmp_path: Path) -> list[dict]:
return _attach(tmp_path, POINTS, CHAINAGES)
def test_연동_물넘이_범위_안_자기_관_함께(sections):
at95 = _at(sections, 95.0)
assert at95["culvert"]["chainage_m"] == 95.0
assert at95["culvert"]["type"] == "pipe"
assert at95["culvert"]["chute"]["structure"] == "도수로"
assert at95["ford_pavement"]["chainage_m"] == 90.0
assert set(_at(sections, 85.0)) == {"chainage_m", "ford_pavement"}
def test_흙막이는_자기_관에_밀림_없으면_그대로(sections):
at100 = _at(sections, 100.0)
assert at100["box"]["chainage_m"] == 100.3
assert at100["culvert"]["chainage_m"] == 100.0
assert "body" not in at100["culvert"] # 관 — 흙막이 세트 아님
at60 = _at(sections, 60.0)
assert at60["ford"]["chainage_m"] == 60.0
assert at60["culvert"]["chainage_m"] == 60.0 # 세월교 흙막이
def test_같은_키는_소유_측점_먼저(tmp_path):
sections = _attach(tmp_path, SAME_KEY_POINTS, SAME_KEY_CHAINAGES)
assert _at(sections, 90.0)["ford_pavement"]["chainage_m"] == 90.4
assert _at(sections, 85.0)["ford_pavement"]["chainage_m"] == 89.8
def test_수량은_주인_측점_한_번(sections):
"""소비처 규칙 — 시설 `chainage_m` 의 가장 가까운 측점만 셈."""
owner = {
p["chainage_m"]: min(CHAINAGES, key=lambda c: abs(c - p["chainage_m"])) for p in POINTS
}
counted: dict[tuple[str, float], int] = {}
for section in sections:
for key in ("culvert", "ford", "box", "ford_pavement"):
value = section.get(key)
if value and owner[value["chainage_m"]] == section["chainage_m"]:
name = "guard" if key == "culvert" and "body" in value else key
counted[(name, value["chainage_m"])] = (
counted.get((name, value["chainage_m"]), 0) + 1
)
assert counted == {
("ford_pavement", 20.0): 1,
("ford_pavement", 90.0): 1,
("culvert", 95.0): 1, # 관 연장 · 도수로 — 95 m 한 번
("ford", 60.0): 1,
("guard", 60.0): 1,
("culvert", 100.0): 1,
("box", 100.3): 1,
}
# 박스 흙막이는 자기 관에 밀려 어디에도 안 실림 — 관 연장 · 수량을 두 번 세지 않음
assert not any(s.get("culvert", {}).get("chainage_m") == 100.3 for s in sections)
@pytest.mark.skipif(not mirror.TSC.is_file(), reason="프론트엔드 의존성 미설치")
@pytest.mark.parametrize(
("points", "chainages"), [(POINTS, CHAINAGES), (SAME_KEY_POINTS, SAME_KEY_CHAINAGES)]
)
def test_파이썬_TS_짝_같은_값(tmp_path, monkeypatch, points, chainages):
expected = _attach(tmp_path / "py", points, chainages)
monkeypatch.setattr(mirror, "POINTS", points)
monkeypatch.setattr(mirror, "CHAINAGES", chainages)
assert mirror._ts_sections(tmp_path) == expected