"""PLAN 472-3 3-B (요구 273) — 파이썬 실행기 `common_util_calc_runner` → Node 입구 → 기존 묶음 값. 못박는 것 - `NODE_IDS` = 등록부 `node:` 줄 = 입구 표(`available`). - 고정 자료 `fixtures/golden_472` 로 `run_calc` 값 = [저장] 재계산이 부르는 server-calc 묶음 값 (`Server_Calc_Prebuild` BUNDLE · 면적 · 다단 벽 · 누가토량 · 배분 · 끝값 −17,570.7). - Node 실행기 아닌 id(파이썬 · 브라우저 실행기)는 ValueError. ./venv/Scripts/python.exe -m pytest resources/tester/test_472_3_calc_runner.py -q """ from __future__ import annotations import json import re from pathlib import Path import pytest from common_util import common_util_calc_runner as runner HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] FIXTURE = HERE / "fixtures" / "golden_472" REGISTRY = ROOT / "A00_Common" / "design" / "logic" / "design_calc_registry.ts" pytestmark = pytest.mark.skipif( not (ROOT / "config" / "node_modules" / "vite").is_dir(), reason="프론트엔드 의존성 미설치", ) def _fixture(name: str): return json.loads((FIXTURE / name).read_text(encoding="utf-8")) def _context() -> dict: ctx = _fixture("context.json") keys = ("earthwork_conversion", "haul_equipment_limits", "natural_spoil_min_ground_slope") return {k: ctx[k] for k in keys} def test_NODE_IDS_등록부_입구와_같음() -> None: src = "".join( # 등록부 두 몫(나눔) path.read_text(encoding="utf-8") for path in (REGISTRY, REGISTRY.with_name("design_calc_registry_facility.ts")) ) assert set(re.findall(r'runner: "node:([a-z_.]+)"', src)) == runner.NODE_IDS out = runner.run_bundle_json(runner.BUNDLE, runner.NPM_SCRIPT, {"run": [], "inputs": {}}) assert set(out["available"]) == runner.NODE_IDS def test_실행기_값_server_calc_와_같음() -> None: from B06_Section.B06_Section_Server_Calc_Prebuild import _NPM_SCRIPT, BUNDLE from common_util.common_util_node_bundle import run_bundle_json sc = run_bundle_json( BUNDLE, _NPM_SCRIPT, {"detail": _fixture("detail.json"), "context": _context()} ) got = runner.run_calc( ["structure.layouts", "masshaul.cumulative", "haul.plan"], {"detail": _fixture("detail.json"), "context": _context()}, ) assert got["structure.layouts"]["areas"] == sc["areas"] and sc["areas"] assert got["structure.layouts"]["extra_walls"] == sc["extra_walls"] merged = {**got["masshaul.cumulative"], **got["haul.plan"]} assert merged == sc["mass_haul"] assert merged["final_cumulative_m3"] == -17570.7 def test_Node_실행기_아닌_id_는_ValueError() -> None: with pytest.raises(ValueError, match="ground.widen"): runner.run_calc(["masshaul.cumulative", "ground.widen"], {}) # ── 472-4 ② — 횡단 설계선 · 소단 구간 Node 길 ────────────────────────────────── def test_소단_구간_Node_와_파이썬_짝_같음(tmp_path, monkeypatch) -> None: from test_272_berm_source import STRUCTURE_CASES, _project from B06_Section import B06_Section_Engine_Design_Node as node_mod from B06_Section.B06_Section_Engine_Structures_Wall import berm_span_at, load_berm_spans calls = [] real = node_mod.run_calc monkeypatch.setattr( node_mod, "run_calc", lambda ids, inputs: calls.append(ids) or real(ids, inputs) ) chainages = [0.0, 5.0, 50.0, 65.0, 80.0, 100.0, 150.0, 250.0] for index, structures in enumerate(STRUCTURE_CASES): root = _project(tmp_path / f"c{index}", structures) got = node_mod.berm_specs_at(root, chainages) spans = load_berm_spans(root) want = [berm_span_at(spans, c) for c in chainages] assert got == [pytest.approx(w) if w else w for w in want], index assert len(calls) == len(STRUCTURE_CASES) # 파이썬 물러섬 아님 def test_횡단_설계_못_세운_측점은_None() -> None: from B06_Section.B06_Section_Engine_Design_Node import compute_cross_designs ground = [{"offset_m": o, "elevation_m": 100.0 + 0.3 * o} for o in range(-20, 21)] options = {"ground_type": "soil", "section_mode": "left_cut"} got = compute_cross_designs([(ground, 100.0, options), (ground, None, options)], None) assert got[0]["cut_area_m2"] > 0 and got[1] is None