Files
Aislo/resources/tester/test_298_add_reanalyze.py
T
eomsangdonandClaude Opus 5.5 529baca3ff feat(구조물표): 323-6 바꿈 한 길 — 바뀜 신호 하나(구조물 초안도 설계 가게 · watchDrafts) · 종류 바꿈 표 규칙(swapsInPlace · E · F 점형 ↔ 구간형 = 지우고 같은 기준 측점에 새로) · 조용한 되돌림 없앰(알림) · 그리기가 조정값 안 만듦 · replayDraftPipeOptions 걷음
- 구조물 초안 쓰기 = useDesignStore().set("structures")(칸 그대로) → 관 초안과 같은 구독 · 횡단 보기 watchDrafts(구조물 = 늘 · 관 = 자리 · 종류 · 구간 · 잇기가 바뀔 때만 · 옵션만은 폼 길이 그 카드만 · 카드 줌 · 팬 지킴) · 옛 되부름 onStructuresChanged 걷음
- 종류 바꿈 = 표(catalog) swapsInPlace — 정본 다름 · 구조물 자리꼴 다름(E · F) = 지우고 같은 기준 측점에 새로(구간 = 기본 길이) · 같으면 종류 키만 · 막히면 옛 기록 · 옛 종류 폼 + 알림(전엔 [수정] 조기 반환 → 조용히 되돌림 · 드롭다운만 새 종류)
- syncApplied = 사용자가 조작한 세션이 있을 때만 깎음(카드 그리기가 자동 배치를 조정값으로 굳혀 [저장]으로 내보내던 것)
- replayDraftPipeOptions 걷음 — 그림 자료는 형상 얹기(초안 현황) · 다단 조정값은 조정 세션이 정본
- 시험: test_323_6_change(신호 · 거르기 · 표 규칙 · 그리기 · 걷음) · test_319 점형 ↔ 구간형 · 막힘 알림 · 되얹기 시험 = 형상 얹기로 · 옛 기대 글 고침

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ji4nuyLVHcxrXWHTdb5nRD
2026-10-14 18:21:41 +09:00

170 lines
7.5 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();"), # 초안 씀 = 카드 다시(바뀜 신호 하나 · 323-6)
]
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 "persistPipes();" in body # 초안 씀 → 구독(`watchDrafts`)이 카드 다시(323-6)
# 카드 다시 = 바뀜 신호 하나(323-6 · 관 · 구조물 초안 구독 · 옛 되부름 갈음)
assert (
"watchDrafts(projectId, () => host.visible() && 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