Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01F3mdb91125mPoC6tpsQx4P
159 lines
6.6 KiB
Python
159 lines
6.6 KiB
Python
"""B06 배수관 세트(배관·기슭막이·보호공) 엔진 검증.
|
|
|
|
정본(`pipe_points.json`) + 레지스트리 기본값 조합이 계획서(3-0 근거 표)대로
|
|
나오는지, 배관 외 시설(BOX암거 등)이 이번 범위에서 빠지는지 확인한다.
|
|
"""
|
|
|
|
import json
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from B06_Section.B06_Section_Engine_Culvert import ( # noqa: E402
|
|
CHUTE_THICKNESS_CM,
|
|
MIN_PIPE_COVER_M,
|
|
REVET_FACE_SLOPE,
|
|
_culvert_set,
|
|
attach_culvert_sets,
|
|
pipe_points_file,
|
|
)
|
|
|
|
|
|
def test_cross_reference_constants_match_plan():
|
|
"""계획서 3-0 표 값 — 토피 0.5m·전면 1:0.3 · 도수로/돌붙임 빈 두께 30 cm(90-2 사용자 확정)."""
|
|
assert MIN_PIPE_COVER_M == 0.5
|
|
assert CHUTE_THICKNESS_CM == 30.0
|
|
assert REVET_FACE_SLOPE == 0.3
|
|
# 배관은 수평도 가능 — 세트에 경사 강제값을 싣지 않는다(2026-08-20 사용자 확정).
|
|
assert "min_slope" not in _culvert_set(None)
|
|
|
|
|
|
def test_empty_options_have_no_members():
|
|
"""빈 고르기 칸 = 그 부재 없음(54-39) — 구조 · 형태 · 관종 · 기초 빈 값."""
|
|
spec = _culvert_set(None)
|
|
assert spec["type"] == "pipe"
|
|
assert spec["min_cover_m"] == MIN_PIPE_COVER_M
|
|
assert spec["pipe_kind"] is None
|
|
assert spec["foundation"] is None
|
|
assert spec["inlet"] == {"role": "inlet", "structure": ""}
|
|
assert spec["outlet"] == {"role": "outlet", "structure": ""}
|
|
|
|
|
|
def test_saved_values_drive_members():
|
|
"""고른 값만 부재 — 빈 높이 = 관경 기준(Ø1000 → 1.5 · 139-1) · 빈 형태 = 없음."""
|
|
spec = _culvert_set({"inlet_type": "집수정", "inlet_structure": "L", "outlet_type": "기슭막이"})
|
|
assert spec["inlet"]["basin_form"] == "L"
|
|
assert spec["inlet"]["basin_height_m"] is None
|
|
part = spec["outlet"]
|
|
assert part["structure"] == "기슭막이"
|
|
assert part["revet_form"] is None
|
|
assert part["revet_height_m"] == 1.5
|
|
assert part["face_slope"] == REVET_FACE_SLOPE
|
|
# 옛 보호공(apron) 칸은 걷음 — 유출 보호는 도수로/돌붙임 띠(90-2)
|
|
assert "apron_length_m" not in part
|
|
|
|
|
|
def test_basin_inlet_skips_revet_and_apron():
|
|
"""유입구 = 집수정이면 그쪽 기슭막이·보호공을 만들지 않는다(라벨만)."""
|
|
spec = _culvert_set({"inlet_type": "집수정", "outlet_type": "기슭막이"})
|
|
assert spec["inlet"]["structure"] == "집수정"
|
|
assert "revet_height_m" not in spec["inlet"]
|
|
# 유출측은 그대로 세트를 갖춘다.
|
|
assert spec["outlet"]["structure"] == "기슭막이"
|
|
assert spec["outlet"]["revet_height_m"] == 1.5 # 빈 높이 = 관경 기준(139-1)
|
|
|
|
|
|
def test_string_diameter_and_custom_height():
|
|
"""관경 문자열 저장분("800")과 사용자 높이 변경이 그대로 반영된다."""
|
|
spec = _culvert_set(
|
|
{"pipe_diameter_mm": "800", "outlet_type": "기슭막이", "outlet_revet_height_m": 1.5}
|
|
)
|
|
assert spec["diameter_m"] == 0.8
|
|
assert spec["outlet"]["revet_height_m"] == 1.5
|
|
|
|
|
|
def test_old_default_moves_for_large_pipe():
|
|
"""옛 기본 1.5 · 관경 > 1.0 = 관경 + 0.5 로 읽음(139-1) · 관경 ≤ 1.0 · 다른 값은 그대로."""
|
|
big = _culvert_set(
|
|
{
|
|
"pipe_diameter_mm": 1200,
|
|
"inlet_type": "집수정",
|
|
"inlet_basin_height_m": 1.5,
|
|
"outlet_type": "기슭막이",
|
|
"outlet_revet_height_m": 1.5,
|
|
}
|
|
)
|
|
assert big["outlet"]["revet_height_m"] == 1.7
|
|
assert big["inlet"]["basin_height_m"] == 1.7
|
|
small = _culvert_set(
|
|
{"pipe_diameter_mm": 800, "outlet_type": "기슭막이", "outlet_revet_height_m": 1.5}
|
|
)
|
|
assert small["outlet"]["revet_height_m"] == 1.5
|
|
hand = _culvert_set(
|
|
{"pipe_diameter_mm": 1500, "outlet_type": "기슭막이", "outlet_revet_height_m": 1.6}
|
|
)
|
|
assert hand["outlet"]["revet_height_m"] == 1.6
|
|
|
|
|
|
def test_chute_thickness_cm_to_m():
|
|
"""도수로/돌붙임(90-2) — 구조를 고르면 `chute` · 두께 cm → m · 빈 두께 30 cm · 안 고르면 없음."""
|
|
assert "chute" not in _culvert_set({"chute_add_1_thickness": 40})
|
|
spec = _culvert_set({"chute_add_1_structure": "돌붙임", "chute_add_1_thickness": "45"})
|
|
assert spec["chute"] == {"structure": "돌붙임", "thickness_m": 0.45}
|
|
blank = _culvert_set({"chute_add_1_structure": "도수로", "chute_add_1_thickness": ""})
|
|
assert blank["chute"] == {"structure": "도수로", "thickness_m": 0.3}
|
|
|
|
|
|
def test_attach_only_pipe_facilities(tmp_path):
|
|
"""배관은 `culvert`, 세월교는 `ford`, BOX암거는 `box` 키로 얹는다.
|
|
|
|
배관 매칭은 chainage ±0.02m 그대로다. 세월교·BOX암거는 구체 폭만큼 이어져
|
|
기준 측점 전후 절반까지 붙는다(2026-08-25 사용자 확정) — 아래 100.0m은
|
|
어느 쪽 범위에도 들지 않는다.
|
|
"""
|
|
target = pipe_points_file(tmp_path)
|
|
target.parent.mkdir(parents=True)
|
|
target.write_text(
|
|
json.dumps(
|
|
{
|
|
"route_signature": "x",
|
|
"points": [
|
|
{
|
|
"chainage_m": 84.3,
|
|
"source": "spacing",
|
|
"options": {"pipe_diameter_mm": 1000},
|
|
},
|
|
{"chainage_m": 149.73, "source": "stream", "facility": "ford_bridge"},
|
|
{"chainage_m": 200.92, "source": "spacing", "facility": "box_culvert"},
|
|
],
|
|
},
|
|
ensure_ascii=False,
|
|
),
|
|
encoding="utf-8",
|
|
)
|
|
cross_sections = [
|
|
{"chainage_m": 84.3, "samples": []},
|
|
{"chainage_m": 149.73, "samples": []},
|
|
{"chainage_m": 200.92, "samples": []},
|
|
{"chainage_m": 100.0, "samples": []},
|
|
]
|
|
attached = attach_culvert_sets(tmp_path, cross_sections)
|
|
assert attached == 3
|
|
assert "culvert" in cross_sections[0]
|
|
assert cross_sections[0]["culvert"]["diameter_m"] == 1.0
|
|
# 세월교·BOX암거는 그림이 달라 키를 나눈다 — 배수관 소비처가 집어가면 안 된다.
|
|
assert cross_sections[1]["ford"]["type"] == "ford"
|
|
assert cross_sections[2]["box"]["type"] == "box"
|
|
assert all("culvert" not in section for section in cross_sections[1:])
|
|
assert all("ford" not in section for section in (cross_sections[0], *cross_sections[2:]))
|
|
|
|
|
|
def test_attach_without_file_is_noop(tmp_path):
|
|
"""정본 파일이 없으면 아무 것도 얹지 않고 0을 돌려준다."""
|
|
sections = [{"chainage_m": 10.0}]
|
|
assert attach_culvert_sets(tmp_path, sections) == 0
|
|
assert "culvert" not in sections[0]
|