Files
Aislo/resources/tester/test_65_ford_defaults.py
T
eomsangdonandClaude Opus 5.5 4764977f9b feat(B04): 65-4 자동 배치 새 세월교 빈 월류 높이 = 담당 유역 필요 수심 · 시험 65
- _apply_new_defaults 가 유역 설계유량을 받아 새 세월교 빈 월류 높이를 채움(있는 값 · 유량 없음은 그대로)
- ford_height_m(서버) ↔ fordHeightM(화면) 같은 풀이 · 거울 시험
- 시험 test_65_ford_defaults: 유입 · 유출 칸 목록 · 없음 · 저장 키 · 칸 차례 · 정의 기본값 · 물넘이 키 · 집계표 · B06 읽기 줄

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BVPp2o6Mh2FZJr72yW3ofZ
2026-10-06 23:11:52 +09:00

154 lines
6.4 KiB
Python

# -*- coding: utf-8 -*-
"""65장 세월교 · 박스암거 · 물넘이 포장 — 명세 · 기본값 · 자동 배치 월류 높이.
못박는 것
- 세월교 · 박스 유입 · 유출 = 구조 + 흙막이 칸(형태 · 길이 · 높이 · 전 · 후)만 · 높이 · 측벽 · 바닥판 칸 없음
- 흙막이 칸 기본값(찰 · 10 · 1.5 · 5 · 5) · 추가 흙막이 · 도수로/돌붙임 상자 · 키 `{side}_revet_*`
- 「없음」 = 없는 상자(목록 램프 · 잇기 줄 뺌)
- 세월교 칸 차례 관경 → 수량(련) → 월류 폭 → 높이 · 정의 기본값 관경 1000 · 폭 10 · 련 2
- 물넘이 포장 경사각 기본 3° · 월류 폭 · 높이 = 세월교와 같은 키
- 자동 배치 새 세월교 빈 월류 높이 = 필요 수심(서버 `ford_height_m` ↔ TS `fordHeightM` 거울)
- B06 측벽 읽기 줄은 세월교 · 박스 유입 · 유출 칸에 안 옮김
화면(ORCA)은 따로 봄.
"""
from __future__ import annotations
import json
import os
import subprocess
from pathlib import Path
import pytest
from B04_PreProcess import B04_PreProcess_Router_Basins as basins
from B05_Profile.B05_Profile_Structures_Schema import structure_type_map
from common_util.common_util_drainage_pipes import PipePoint
from M02_MasterTemplete.M02_Table_Fill_Spec import container_rows
HERE = os.path.dirname(os.path.abspath(__file__))
ROOT = Path(HERE).resolve().parents[1]
GUARD = [
["structure", None, None],
["form", ["흙막이"], "돌쌓기(찰)"],
["length", ["흙막이"], "10"],
["height", ["흙막이"], "1.5"],
["before", ["흙막이"], "5"],
["after", ["흙막이"], "5"],
]
@pytest.fixture(scope="module")
def out() -> dict:
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", os.path.join(HERE, "helper_65_ford_box.cjs")],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_유입_유출은_구조와_흙막이_칸만(out):
for spec in ("ford", "box"):
for side in ("inlet", "outlet"):
assert out[spec][side] == GUARD, (spec, side)
assert [i[0] for i in out[spec]["soil_add"]] == [
"form",
"length",
"height",
"before",
"after",
]
assert out[spec]["chute_add"] == [["structure", None, None]]
keys = out[f"{spec}Keys"]
assert keys["prefixes"] == ["soil_add_", "chute_add_"]
# 측벽 높이 · 좌우 · 상하 · 바닥판 · 날개벽 치수 칸 없음
assert not [
k for k in keys["keys"] if "wall" in k or "slab" in k or k.startswith("wing_in_")
]
def test_없음이면_없는_상자(out):
for spec in ("fordNone", "boxNone"):
assert out[spec]["inlet"] == [True, False] and out[spec]["outlet"] == [True, False]
assert out[spec]["soil_add"] == [False, False]
def test_흙막이_저장_키와_다시_열기(out):
o = out["fordOut"]
assert o["inlet_type"] == "흙막이" and o["wing_in"] == "없음"
assert (o["inlet_revet_form"], o["inlet_revet_length_m"], o["inlet_revet_height_m"]) == (
"돌망태",
8,
1.2,
)
assert o["outlet_type"] == "없음" and not any(k.startswith("outlet_revet_") for k in o)
assert o["soil_add_1_form"] == "바자" and o["chute_add_1_structure"] == "돌붙임"
back = out["fordBack"]
assert back["inlet"][0]["length"] == "8" and back["soil_add"][0]["form"] == "바자"
assert out["boxOut"]["wing_out"] == "흙막이" and out["boxOut"]["outlet_revet_length_m"] == 4
def test_세월교_칸_차례와_정의_기본값(out):
assert [i[0] for i in out["ford"]["ford"]] == ["diameter", "count", "width", "height"]
ford = {o.key: o.default for o in structure_type_map()["ford_bridge"].options}
assert (str(ford["pipe_diameter_mm"]), ford["ford_width_m"], ford["pipe_count"]) == (
"1000",
10,
2,
)
def test_물넘이_경사각_기본_3_월류_폭_높이_같은_키(out):
assert [i[0] for i in out["pave"]["slope"]] == ["angle", "width", "height"]
assert out["paveOut"] == {"ford_slope_deg": 3, "ford_width_m": 5, "ford_height_m": 0.12}
assert "ford_width_m" not in out["paveRetired"] and "ford_height_m" not in out["paveRetired"]
pave = {o.key: o.default for o in structure_type_map()["ford_pavement"].options}
assert pave["ford_slope_deg"] == 3
def test_서버_월류_높이_거울(out):
for q, b, h in out["table"]:
assert basins.ford_height_m(q, b) == h, (q, b)
assert basins.ford_height_m(None, 10) is None and basins.ford_height_m(2.54, 0) is None
def test_자동_배치_새_세월교는_기본값과_월류_높이():
fresh = PipePoint(chainage_m=120.0, source="stream", facility="ford_bridge")
basins._apply_new_defaults([fresh], {120.0: 8.0})
o = fresh.options
assert (str(o["pipe_diameter_mm"]), o["ford_width_m"], o["pipe_count"]) == ("1000", 10, 2)
assert o["ford_height_m"] == basins.ford_height_m(8.0, 10) == 0.16
kept = PipePoint(
chainage_m=120.0, source="stream", facility="ford_bridge", options={"ford_height_m": 0.5}
)
basins._apply_new_defaults([kept], {120.0: 8.0})
assert kept.options["ford_height_m"] == 0.5 # 있는 값은 그대로
blind = PipePoint(chainage_m=140.0, source="stream", facility="ford_bridge")
basins._apply_new_defaults([blind], {})
assert "ford_height_m" not in blind.options # 유량 없으면 빈칸
def test_집계표_세월교_흙막이_상자_건():
item = {
"type_id": "ford_bridge",
"options": {"soil_add_1_form": "바자", "soil_add_2_form": "돌망태"},
"복사본": "pipe@50.0",
"흙막이단": 0, # 배관 단 수는 세월교에 안 걸림
}
rows = container_rows(item, {"gsoiladd2": "SS000001"})
assert [(r["그룹"], r["summary_item"]) for r in rows] == [
("흙막이 1", None),
("흙막이 2", "SS000001"),
]
def test_B06_측벽_읽기_줄은_세월교_박스_칸에_안_옮김():
dock = (ROOT / "B06_Section/B06_Section_UI_Adjust_Dock.ts").read_text(encoding="utf-8")
assert "slot === sideSlots?.wingInlet || slot === sideSlots?.wingOutlet) return null" in dock
lists = (ROOT / "B05_Profile/B05_Profile_UI_Structures_List.ts").read_text(encoding="utf-8")
assert "return isNone(c, state[c.id][0])" in lists