"""107 세월교 — 유입 · 유출 따로 · 원지반 제약 삭제 · 유입 날개벽 수평 여유. 화면 TS 를 그 자리에서 돌린 값(`helper_107_ford_free.cjs`) · 픽스처 = 73-4 세월교 173.09 · 박스 352.14. """ 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_107_ford_free.cjs" NODE_MODULES = ROOT / "config" / "node_modules" pytestmark = pytest.mark.skipif(not NODE_MODULES.is_dir(), reason="프론트엔드 의존성 미설치") @pytest.fixture(scope="module") def out() -> 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 _read(rel: str) -> str: return (ROOT / rel).read_text(encoding="utf-8") def test_세월교_박스를_고르면_같은_측점_벽_고름을_풂(out: dict) -> None: assert out["released"] == [173.09, 352.14] def test_벽을_고르면_세월교_박스_고름을_풂() -> None: station = _read("B06_Section/B06_Section_UI_Page_Station_Controls.ts") assert ( "if (key && ford.control.selectedFor(chainageM)) ford.control.select(chainageM, null);" in station ) assert ( "if (key && box.control.selectedFor(chainageM)) box.control.select(chainageM, null);" in station ) view = _read("B06_Section/B06_Section_UI_Cross_View.ts") # 카드 안 — 벽을 고르면 본체 창을 닫고 · 본체를 고르면 벽 창을 닫음 assert "bodies.clearSelection(); // 세월교 · 박스 고름을 풂" in view assert "if (activeRevet !== null) toggleRevet(activeRevet);" in view @pytest.mark.parametrize("variant", ["guards", "inletWing"]) def test_세월교_원지반을_바꿔도_바닥_그대로(out: dict, variant: str) -> None: """107-2 — 바닥 = 노면 − (관경 + 최소 토피) · 하상 −2 m · 전체 +0.5 / −1 m 사본도 같은 모양.""" runs = out["ford"][variant] for name, shape in runs.items(): assert shape == runs["base"], name base = runs["base"] # 바닥판 수평(139-3) — 표고 하나 · 낮은 노견 쪽 깊이 = 관경 + 토피 · 높은 쪽은 그보다 깊음 sides = base["sides"] assert len({side["slabTop"] for side in sides}) == 1 assert min(side["depth"] for side in sides) == pytest.approx(base["pipeCover"]) assert all(side["depth"] >= base["pipeCover"] - 1e-6 for side in sides) @pytest.mark.parametrize("variant", ["guards", "bare"]) def test_박스_원지반을_바꿔도_본체_끝_날개_흙막이_그대로(out: dict, variant: str) -> None: runs = out["box"][variant] for name, shape in runs.items(): assert shape == runs["base"], name if variant == "bare": assert all(side["apron"] for side in runs["base"]["sides"]) else: assert len(runs["base"]["walls"]) == 2 def test_유입_날개벽_끝이_묻히면_수평_여유_뒤_절토(out: dict) -> None: """107-3 — 흙막이 수평공간과 같은 칸(빈 값 = 1 m) · 유출 날개벽은 수평 구간 없음.""" cuts = out["wingCut"] for case, berm in (("auto", 1.0), ("two", 2.0)): (x0, z0), (x1, z1) = cuts[case]["inlet"][:2] assert z1 == pytest.approx(z0) and abs(x1 - x0) == pytest.approx(berm), case (x0, z0), (x1, z1) = cuts["zero"]["inlet"][:2] assert z1 > z0 + 0.1 # 0 = 바로 절토 assert cuts["auto"]["outlet"][1][1] > cuts["auto"]["outlet"][0][1] + 0.1 def test_날개벽_수평_여유_칸은_흙막이_수평공간과_같은_키() -> None: ford = _read("A00_Common/design/design_structure_ford.ts") assert '...bodyGuardItems(id, "본체 끝 높이(자동)", true),' in ford guard = _read("A00_Common/design/design_structure_guard.ts") assert "map: wingBerm ? { 흙막이: branch, 날개벽: branch } : { 흙막이: branch }," in guard