Files
Aislo/resources/tester/test_298_add_reanalyze.py
T
eomsangdonandClaude Opus 5.5 8ce01dfa35 fix(b06): 신규 관 계열 [추가] — B06 길도 빈 월류 높이 채움 · 알림 글 「뒤에서 다시 나눔」 · 다시 나눔 실패 토스트 (298-1)
- B05 · B06 [추가] = 그 측점 횡단(카드 · 측점선) 바로 + 세부유역 뒤에서 다시 나눔(세션 초안 · 저장 안 함 · 최신 응답만 · 지도 칸 표시만) — 있던 길 그대로
- 까닭 1: B06 [추가]는 높이 기다림을 판에 안 알려 새 세월교 · 물넘이 월류 높이(126-1)가 빈칸 — 기다림 표를 프로젝트마다 한 벌(fordWaitsOf)로 판 · B06 이 같이 씀 · 채운 높이를 B06 목록 · 폼이 받음(adoptFordHeights)
- 까닭 2: B06 알림 글이 「세부 배수유역은 [저장]·[확정] 뒤 계산」 — 실제는 뒤에서 바로 다시 나눔 · 넣기 · 빼기 · 옮기기 글 고침
- 다시 나눔 실패 = 지도 칸 문구 + 토스트 한 줄(analyzeFailed)
- 시험: test_298_add_reanalyze 새로 · test_126 · test_291_1 · helper_165 · helper_231_1 맞춤

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

168 lines
7.3 KiB
Python

# -*- coding: utf-8 -*-
"""PLAN 298-1 — 신규 관 계열(배관 · 박스암거 · 세월교 · 물넘이포장) [추가] = 횡단도 생김 + 세부유역 뒤에서 다시 나눔.
B05 [추가] = 배수유역도 판 `addPipe` → 다시 나눔(이미 있던 길) · B06 [추가] = 관 초안 → 판 `reload`(같은 길).
고친 것 — B06 길도 빈 월류 높이를 채움(126-1 · 판과 같은 기다림 표 `fordWaitsOf`) · 채운 높이를 B06 목록 · 폼이
받음 · B06 알림 글이 「저장 뒤 계산」이라 틀리던 것 · 다시 나눔 실패 = 토스트 한 줄.
Node(`helper_298_add_reanalyze.cjs`) + 배선 대조.
"""
import json
import subprocess
from pathlib import Path
import pytest
HERE = Path(__file__).resolve().parent
ROOT = HERE.parents[1]
def _read(*parts: str) -> str:
return ROOT.joinpath(*parts).read_text(encoding="utf-8")
def _body(src: str, start: str, end: str) -> str:
at = src.index(start)
return src[at : src.index(end, at)]
@pytest.fixture(scope="module")
def out():
proc = subprocess.run(
["node", str(HERE / "helper_298_add_reanalyze.cjs")],
capture_output=True,
text=True,
encoding="utf-8",
timeout=60,
)
assert proc.returncode == 0, proc.stderr
return json.loads(proc.stdout)
PANEL = _read("B05_Profile", "B05_Profile_UI_Drainage_Panel.ts")
B06 = _read("B06_Section", "B06_Section_UI_Page_Structures_Panel.ts")
CROSS = _read("A00_Common", "design", "design_view_cross.ts")
# ── 126-1 채움 — B06 [추가]도 판과 같은 표 ──────────────────────────────────
def test_B06_추가가_적은_기다림을_판이_채움(out):
assert out["b06Wants"] is True and out["same"] is True
assert out["filled"] is True
assert out["heights"] == [out["need"], None] # 기다리던 측점만 · 옛 빈칸은 그대로
assert out["left"] == []
def test_다른_프로젝트_같은_측점은_안_채움(out):
assert out["otherFilled"] is False
assert out["otherHeights"] == [None, None]
def test_B06_추가_순간_기다림_적고_초안_씀():
body = _body(B06, " onPipeAdd: (chainageM, attributes) => {", " onPipeUpdate:")
order = [
body.index("pipeFacilities.push({"),
body.index("if (waitsFordHeight(attributes.facility, attributes.options))"),
body.index("fordWaitsOf(deps.projectId).add(at);"),
body.index("persistPipes();"),
body.index("deps.onStructuresChanged?.();"),
]
assert order == sorted(order)
def test_판은_같은_표로_채움():
assert "const fordWaits = () => fordWaitsOf(projectId);" in PANEL
assert "new Set<number>()" not in _body(PANEL, "let requestSeq = 0;", "let canvasWidth")
def test_채운_높이를_B06_목록_폼이_받음():
adopt = _body(B06, " adoptFordHeights: () => {", " releasePipe:")
assert "readPendingPipes(deps.projectId)" in adopt
assert "if (entry.options?.ford_height_m !== undefined) return false;" in adopt # 빈 칸만
assert "section.setPipeFacilities(pipeFacilities);" in adopt
assert "persistPipes" not in adopt # 받기만 — 초안을 다시 안 씀(되돌이 없음)
assert (
"pipesChanged: () => (structuresPanel.adoptFordHeights(), void refreshDetailForStructures()),"
in CROSS
)
view = _read("B06_Section", "B06_Section_UI_Section_View_Profile.ts")
sig = _body(view, "const signature = JSON.stringify(", "const changed")
assert "p.options?.ford_height_m ?? null" in sig # 늦게 온 유량으로 채운 높이도 알림
# ── 다시 나눔 = 요청 한 번 · 저장 안 함 · 최신 응답만 · 잠금 없음 ────────────
def test_B06_초안_쓰면_판이_한_번_다시_나눔():
written = _body(CROSS, " onPipesWritten: () => {", " detail: () => sectionDetail,")
assert "window.clearTimeout(pipeReloadTimer);" in written # 잇단 조작 = 한 번
assert "shared.profile?.side?.reload()" in written
persist = _body(B06, " function persistPipes(): void {", " /** 관 옮기기")
assert persist.index("writePendingPipes(") < persist.index("deps.onPipesWritten?.();")
def test_다시_나눔은_세션_초안으로_계산만_저장_안_함():
load = _body(PANEL, "const loadSaved = ", "const resetPipes")
assert "readPendingPipes(projectId as string)" in load
assert "computeDetailBasins(projectId as string, pending)" in load
analyze = _body(PANEL, "const analyze = ", "analyzeButton.addEventListener")
assert "computeDetailBasins(" in analyze
run = _body(PANEL, "async function run(", "/** 지금 관 목록")
assert "saveDetailPipePoints" not in run + load + analyze
# B05 [추가] = 판 addPipe → 배치 바뀜 → 초안 + 다시 나눔
commit = _body(PANEL, "const pipeEditor = createPipeEditor(", "(fromM, toM) =>")
assert commit.index("rememberPipes();") < commit.index("void analyze();")
def test_최신_응답만_잠금_없이_지도_칸_표시():
run = _body(PANEL, "async function run(", "/** 지금 관 목록")
assert "const seq = ++requestSeq;" in run
assert run.index("if (seq !== requestSeq) return;") < run.index("apply(response)")
# 지도 칸 안 작은 표시(문구 · 지도 가운데 서클)만 — 화면 전체 잠금 없음
assert 'status.textContent = L("B05_Drainage_Status_Analyzing");' in run
assert 'showProgress(null, L("B05_Drainage_Status_Analyzing"));' in run
assert "Overlay" not in run and "lock" not in run.lower()
chrome = _read("B05_Profile", "B05_Profile_UI_Drainage_Chrome.ts")
assert "viewport.append(" in chrome and "progress.root," in chrome
def test_실패하면_토스트_한_줄(out):
run = _body(PANEL, "async function run(", "/** 지금 관 목록")
assert (
'status.textContent = analyzeFailed(error, L("B05_Drainage_Status_AnalyzeFailed"));' in run
)
failed = out["failed"]
assert failed["status"] == "502 Bad Gateway"
assert failed["toasts"] == [
["세부유역 산정에 실패했습니다. 502 Bad Gateway", "error"],
["세부유역 산정에 실패했습니다.", "error"],
]
# ── 횡단도 생김 · 알림 글 ────────────────────────────────────────────────
def test_추가_뒤_새_측점_횡단_미리_뜸():
body = _body(B06, " onPipeAdd: (chainageM, attributes) => {", " onPipeUpdate:")
assert "deps.onStructuresChanged?.();" in body
assert "onStructuresChanged: () => void refreshDetailForStructures()," in CROSS
drafts = _read("B06_Section", "B06_Section_Section_Drafts.ts")
stations = _body(
drafts, "export async function withDraftStations(", "parked.set(detail, stale);"
)
assert "readPendingPipes(projectId)" in stations
assert "previewOf(projectId, routeId, missing)" in stations
def test_B06_알림_글_뒤에서_다시_나눔():
for name in ("PIPE_ADD_NOTICE", "PIPE_REMOVE_NOTICE", "PIPE_MOVE_NOTICE"):
line = _body(B06, f"const {name} =", ";\n")
assert "세부 배수유역은 뒤에서 다시 나눕니다." in line, name
assert "[저장]·[확정] 뒤에" not in line, name
def test_줄_수():
assert len(PANEL.splitlines()) <= 700
assert len(CROSS.splitlines()) <= 685