Files
Aislo/resources/tester/test_417_route_carry.py
T
eomsangdonandClaude Opus 5.5 041a22b4b3 fix(B05·B06 한 페이지): 노선 바뀐 저장 때 옛 저장 관 자리 조절 = 같은 관 id 의 새 자리로 (417-1 M2)
- 노선 저장 직전 저장 관(id · 옛 누가거리)을 떠 두고 저장 뒤 같은 id 새 자리로 — 초안으로 안 따라간 관 조절(첫 초안 측점 짝 · 관 id 규칙 갈림)
- carryChainageStates 시설 풀이 = 옛 자리 → 새 자리 · 관 열쇠 → 새 자리
- 시험 test_417 옛 자리 짝 · 346-3 저장 차례(관 받기)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Se7ytqbohmBQnfYpwYFnYf
2026-10-18 13:04:33 +09:00

180 lines
7.6 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# -*- coding: utf-8 -*-
"""417 — 노선 바뀐 [저장] 때 B06 조절 · 사면 쉬프트 · 소단 깊이 옮김(M2) · 평면 초안 횡단 입력
(M3 · L4).
못박는 것
- 브라우저: 측점 열쇠 칸(사면 쉬프트 · 소단 깊이) · 누가거리 열쇠 칸(옹벽 · BOX · 측점 폭 ·
상세 표 · 연동)이 새 노선 측점으로 옮겨 감 · 구조물 조절은 관 · 구조물 열쇠만(일반 측점 옹벽
조절은 버림).
- 옮긴 값이 새 상세 설계 칸에 얹힘(같은 [저장] 횡단 패치가 실음).
- 평면 초안 횡단 입력 = 세션 사면 쉬프트 · 소단 깊이 + 저장분(세션 없음)도.
- 서버: `carry_cross_inputs` 가 `ROUTE_CARRY_KEYS` 를 측점 열쇠마다 넘김 · 구조물 조절은 관
열쇠만 · `compute_default_designs` 가 넘긴 사면 쉬프트로 설계선을 셈.
- 거울: 브라우저 `ROUTE_CARRY_STATES` 설계 칸 = 서버 `ROUTE_CARRY_KEYS` +
`STRUCTURE_ADJUST_KEYS` = `USER_TOUCHED_KEYS` − 소단 제원 · 암 경사(둘은 `CARRY_INPUT_KEYS`).
Node 헬퍼 `helper_417_route_carry.mjs` 가 vite ssr 로 묶은 진짜 모듈(`helper_417_entry.ts`)을 부름.
"""
import json
import re
import shutil
import subprocess
import sys
from pathlib import Path
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
from B05_Profile.B05_Profile_Engine_Sections import find_cross_file # noqa: E402
from B05_Profile.B05_Profile_Route_Save_Parts import carry_cross_inputs # noqa: E402
from B06_Section.B06_Section_Engine_Design import ROUTE_CARRY_KEYS # noqa: E402
from B06_Section.B06_Section_Router_Design import ( # noqa: E402
CARRY_INPUT_KEYS,
STRUCTURE_ADJUST_KEYS,
USER_TOUCHED_KEYS,
compute_default_designs,
)
from config.config_system import STANDARD_CROSS_SECTION # noqa: E402
TS_KEYS = ROOT / "B06_Section" / "B06_Section_Station_Keys.ts"
@pytest.fixture(scope="module")
def out(tmp_path_factory):
work = tmp_path_factory.mktemp("b417")
build = subprocess.run(
[
"node",
str(ROOT / "config" / "node_modules" / "vite" / "bin" / "vite.js"),
"build",
"--configLoader",
"runner",
"--ssr",
"../resources/tester/helper_417_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_417_entry.js", bundle)
source = work / "input.json"
source.write_text(json.dumps({"standard": STANDARD_CROSS_SECTION}, default=str), "utf-8")
proc = subprocess.run(
["node", str(HERE / "helper_417_route_carry.mjs"), str(bundle), str(source)],
cwd=ROOT,
capture_output=True,
text=True,
encoding="utf-8",
timeout=120,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_사면_쉬프트_소단_깊이_새_노선으로(out):
assert out["shoulderKeys"] == ["grid:2"]
assert out["bermKeys"] == ["grid:2"]
def test_조절은_관_따라_옮김_일반_측점_옹벽은_버림(out):
assert out["revet"] == {"47.00:outlet": {"x": 0.3, "d": None, "h": 1.5, "m": None}}
assert out["box"] == {"47.00": {"left": {"x": 0.2}}}
assert out["link"] == {"detached": {"47.00": True}, "grade": {}}
assert out["width"] == {"40.00": 6}
assert out["table"] == {"47.00": {"rows": 2}}
assert out["chainageOutside"] == 1 # 40 m 옹벽 조절(일반 측점)
def test_관_id_규칙이_갈려도_시설_자리로_옮김(out):
assert out["splitResolved"] == {"52.00:outlet": {"x": 0.3, "d": None, "h": 1.5, "m": None}}
assert out["splitDropped"] == {} # 풀이 없음 = 같은 자리 측점 없음 → 버림
# 초안으로 안 따라간 옛 저장 시설 측점 고침 = 좌표 짝 시설 측점으로
assert out["movedByPlace"] == {"52.00:outlet": {"x": 0.3, "d": None, "h": 1.5, "m": None}}
def test_새_상세_설계_칸에_얹힘(out):
d40, d47 = out["design40"], out["design47"]
assert d40["shoulder_extend_left_m"] == 0.5 and d40["berm_depth_right_m"] == 1.2
assert d40["display_half_width_m"] == 6 and "revet_adjust" not in d40
assert d47["revet_adjust"]["outlet"]["h"] == 1.5
assert d47["box_adjust"]["left"]["x"] == 0.2
assert d47["revet_link_detached"] is True and d47["detail_table"] == {"rows": 2}
def test_평면_초안_횡단_입력에_사면_쉬프트_소단_깊이(out):
assert out["input40"] == {"shoulderExtendLeftM": 0.5, "bermDepthRightM": 1.2}
# 세션 없음 = 저장분 그대로(저장 뒤 사라지지 않음 · L4)
assert out["input20"] == {"shoulderExtendRightM": 0.7, "bermDepthLeftM": 0.4}
def test_서버_이월_측점_손질_넘김_구조물_조절은_관만():
old = {"grid:2": 40.0, "pipe:p1": 45.0, "grid:3": 60.0}
new = {"grid:2": 40.0, "pipe:p1": 47.0}
designs = {
40.0: {"shoulder_extend_left_m": 0.5, "structure_note": "메모", "revet_adjust": {"h": 1}},
45.0: {"berm_depth_right_m": 1.2, "revet_adjust": {"h": 2}, "detail_table": {"r": 1}},
60.0: {"shoulder_extend_right_m": 0.3},
}
inputs, adjusts, dropped = carry_cross_inputs(
old, new, designs, (*CARRY_INPUT_KEYS, *ROUTE_CARRY_KEYS), STRUCTURE_ADJUST_KEYS
)
assert inputs == {
40.0: {"shoulder_extend_left_m": 0.5, "structure_note": "메모"},
47.0: {"berm_depth_right_m": 1.2, "detail_table": {"r": 1}},
}
assert adjusts == {47.0: {"revet_adjust": {"h": 2}}}
assert dropped == 1
def test_서버_기본_설계가_넘긴_사면_쉬프트로_셈(tmp_path):
route = tmp_path / "routes" / "r1"
(route / "longitudinal").mkdir(parents=True)
(route / "cross_sections").mkdir()
profile = [
{"chainage_m": 0.0, "elevation_m": 100.0},
{"chainage_m": 40.0, "elevation_m": 100.0},
]
relative = "routes/r1/longitudinal/longitudinal.json"
(tmp_path / relative).write_text(
json.dumps({"stations": [{"chainage_m": 20.0}], "design_profiles": [{"samples": profile}]}),
"utf-8",
)
samples = [
{"offset_m": float(o), "elevation_m": 100.0 + 0.3 * o, "valid": True}
for o in range(-15, 16)
]
cross = {"chainage_m": 20.0, "samples": samples, "uphill_side": "left"}
find_cross_file(route / "cross_sections", 20.0).write_text(json.dumps(cross), "utf-8")
plain = compute_default_designs(tmp_path, relative, [20.0])
carried = {20.0: {"shoulder_extend_right_m": 0.8, "structure_note": "메모"}}
shifted = compute_default_designs(tmp_path, relative, [20.0], carried=carried)
assert plain and shifted
design = shifted[0][1]
assert design["shoulder_extend_right_m"] == pytest.approx(0.8)
assert design["structure_note"] == "메모"
assert "shoulder_extend_right_m" not in plain[0][1]
assert design["design_line"] != plain[0][1]["design_line"] # 설계선이 쉬프트로 바뀜
def test_거울_브라우저_옮김_목록_서버_이월_열쇠():
text = TS_KEYS.read_text(encoding="utf-8")
block = re.search(r"ROUTE_CARRY_STATES = \{(.*?)\} as const", text, re.S).group(1)
rows = re.findall(r":\s*\[([^\]]*)\]", block) # 칸 이름 → [설계 칸 …]
ts_keys = {key for row in rows for key in re.findall(r'"([a-z_]+)"', row)}
assert ts_keys == set(ROUTE_CARRY_KEYS) | set(STRUCTURE_ADJUST_KEYS)
assert ts_keys == set(USER_TOUCHED_KEYS) - {"berm", "cut_slope_ratio_user"}
assert {"berm", "cut_slope_ratio_user"} <= set(CARRY_INPUT_KEYS)