"""79 흙막이 · 세월교 · 박스암거 형상 — 화면 TS 를 그 자리에서 돌린 값(`helper_79_guard_shape.cjs`). 픽스처 = 73-4 와 같은 검증_sub7 상세 일부(세월교 173.09 · 박스 352.14 유입 · 유출 흙막이). - 79-5 관 보호공 · 독립 기슭막이(옹벽) 기본 자리 = 성토사면 제일 위(d 0 · 관은 m 단위 맞춤만큼 아래) - 79-6 구간 연동 측점 벽 = 연동 표시 - 79-7 흙막이 윗면 = 본체 윗면 · 높이 빈 칸 = 본체 끝 높이 · 세월교 ㄴ형 측벽 걷음 · 박스 수평 · 박스 날개벽 끝 절 · 성토선 · 끝 절토 = 수평공간(컨테이너 칸) 뒤 절토 경사 """ import json import subprocess import sys from pathlib import Path import pytest ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) HELPER = Path(__file__).parent / "helper_79_guard_shape.cjs" NODE_MODULES = ROOT / "config" / "node_modules" pytestmark = pytest.mark.skipif(not NODE_MODULES.is_dir(), reason="프론트엔드 의존성 미설치") @pytest.fixture(scope="module") def shape() -> dict: done = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HELPER)], cwd=str(ROOT), capture_output=True, text=True, encoding="utf-8" ) assert done.returncode == 0, done.stderr return json.loads(done.stdout) def test_세월교_높이_빈칸은_본체_끝_높이_윗면은_본체_윗면(shape: dict) -> None: walls = shape["fordAuto"] assert {w["role"] for w in walls} == {"inlet", "outlet"} for wall in walls: assert wall["topGap"] == pytest.approx(0, abs=1e-3) assert wall["bottomGap"] == pytest.approx(0, abs=1e-3) assert wall["pureHeight"] == pytest.approx(wall["endHeight"], abs=1e-3) def test_박스_높이_빈칸은_박스_높이_값_윗면은_본체_윗면(shape: dict) -> None: # 2 m 박스 → 2 m(사용자 원문 숫자 · 79b) — 바닥 = 윗면 − 그 높이 walls = shape["boxAuto"] assert {w["role"] for w in walls} == {"inlet", "outlet"} for wall in walls: assert wall["topGap"] == pytest.approx(0, abs=1e-3) assert wall["pureHeight"] == pytest.approx(wall["autoHeight"], abs=1e-3) assert wall["autoHeight"] < wall["endHeight"] assert wall["bottomGap"] == pytest.approx(wall["endHeight"] - wall["autoHeight"], abs=1e-3) def test_높이를_적으면_윗면은_그대로_바닥이_따라감(shape: dict) -> None: for wall in shape["boxSet"]: assert wall["topGap"] == pytest.approx(0, abs=1e-3) assert wall["bottomGap"] == pytest.approx(wall["endHeight"] - wall["pureHeight"], abs=1e-3) def test_세월교_흙막이_쪽_ㄴ형_측벽은_사라짐(shape: dict) -> None: assert all(side["parts"] == 0 for side in shape["fordParts"]) one = {side["role"]: side["parts"] for side in shape["fordOneGuardParts"]} assert one == {"inlet": 0, "outlet": 2} def test_박스_본체는_늘_수평(shape: dict) -> None: level = shape["boxLevel"] assert level["left"] == pytest.approx(level["right"], abs=1e-6) assert level["slope"] is None def test_박스_날개벽_끝_절성토선(shape: dict) -> None: for side in shape["boxWing"]: assert side["cut"] + side["fill"] >= 1 def test_흙막이_끝_절토는_수평공간_뒤(shape: dict) -> None: tails = [tail for group in shape["cutDefault"] for tail in group] assert tails, "절토 끝선이 없음" for tail in tails: assert tail["points"] == 3 assert tail["flat"] == pytest.approx(0, abs=1e-6) assert tail["berm"] == pytest.approx(1.0, abs=1e-3) for tail in shape["cutBerm2"]: assert tail["berm"] == pytest.approx(2.0, abs=1e-3) def test_관_보호공_기본_자리는_제일_위(shape: dict) -> None: pipe = shape["pipe"] assert pipe is not None assert pipe["d"]["inlet"] == pytest.approx(0, abs=1e-6) # 유출은 관 m 단위 맞춤이 사면 아래로 민 만큼만 내려감(종전 기본 = 경사길이 4 m · d 3.07) assert pipe["d"]["outlet"] < 1.0 inlet = next(w for w in pipe["walls"] if w["role"] == "inlet") assert inlet["topBelowEdge"] == pytest.approx(0, abs=0.05) assert float(pipe["lengthM"]).is_integer() def test_저장_x_있는_관_보호공은_종전_자리_그대로(shape: dict) -> None: # d 빈 칸 기본 = 경사길이 4 m 지점(3.07) — x 원점 그대로라 절대 자리 안 바뀜(79b) stored = shape["pipeStoredX"] assert stored["x"] == pytest.approx(1.0) assert stored["d"] == pytest.approx(4.0 * 1.2 / (1 + 1.2**2) ** 0.5, abs=0.05) def test_저장_x_있는_독립_기슭막이_옹벽은_종전_자리_그대로(shape: dict) -> None: stored = shape["revetStoredX"] assert stored["x"] == pytest.approx(1.0) assert stored["d"] == pytest.approx(2.6) # 종전 기본 하강(3.07 / 1.2 → 0.1 눈금) run = 4.0 * 1.2 / (1 + 1.2**2) ** 0.5 assert stored["shift"] == pytest.approx(run + 1.0, abs=1e-3) assert stored["shiftXD"] == pytest.approx(run + 1.0, abs=1e-3) def test_독립_기슭막이_옹벽_기본_자리는_제일_위(shape: dict) -> None: revet = shape["revet"] assert revet["d"] == 0 assert revet["topBelowEdge"] == pytest.approx(0, abs=1e-3) assert revet["linked"] is False assert shape["revetLinked"] is True