Files
Aislo/resources/tester/test_365_page_pipeline.py
T
eomsangdonandClaude Opus 5.5 7efee22ab4 feat(365-2): 횡단 고침 → 묶음 cross 입력 · 입력 지문 고리(최대 3바퀴)
- 횡단 입력 = 부를 때마다 저장 상세 + 세션 고침(지반 유형 · 측구 · 2단 경사 · 암 경계 · 암 경사 · 측점 열쇠) + 표준 — 상세 객체마다 기억 걷음
- B06 고침(DesignSync 다시 셈 끝 · 암 경계) 뒤 pipelineCross — 노선 초안 동안 B06 고침이 카드(새 노선 상세)에 보임
- 고리 — 열 때 · 페이지 들어올 때 입력(세션 · 가게)을 묶음 입력(넘긴 값 · follow 뒤 값)과 견줌 · 다르면 한 바퀴 · 끝난 뒤 다시 · 3바퀴 넘으면 「계산 불안정」 한 줄
- 시험 test_365_page_pipeline(초안 동안 고침 카드 · 같은 입력 0바퀴 · 세션만 바뀜 1바퀴 · 불안정 3바퀴 알림 1)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TteK8mfYZetk5P3bEZkyVZ
2026-10-17 12:08:22 +09:00

162 lines
6.2 KiB
Python

"""PLAN 365 sub7 — 평면 > 종단 <> 횡단 한 방향 · 묶음 주인 = 페이지 · 캐시 · 고리.
못박는 것(365-1)
- 묶음은 페이지(종단도 한 벌 `sharedProfile`)가 세움 — 종단 보기는 다리만 받음(옵션 안 넘김).
- 노선 초안 둔 채 다시 열기(새로고침 · B06 바로 열기 · 편집기 없음) = B05 길과 같은 초안 상세
(측점 · 계획고 · 면적) · 측점을 가게에 다시 냄 · 초안 전 = 저장 상세(열 때 맡김) → 버리면 saved.
- 초안 동안 측점 갈아끼움은 메모리만 — 세션 상세(저장 노선)를 새 측점 + 옛 설계로 덮지 않음.
못박는 것(365-2)
- 초안 동안 B06 고침(세션 칸 · 측점 열쇠) → `pipelineCross` → 묶음 cross 입력 · 카드에 보임.
- 고리 — 같은 입력 = 0 바퀴 · 세션만 바뀜 = 1 바퀴 · 안 맞으면 3 바퀴 뒤 「계산 불안정」 한 번.
짝: `helper_365_page_pipeline.cjs`(진짜 TS · 서버 창구만 가짜 · 틀 = 346-2 시험) ·
`helper_365_swap_entry.ts` + `helper_365_swap.mjs`(진짜 상세 가게 · vite ssr).
"""
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]
if str(HERE) not in sys.path:
sys.path.insert(0, str(HERE))
import test_346_2_pipeline_cross_page as base # noqa: E402
from common_util.common_util_surface_sampler import DtmGridSampler, dtm_grid_bytes # noqa: E402
pytestmark = base.pytestmark
def _job(folder: Path) -> tuple[Path, Path]:
npz, nodes = base._fake(folder)
line = base._line(nodes)
sampler = DtmGridSampler.from_npz(npz)
pipes = base._pipes(line)
saved = base._sections(line, sampler, pipes)
options = base.resolve_grade_options("trunk", terrain_type="normal", paved=False, stored=None)
policy = base.design_ground_following_profile(saved["longitudinal"], options)[0]["policy"]
job = {
"nodes": nodes,
"line": line,
"minRadius": base.MIN_RADIUS_M,
"policy": policy,
"types": base._types(),
"interval": base.INTERVAL_M,
"pipes": pipes,
"standard": base.STANDARD_CROSS_SECTION,
"conversion": base.ONES,
"detail": base._saved_detail(saved),
"haulDelayMs": 10,
}
path = folder / "job.json"
path.write_text(json.dumps(job), encoding="utf-8")
grid = folder / "grid.bin"
grid.write_bytes(dtm_grid_bytes(npz))
return path, grid
@pytest.fixture(scope="module")
def r() -> dict:
with tempfile.TemporaryDirectory() as folder:
path, grid = _job(Path(folder))
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(HERE / "helper_365_page_pipeline.cjs"), str(path), str(grid)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=180,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_365_1_묶음_주인_페이지():
shared = (ROOT / "A00_Common/design/design_shared.ts").read_text(encoding="utf-8")
view = (ROOT / "A00_Common/design/design_view_profile.ts").read_text(encoding="utf-8")
assert "routeDraftBridge(projectId, profile, {" in shared # 종단도 한 벌과 함께 세움
assert "draft: routeDraftBridge(activeProjectId, profilePanel)," in view # 다리만 받음
assert "holdDraft" not in view and "holdDraft" in shared
def test_365_1_초안_둔_채_열기_같은_초안(r):
b05, b06 = r["b05"], r["b06"]
assert r["kept"] # 떠나도 세션 초안 남음
assert b05["rows"] and b06["rows"] == b05["rows"] # 측점 · 계획고 · 면적 같음
assert b06["outcome"] == "done" and "cross" in b06["stages"]
assert b06["stations"] == b05["stations"] == [row[0] for row in b05["rows"]] # 측점 다시 냄
assert b05["holds"] == ["saved"] and b06["holds"] == ["saved"] # 초안 전 = 저장 상세
def test_365_1_초안_버림_저장_정본(r):
d = r["dropped"]
assert d["detail"] is None and d["routeDraft"] is None
assert d["restoreWhy"] == "saved"
def test_365_2_초안_동안_횡단_고침_카드에(r):
e = r["edit"]
assert e["before"] != "blasting_rock"
assert e["input"] == "blasting_rock" and e["after"] == "blasting_rock"
assert e["runs"] == 1 and e["sources"] == ["cross"]
def test_365_2_고리_바퀴_수(r):
loop = r["loop"]
assert loop["same"] == 0 and loop["sameRuns"] == 0 # 같은 입력 = 안 돎
assert loop["once"] == 1 and loop["onceRuns"] == 1 and loop["onceGround"] == "ripping_rock"
assert loop["unstable"] == -1 and loop["unstableRuns"] == 3 and loop["alerts"] == 1
@pytest.fixture(scope="module")
def swap(tmp_path_factory) -> dict:
work = tmp_path_factory.mktemp("b365")
build = subprocess.run( # noqa: S603 — 고정 실행 파일
[
"node",
str(ROOT / "config" / "node_modules" / "vite" / "bin" / "vite.js"),
"build",
"--configLoader",
"runner",
"--ssr",
"../resources/tester/helper_365_swap_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_365_swap_entry.js", bundle)
proc = subprocess.run( # noqa: S603 — 고정 실행 파일
["node", str(HERE / "helper_365_swap.mjs"), str(bundle)],
capture_output=True,
text=True,
encoding="utf-8",
timeout=120,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
def test_365_1_초안_동안_세션_상세_안_덮음(swap):
d = swap["draft"]
assert d["swapped"] and d["cache"] == [0, 20, 30, 40, 60] # 메모리 = 초안 측점
assert d["session"] == [0, 20, 40, 60] # 세션 = 저장 노선 그대로
s = swap["saved"]
assert s["swapped"] and s["session"] == s["cache"] == [0, 20, 50, 60] # 초안 없으면 세션도