"""73-4 세월교 · 박스 흙막이 — 관보호공 흙막이와 같은 벽 · 같은 자리 규칙(관 · 본체 끝). 화면 TS 를 그 자리에서 돌린 값(`helper_73_4_body_guard.cjs`)과 서버 Node 번들(면적)을 대조함. 픽스처 = 검증_sub7 상세 일부(세월교 173.09 유입 · 유출 흙막이 · 박스 352.14 유입 · 유출 흙막이 전후 15 · 10 m). """ 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_73_4_body_guard.cjs" FIXTURE = Path(__file__).parent / "fixtures" / "b06_73_4_body_guard_sections.json" NODE_MODULES = ROOT / "config" / "node_modules" pytestmark = pytest.mark.skipif(not NODE_MODULES.is_dir(), reason="프론트엔드 의존성 미설치") @pytest.fixture(scope="module") def screen() -> 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_박스_벽_배면이_본체_끝_윗면이_본체_윗면(screen: dict) -> None: walls = screen["box"]["walls"] assert {w["role"] for w in walls} == {"inlet", "outlet"} for wall in walls: assert wall["dOffset"] == pytest.approx(0, abs=1e-3) assert wall["dElevation"] == pytest.approx(0, abs=1e-3) # 형태 = 폼 값 그대로(유입 찰 · 유출 콘크리트) assert {w["role"]: w["form"] for w in walls} == {"inlet": "돌쌓기(찰)", "outlet": "콘크리트"} # 자동 자리 = 본체 기준 0 — 되받아 저장해도 본체를 따라감 assert screen["box"]["applied"] == {"inlet": 0, "outlet": 0} def test_박스를_늘리면_벽도_m_단위로_따라감(screen: dict) -> None: before = {w["role"]: w["front"] for w in screen["box"]["walls"]} for wall in screen["boxMoved"]: assert wall["dOffset"] == pytest.approx(0, abs=1e-3) assert abs(wall["front"] - before[wall["role"]]) == pytest.approx(1.0, abs=1e-3) def test_세월교_벽_배면이_관_끝(screen: dict) -> None: walls = {w["role"]: w for w in screen["ford"]["walls"]} # 유출은 관 1 m 맞춤만큼 바깥으로 물러남(80 · test_80_ford_pipe) — 1 m 안 assert abs(walls["outlet"]["dOffset"]) < 1 assert walls["outlet"]["dElevation"] == pytest.approx(0, abs=1e-3) assert walls["inlet"]["dOffset"] == pytest.approx(0, abs=1e-3) # 윗면 = 측벽 상단(상판 높이) — 노면 위로는 안 솟음(79-7) assert walls["inlet"]["dElevation"] <= 1e-3 def test_연동_옆_측점에도_벽이_섬(screen: dict) -> None: assert [row["walls"] for row in screen["linked"]] == [2, 2] def test_설계선_트림은_흙막이_쪽(screen: dict) -> None: assert screen["box"]["trimSame"] and screen["ford"]["trimSame"] def test_서버_면적이_화면과_같음(screen: dict) -> None: from B06_Section.B06_Section_Server_Calc_Prebuild import _NPM_SCRIPT, BUNDLE from common_util.common_util_node_bundle import run_bundle_json sections = json.loads(FIXTURE.read_text(encoding="utf-8"))["cross_sections"] output = run_bundle_json(BUNDLE, _NPM_SCRIPT, {"detail": {"cross_sections": sections}}) assert output, "서버 Node 번들 실행 실패" server = {round(r["chainage_m"], 2): r for r in output["areas"]} for row in screen["areas"]: mine = server[round(row["chainage_m"], 2)] for key in ("cut_area_m2", "fill_area_m2"): assert mine[key] == pytest.approx(row[key], abs=1e-4), (row["chainage_m"], key) # 세월교 · 박스 흙막이 세트는 관이 아님 — 관 연장 안 실음 assert all("pipe_length_m" not in r for r in screen["areas"])