# -*- coding: utf-8 -*- """PLAN 206-1 — 양측 성토 측점에 측구가 남는 흠(검증_sub1 No.1+0 사본 재현). 원인 — 측구 판정의 「양측 성토 = 측구 없음」이 **입력** `section_mode` 를 봤다. 입력은 옛 저장값 (초기값 right_cut)이고, 계획고를 +1.98 m 올려 지형 판정(역할)은 양측 성토가 됐는데 입력이 그대로라 옛 결과 `ditch_enabled=True` 가 사용자 선택으로 둔갑(ditch_choice=True)해 측구가 섰다. 좌 · 우를 누르면 카드가 판정값 both_fill 을 입력으로 다시 보내 그때서야 사라졌다. 고침 — 판정된 역할(양쪽 성토)로 가름 · 파이썬 · TS 짝 같은 규칙 · 옛 저장분은 낡음으로 다시 셈. """ from __future__ import annotations import sys from pathlib import Path import pytest PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402 # 검증_sub1(7e420ed6) 노선 230 · No.1+0(20 m) 지반선 사본 — -20 ~ +20 m · 0.5 m 간격 _Z = [ 885.0166, 884.8327, 884.6401, 884.4294, 884.2270, 883.7564, 883.2482, 882.8840, 882.5737, 882.3786, 882.2795, 881.9692, 881.3788, 880.7761, 880.1219, 879.4944, 879.1738, 878.8550, 878.6597, 878.4670, 878.1648, 877.8416, 877.5119, 877.1749, 876.8564, 876.5584, 876.2612, 875.9646, 875.6791, 875.4169, 875.1405, 874.7286, 874.3079, 874.0026, 873.7065, 873.4074, 873.1107, 872.7726, 872.4127, 872.1306, 871.9252, 871.7424, 871.6078, 871.3723, 870.7716, 870.1964, 869.8586, 869.5135, 869.2563, 869.0105, 868.5769, 868.0912, 867.7274, 867.4369, 867.1615, 866.9033, 866.6590, 866.4489, 866.2281, 865.9711, 865.7139, 865.4391, 865.1650, 864.8953, 864.6289, 864.3219, 863.9996, 863.6703, 863.3343, 863.0443, 862.8261, 862.5776, 862.2364, 861.9035, 861.6491, 861.3932, 861.1137, 860.8302, 860.6107, 860.4051, 860.2006, ] # fmt: skip SAMPLES = [{"offset_m": -20.0 + 0.5 * i, "elevation_m": z} for i, z in enumerate(_Z)] ELEV = 873.900731 # 지금 계획고(초기값 871.9252 에서 +1.98 m) # 그 프로젝트 저장분과 같은 입력 — 옛 단면유형 · 옛 측구 결과 STORED = { "ground_type": "ripping_rock", "ditch_side": "right", "ditch_type": "standard", "rock_boundary_offset_m": -0.5, "two_stage_slope": True, } _GROUP = { "road_width_m": 3, "shoulder_left_m": 0.5, "shoulder_right_m": 0.5, "ditch": {"top_width_m": 0.9, "bottom_width_m": 0.3, "depth_m": 0.3}, "fill_slope_ratio": 1.2, } # 그 프로젝트 종단 options 의 표준 횡단면 STANDARD = { "soil": {**_GROUP, "cross_slope_pct": {"min": 3, "max": 5}, "cut_slope_ratio": 1}, "rock": { **_GROUP, "ditch_l_type": {"width_m": 0.5, "depth_m": 0.1}, "cross_slope_pct": {"min": 3, "max": 5}, "cut_slope_ratio": 0.4, }, "paved": { **_GROUP, "cross_slope_pct": {"min": 1.5, "max": 2}, "cut_slope_ratio": 1, "pavement_thickness_m": 0.2, }, } def _design(section_mode: str, **kwargs): return compute_cross_design( SAMPLES, ELEV, section_mode=section_mode, standard=STANDARD, **STORED, **kwargs ) @pytest.mark.parametrize( "stale", [ {"ditch_enabled": True}, # 옛 결과만(초기값 → 계획고 변경 재계산) {"ditch_enabled": True, "ditch_choice": True}, # 둔갑한 선택이 저장된 뒤(지금 DB) ], ) @pytest.mark.parametrize("mode", ["right_cut", "both_cut", "left_cut"]) def test_사본_양측_성토면_옛_입력이어도_측구_없음(mode: str, stale: dict) -> None: design = _design(mode, **stale) assert design["section_mode"] == "both_fill" assert design["ditch_enabled"] is False, "양측 성토인데 측구가 섰다" assert design["ditch_area_m2"] == 0.0 assert design["ditch"] == {"type": "none"} assert design["ditch_choice"] is None, "옛 결과가 선택으로 남았다" def test_사본_옛_입력과_바른_입력이_같은_형상() -> None: """좌 · 우를 눌러야만 사라지던 것 — 이제 입력이 옛 값이어도 처음부터 같은 형상 · 면적.""" stale = _design("right_cut", ditch_enabled=True, ditch_choice=True) fresh = _design("both_fill") for key in ("design_line", "fill_area_m2", "cut_area_m2", "ditch_area_m2"): assert stale[key] == fresh[key], key assert fresh["fill_area_m2"] == pytest.approx(34.0513, abs=1e-4) # 우 노견 바깥에 측구 꼭짓점(-2.3 · -2.6 · -2.9 m = 상단 0.9 · 저폭 0.3 사다리꼴)이 없다 offsets = {p["offset_m"] for p in fresh["design_line"]} assert not offsets & {-2.3, -2.6, -2.9} def test_한쪽이라도_절토면_측구_그대로() -> None: """양측 성토 판정은 역할로만 — 우 절토 지형은 옛 입력 both_fill 이어도 측구 섬.""" design = _design("both_fill", ditch_enabled=False) assert design["section_mode"] == "both_fill" # 이 지형은 양측 성토 cut = compute_cross_design(SAMPLES, ELEV - 2.5, section_mode="both_fill", **STORED) assert cut["section_mode"] in ("right_cut", "both_cut") assert cut["ditch_enabled"] is True and cut["ditch_side"] == "right" def test_TS_짝도_같은_값(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: """거울 — 같은 사본을 TS `computeCrossDesign` 에 넣어 파이썬과 같은지(측구 없음까지).""" import test_b06_cross_design_mirror as mirror cases = [ { "name": f"206-1 사본 {mode}", "samples": SAMPLES, "design_elevation_m": ELEV, "options": { "section_mode": mode, **STORED, "ditch_enabled": True, "ditch_choice": True, }, } for mode in ("right_cut", "both_fill") ] monkeypatch.setattr(mirror, "CASES", cases) py, ts = mirror._python_results(), mirror._ts_results(tmp_path) problems = [] for case, left, right in zip(cases, py, ts): problems.extend(mirror._diff(left, right, case["name"])) assert not problems, "\n".join(problems[:20]) assert all(item["ditch_enabled"] is False for item in ts) def _src(rel: str) -> str: return (PROJECT_ROOT / rel).read_text(encoding="utf-8") def test_옛_저장분은_낡음으로_다시_셈() -> None: """DB 에 남은 양측 성토 + 측구 결과 → B05 · B06 진입 때 브라우저가 다시 셈(공용 낡음 판정).""" common = _src("B06_Section/B06_Section_UI_Section_Common.ts") assert 'design.section_mode === "both_fill" && design.ditch_enabled === true' in common def test_측구가_그려져_있으면_단추를_숨기지_않음() -> None: """179-2 숨김은 「막힘 + 그려진 측구 없음」일 때만 — 그려진 측구는 끌 수 있어야 함.""" cd = _src("B06_Section/B06_Section_UI_Cross_Design.ts") assert "const ditchDrawn = design?.ditch_enabled === true;" in cd assert 'const hasDitch = state.mode !== "both_fill" || ditchDrawn;' in cd assert '(state.mode !== "both_fill" || ditchDrawn)' in cd # 측구형식도 같은 규칙