# -*- coding: utf-8 -*- """467 (요구 272 · 분석 4장 (B1)) — 노선 초안 없는 차례의 갈아끼움이 정본 횡단을 지킴. 못박는 것 - 관만 옮김(노선 초안 없음) — 같은 열쇠 · 누가거리 0.01 m 안 측점은 앞 횡단(서버 정본) 기하 · 표본 · 중심고 그대로(R 12 곡선 축 차 3.3e-4 · 중심 0.5 mm 차여도) · 옮긴 관 측점만 브라우저 표본. - 되돌림(관 제자리) — 관 측점 밖 모든 횡단이 정본과 바이트 같음(관 측점 = 브라우저 표본). - 노선 초안 차례 = 옛 규칙(축 허용 1e-4 · 넘으면 브라우저 표본). - 가게 길(`swapSectionStations`) = 가게 `routeDraft` 로 가름 — 없음 · 있음 · 초안 버린 직후(초안 표본이 안 남음). Node 헬퍼 `helper_272_swap_canon.mjs` 가 vite ssr 로 묶은 진짜 모듈을 부름. """ import json import shutil import subprocess from pathlib import Path import pytest HERE = Path(__file__).resolve().parent ROOT = HERE.parents[1] CURVES = ("27.50", "33.20", "38.90") STRAIGHT = ("0.00", "20.00", "40.00", "60.00", "70.00") STILL = (*STRAIGHT, *CURVES) @pytest.fixture(scope="module") def out(tmp_path_factory): work = tmp_path_factory.mktemp("b272swap") build = subprocess.run( # noqa: S603 — 고정 실행 파일 [ "node", str(ROOT / "config" / "node_modules" / "vite" / "bin" / "vite.js"), "build", "--configLoader", "runner", "--ssr", "../resources/tester/helper_272_swap_canon_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_272_swap_canon_entry.js", bundle) proc = subprocess.run( # noqa: S603 ["node", str(HERE / "helper_272_swap_canon.mjs"), str(bundle)], 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): keep = out["pure"]["keep"] for at in STILL: assert keep[at]["same"], at # 곡선 축 차 3.3e-4 여도 서버 표본 · 기하 assert keep[at]["mark"] == f"canon@{float(at):g}" assert keep["55.00"]["browser"] and not keep["55.00"]["same"] # 옮긴 관 = 새 자리 표본 assert out["pure"]["keepMoved"] is False # 「노선 밖 고침」 셈 그대로(0.01 m) assert out["pure"]["defaultIsKeep"] # 기본 = 노선 초안 없음 def test_되돌림_정본과_바이트_같음(out): back = out["pure"]["back"] assert all(back[at]["same"] for at in STILL) assert out["pure"]["backBytes"] # 관 측점 밖 횡단 = 정본 바이트 assert back["45.00"]["browser"] # 관 측점 = 브라우저 표본(정본은 저장 뒤 서버 값) def test_격자에_구조물_놓고_뺌_같은_자리_정본(out): g = out["gridStructure"] assert ( g["placed"]["same"] and not g["placed"]["browser"] ) # 열쇠 바뀜(구조물) · 같은 자리 = 정본 assert g["removed"]["same"] and not g["removed"]["browser"] # 다시 격자 = 정본 표본 · 기하 assert g["removedBytes"] def test_노선_초안_차례는_옛_규칙(out): draft = out["pure"]["draft"] for at in CURVES: # 축 차 > 1e-4 = 새 표본 assert draft[at]["browser"] and not draft[at]["same"], at for at in STRAIGHT: # 축 차 1e-5 · 자리 같음 = 앞 표본(기하는 새 틀) assert not draft[at]["browser"] and not draft[at]["same"], at def test_가게_길_초안_없음(out): s = out["storeNoDraft"] assert s["moved"] and s["back"] assert all(s["moved"][at]["same"] for at in STILL) assert all(s["back"][at]["same"] for at in STILL) and s["backBytes"] def test_가게_길_초안_있음_버린_직후(out): s = out["storeDraft"] assert s["routeDraftSet"] for at in CURVES: # 초안이 옮긴 곡선 = 새 표본 assert s["draft"][at]["browser"], at for at in STRAIGHT: # 초안 차례 = 옛 규칙(기하 새 틀 · 표본 앞 값) assert not s["draft"][at]["same"] and not s["draft"][at]["browser"], at dropped = s["dropped"] for at in ( CURVES ): # 버린 뒤 = 저장 정본 자리 · 표본(초안 3 cm 자리 표본이 안 남음 · 467 덧 정본 물러섬) assert dropped[at]["same"] and not dropped[at]["browser"], at for at in STRAIGHT: # 초안이 안 옮긴 측점 = 앞 횡단 표본(서버) 그대로 assert not dropped[at]["browser"], at def test_옮겼다_제자리_관_측점_정본(out): p = out["pipeBack"] assert p["pipe"]["same"] and not p["pipe"]["browser"] # 저장 정본 표본 · 기하 assert p["bytes"] # 관 밖 횡단 = 정본 바이트(관 설계 = 옮김 규칙 · 다시 셈 입력은 정본 물러섬) def test_일반_측점_자리_관_지움_다른_목록(out): g = out["pipeGone"] assert g["placedKind"] == "irregular" assert g["same"] is False # 관 카드 → 일반 측점 = 갈아끼움 assert g["sameUntouched"] is True # 일반 측점 그대로 = 같은 목록 assert g["culvertLeft"] is False and g["kind"] == "regular" # 관 카드 안 남음 assert g["canon"] # 그 자리 정본 표본 · 기하 def test_관_지움_되돌림_정본_설계(out): r = out["pipeRestore"] assert r["design"] and r["copy"] # 지우기 전 설계(정본 사본) — 빈 기본 설계 아님(472 덧) assert r["place"] and r["bytes"] assert r["bareFresh"]