"""91 원지반 · 암 제약 삭제 · 유출측 복합경사. 화면 TS 를 그 자리에서 돌린 값(`helper_91_ground_free.cjs`) · 픽스처 = 73-4 세월교 173.09 · 박스 352.14 · 관 180. """ 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_91_ground_free.cjs" NODE_MODULES = ROOT / "config" / "node_modules" pytestmark = pytest.mark.skipif(not NODE_MODULES.is_dir(), reason="프론트엔드 의존성 미설치") @pytest.fixture(scope="module") def fit() -> 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_세월교_바닥_기본도_원지반을_바꿔도_그대로(fit: dict) -> None: """107-2 — 기본 바닥 = 노면 − (관경 + 토피) · 하상을 안 따라감(옛 91-2 「자동은 하상」 뒤집음).""" assert fit["fordAuto"] == fit["fordAutoBedDown"] def test_세월교_바닥_손_값은_원지반을_바꿔도_그대로(fit: dict) -> None: assert fit["fordHand"] == fit["fordHandBedDown"] # 손 값 = 측벽 높이 · 흙막이 바닥 = 바닥판 밑(두께 0.3) for side in fit["fordHand"]: assert side["wallBottom"] == pytest.approx(side["slabTop"] - 0.3, abs=1e-3) def test_세월교_박스_칸은_측벽_높이_줄_없음() -> None: """107-3 — 「측벽 높이(자동)」 줄 지움(유입 · 유출).""" dock = (ROOT / "B06_Section/B06_Section_UI_Adjust_Dock.ts").read_text(encoding="utf-8") assert "sideSlots?.wingOutlet) return null" in dock assert "wall-height" not in dock def test_다단_자동_자리는_원지반과_무관_사면_1m(fit: dict) -> None: base, up, down = fit["tiers"] assert base["walls"] == up["walls"] == down["walls"] assert base["slopes"] == [1.0, 1.0] def test_전면_기울기는_단면유형과_무관_칸만(fit: dict) -> None: # 단면유형 넷 모두 같은 값 · 비면 성토 표값(1.5 m 찰 = 0.25) · 절토 칸 = 0.2 assert all(pair == [0.25, 0.2] for pair in fit["lean"]) def test_B08_성토절토_판정도_칸_아니면_성토() -> None: from common_util.common_util_structure_face_role import structure_face_role_of for mode in ("left_cut", "right_cut", "both_cut", "both_fill"): for side in ("좌", "우", "자동(성토 쪽)"): assert structure_face_role_of(mode, {"side": side})[0] == "성토" assert structure_face_role_of("both_fill", {"face_role": "절토"})[0] == "절토" def test_설계선_끝은_화면_픽셀이_아니라_수치로() -> None: line = (ROOT / "B06_Section/B06_Section_UI_Cross_Design_Line.ts").read_text(encoding="utf-8") assert "OVERLAP_PX" not in line assert line.count("Math.abs(point.elevation_m - g) <= OVERLAP_M") == 2 def test_지형에_막힘_잔재_없음() -> None: for name in ( "B06_Section_UI_Cross_Culvert.ts", "B06_Section_UI_Cross_Revetment.ts", "B06_Section_UI_Cross_Culvert_Wire.ts", "B06_Section_UI_Cross_Culvert_Types.ts", "B06_Section_UI_Cross_Culvert_Extra.ts", ): source = (ROOT / "B06_Section" / name).read_text(encoding="utf-8") assert "shiftBlockedM" not in source and "지형에 막힘" not in source, name assert "noticeExtraLimit" not in source and "noticeShortfall" not in source, name def _knee(cut: dict) -> bool: """암(1:0.4) 뒤 토사(1:1.0) 무릎이 있음 — 픽스처 = 리핑암 · 2단계 · 경계 −0.5 m.""" ratios = [leg["ratio"] for leg in cut["legs"] if leg["ratio"] is not None] return any(a == 0.4 and b == 1.0 for a, b in zip(ratios, ratios[1:])) def test_흙막이_수평공간_뒤_절토_복합경사(fit: dict) -> None: tails = fit["guardTails"] assert tails and all(cut["endGap"] < 0.01 for cut in tails) # 유입 = 3도 선이 못 만나 수평공간 뒤 복합경사 · 유출 = 묻히면 3도 내림 선 하나(139-4) for cut in tails: assert cut["legs"][0]["ratio"] is None assert (len(cut["legs"]) == 1) == (cut["role"] == "outlet") assert sum(_knee(cut) for cut in tails) >= 2 def test_박스_끝_세월교_측벽_끝_절토_복합경사(fit: dict) -> None: # 세월교 바닥판 수평(139-3) — 높은 노견 쪽 끝은 암까지 안 닿는 짧은 토사 절토일 수 있음 for cuts in (fit["boxEnd"], fit["fordEnd"]): assert cuts and all(cut["endGap"] < 0.01 for cut in cuts) assert any(_knee(cut) for cut in cuts) rock = [c for c in cuts if any(leg["ratio"] == 0.4 for leg in c["legs"])] assert all(_knee(cut) for cut in rock) def test_절토_쪽_배관_유출_벽_뒤_절토사면_살아남(fit: dict) -> None: cut = fit["cutOutlet"] assert _knee(cut) and cut["endGap"] < 0.01 def test_소단은_구조물_끝에서_새로_셈(fit: dict) -> None: legs = fit["cutOutletBerm"]["legs"] flat = next(i for i, leg in enumerate(legs) if leg["ratio"] is None) assert sum(leg["slant"] for leg in legs[:flat]) == pytest.approx(3.0, abs=1e-3) assert legs[flat]["run"] == pytest.approx(0.5, abs=1e-3) assert fit["cutOutletBerm"]["endGap"] < 0.01