Files
Aislo/resources/tester/test_349_0p_fractional_station.py
T
eomsangdonandClaude Opus 5.5 e1f8ba7bff refactor(472 덧 관 자리): 관 세트 읽기 · 얹기를 B06_Section_Engine_Culvert_Sets.py 로 나눔 — 동작 같음 · 700줄 여유
- load_culvert_sets · culvert_sets_from · attach_culvert_sets · attach_culvert_specs + 얹기 상수 셋 옮김(700 → 576)
- 부르는 쪽(Engine_Attach · Engine_Tilt · Router_Design_Ranges) · 시험 import 만 새 자리로
- test_b06_pipe_length 소스 읽기 = 새 파일 · 위키 B06_backend 자리 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 12:11:06 +09:00

179 lines
7.1 KiB
Python

"""PLAN 349-0p — 관 · 구조물 측점이 소수 자리(386.6 · 150.3)인 노선 꼴 · 입력 없는 측점 암 경계.
· 세월교 · 물넘이 · 관이 소수 자리 누가거리에 선 노선 — cross 단계 결과(포장 · 노면 하강 · 기울임 ·
면적 · 흙 · 암 나눔) = 서버 [확정] 보정 뒤 설계
· 저장 입력이 없는 측점(초안 새 측점) — 암 경계 = 기본 오프셋(서버 config · 화면이 서버 문맥에서
기억한 값) · 기본값이 안 오면 옛 그대로(경계 없음 = 깎기 전부 암)
짝: `helper_349_0n_cross_enforce.cjs` · 틀 = `test_349_0n_cross_enforce_mirror.py`.
"""
from __future__ import annotations
import json
import subprocess
import sys
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_349_0n_cross_enforce_mirror import OFFSETS, _stations, _z # noqa: E402
import B05_Profile.B05_Profile_Structures_Repository as structures_repo # noqa: E402
import B06_Section.B06_Section_Engine_Tilt as tilt_mod # noqa: E402
import B06_Section.B06_Section_Router_Design as design_mod # noqa: E402
import B06_Section.B06_Section_Router_Design_Ranges as ranges_mod # noqa: E402
from B05_Profile.B05_Profile_Structures_Schema import StructureInstance # noqa: E402
from B06_Section.B06_Section_Engine_Culvert_Sets import culvert_sets_from # noqa: E402
from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402
from common_util.common_util_drainage_pipes import parse_pipe_points # noqa: E402
from config.config_system_design import ( # noqa: E402
STANDARD_CROSS_SECTION,
STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M,
)
pytestmark = pytest.mark.skipif(
not (ROOT / "config" / "node_modules" / "typescript").is_dir(),
reason="프론트엔드 의존성 미설치",
)
PIPES = [
{"chainage_m": 150.3, "options": {"pipe_diameter_mm": 800}},
{
"chainage_m": 320.4,
"facility": "ford_pavement",
"options": {"ford_width_m": 12, "ford_slope_deg": 4},
},
{"chainage_m": 386.6, "facility": "ford_bridge", "options": {"ford_height_m": 0.25}},
]
STRUCTURES = [
StructureInstance(
structure_id="pv1",
type_id="gravel_surfacing",
placement="interval",
start_m=40.3,
end_m=80.6,
),
]
OWN_ROCK = -1.0 # 저장 입력이 있는 측점(짝수 격자)만 — 나머지는 초안 새 측점처럼 입력 없음
def _inputs(stations: list[dict]) -> dict:
return {
s["key"]: {"sectionMode": "left_cut", "rockBoundaryOffsetM": OWN_ROCK}
for s in stations
if s["key"].startswith("grid:") and int(s["key"][5:]) % 2 == 0
}
def _run(tmp: Path, name: str, default: float | None) -> dict:
stations = _stations(PIPES)
job = {
"pipes": PIPES,
"structures": [s.model_dump(mode="json") for s in STRUCTURES],
"stations": stations,
"probe": [0],
"standard": STANDARD_CROSS_SECTION,
"byKey": _inputs(stations),
"rockBoundaryDefaultM": default,
}
path = tmp / f"{name}.json"
path.write_text(json.dumps(job), encoding="utf-8")
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(HERE / "helper_349_0n_cross_enforce.cjs"), str(path)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=120,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)["stage"]["byKey"]
@pytest.fixture(scope="module")
def r(tmp_path_factory) -> dict:
tmp = tmp_path_factory.mktemp("frac")
return {
"stations": _stations(PIPES),
"with": _run(tmp, "with", STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M),
"without": _run(tmp, "without", None),
}
@pytest.fixture(scope="module")
def server(r) -> list[dict]:
"""서버 [확정] — 저장분(입력 없는 측점은 config 암 경계) → 포장 구간 → 세월교 하강."""
patch = pytest.MonkeyPatch()
sets = culvert_sets_from(parse_pipe_points(PIPES))
# 구간 읽기는 472-4 에서 `_Router_Design_Ranges` 로 나눔 — 정본 읽기를 그 모듈에서 갈아 끼움.
for module in (ranges_mod, tilt_mod):
patch.setattr(module, "load_culvert_sets", lambda _root: sets)
patch.setattr(module, "load_structures", lambda _root: (0, STRUCTURES))
patch.setattr(structures_repo, "load_structures", lambda _root: (0, STRUCTURES))
own = _inputs(r["stations"])
longitudinal = {
"design_profiles": [
{
"samples": [
{"chainage_m": s["chainageM"], "elevation_m": s["elevation"]}
for s in r["stations"]
]
}
]
}
sections = []
for s in r["stations"]:
samples = [
{"offset_m": o, "elevation_m": _z(o, s["chainageM"]), "valid": True} for o in OFFSETS
]
rock = OWN_ROCK if s["key"] in own else STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M
design = compute_cross_design(
samples,
s["elevation"],
ground_type="ripping_rock",
section_mode="left_cut",
ditch_type="standard",
standard=STANDARD_CROSS_SECTION,
rock_boundary_offset_m=rock,
)
sections.append({"chainage_m": s["chainageM"], "samples": samples, "design": design})
try:
root = Path("unused")
design_mod.enforce_pavement_ranges(longitudinal, sections, root, STANDARD_CROSS_SECTION)
design_mod.enforce_ford_surface_drops(longitudinal, sections, root, STANDARD_CROSS_SECTION)
finally:
patch.undo()
return sections
def test_소수_자리_측점_결과_서버와_같음(r, server):
keys = ("cut_area_m2", "cut_soil_area_m2", "cut_rock_area_m2", "fill_area_m2")
for station, section in zip(r["stations"], server, strict=True):
a, b = r["with"][station["key"]], section["design"]
for key in keys:
assert a[key] == pytest.approx(b[key], abs=2e-3), (station["key"], key)
for key in ("paved", "tilted", "surface_drop_m", "rock_boundary_offset_m"):
assert a.get(key) == b.get(key), (station["key"], key)
by = {s["key"]: s["chainageM"] for s in r["stations"]}
assert r["with"]["pipe:p2"]["surface_drop_m"] == 0.25 and by["pipe:p2"] == 386.6
assert r["with"]["pipe:p1"]["tilted"] and r["with"]["grid:16"]["paved"] # 320.4 ± 6
assert r["with"]["grid:4"]["paved"] and not r["with"]["grid:2"]["paved"] # 40.3 ~ 80.6
def test_입력_없는_측점_암_경계_기본값(r):
rock = [
s["key"]
for s in r["stations"]
if r["with"][s["key"]]["cut_area_m2"] > 0.01 and s["key"] not in _inputs(r["stations"])
]
assert rock # 깎기 있는 새 측점이 실제로 있음
for key in rock:
assert r["with"][key]["rock_boundary_offset_m"] == STANDARD_ROCK_BOUNDARY_DEFAULT_OFFSET_M
assert r["without"][key]["cut_soil_area_m2"] == 0 # 기본값이 안 오면 옛 그대로(전부 암)
assert any(r["with"][key]["cut_rock_area_m2"] > 0 for key in rock)