"""PLAN 439 — B06 카드 머리 줄 단추가 저장 노선에서 늘 반영됨(요구 266). 못박는 것 - 저장 노선 cross 차례(`refreshSavedCross`) = 카드 자료(상세 설계)를 **늘** 다시 셈 — 복합경사 · 측구 · 지반 단추마다 그 측점 설계선 · 면적이 바뀜. - 묶음 측점 = 상세 측점이면 돌려줌 · 1 mm 넘게 다르면 null(묶음 엔진) — 그래도 카드 자료는 고침. - 사면 이동 한 걸음도 같음 · 걸음마다 받는 쪽(카드 · 종단 · 유토 다시 그림) 한 번(441 · 요구 268-2 · 4). - 선형 저장분 없음 = 서버 폴백 없음(511-B B-4 · `/cross-design/preview` 걷음). 짝: `helper_266_card_toggle.mjs` · 진입점 `helper_266_card_toggle_entry.ts` · 지형 · 노선 틀 = `test_346_2_pipeline_cross_page.py`. """ from __future__ import annotations import json import shutil import subprocess import sys import tempfile from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] for path in (ROOT, HERE): if str(path) not in sys.path: sys.path.insert(0, str(path)) from test_346_2_pipeline_cross_page import _fake, _line, _sections # noqa: E402 from common_util.common_util_surface_sampler import DtmGridSampler # noqa: E402 from config.config_system import STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M # noqa: E402 from config.config_system_design import STANDARD_CROSS_SECTION # noqa: E402 pytestmark = pytest.mark.skipif( not (ROOT / "config" / "node_modules" / "vite").is_dir(), reason="프론트엔드 의존성 미설치", ) CUT_DEPTH_M = 3.0 @pytest.fixture(scope="module") def r(tmp_path_factory) -> dict: work = tmp_path_factory.mktemp("b439") build = subprocess.run( # noqa: S603 — 고정 실행 파일 [ "node", str(ROOT / "config" / "node_modules" / "vite" / "bin" / "vite.js"), "build", "--configLoader", "runner", "--ssr", "../resources/tester/helper_266_card_toggle_entry.ts", "--outDir", str(work), "--logLevel", "error", ], cwd=ROOT, capture_output=True, text=True, encoding="utf-8", timeout=300, ) assert build.returncode == 0, build.stderr bundle = work / "bundle.mjs" shutil.copy(work / "helper_266_card_toggle_entry.js", bundle) with tempfile.TemporaryDirectory() as folder: npz, nodes = _fake(Path(folder)) sampler = DtmGridSampler.from_npz(npz) rows = _sections(_line(nodes), sampler, [])["cross_sections"] middle = len(rows) // 2 picked = rows[middle - 1 : middle + 2] sections = [{**s, "plan_z": float(s["center_z"]) - CUT_DEPTH_M} for s in picked] job = { "sections": sections, "target": 1, "mode": f"{picked[1].get('uphill_side') or 'left'}_cut", "standard": STANDARD_CROSS_SECTION, "rockDefault": STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M, } path = Path(folder) / "job.json" path.write_text(json.dumps(job), encoding="utf-8") proc = subprocess.run( # noqa: S603 — 고정 실행 파일 ["node", str(HERE / "helper_266_card_toggle.mjs"), str(bundle), str(path)], cwd=ROOT, capture_output=True, text=True, encoding="utf-8", timeout=120, ) assert proc.returncode == 0, proc.stderr return json.loads(proc.stdout) def _check_toggles(case: dict, returned) -> None: steps = {step["tag"]: step for step in case["steps"]} assert steps["open"]["cut"] > 0.5 # 깎기 측점 before = steps["open"] assert all(step["calls"] == 1 for step in case["steps"]) # 걸음마다 다시 그림 한 번(441) for tag in ("two_stage", "ditch_off", "soil", "slope"): step = steps[tag] assert case["target"] in step["updated"], tag # 카드 다시 그림 대상 assert step["line"] != before["line"], tag # 카드 설계선 바뀜 assert step["out"] == returned, tag before = step assert steps["two_stage"]["two_stage_slope"] is True assert steps["ditch_off"]["ditch_enabled"] is False assert steps["soil"]["ground_type"] == "soil" assert steps["soil"]["cut"] != steps["open"]["cut"] # 면적 바뀜 def test_측점_같음_카드_단추마다_설계선_면적_바뀜_돌려줌(r): _check_toggles(r["same"], 3) def test_측점_어긋남_카드_자료는_고침_돌려줄_값만_null(r): _check_toggles(r["mismatch"], None) def test_선형_저장분_없음_서버_폴백_없음(r): # 511-B B-4 — 옛 데이터 서버 프리뷰(`/cross-design/preview`) 걷음 · 부름 0 assert r["server"]["calls"] == 0, r["server"]