Files
Aislo/resources/tester/test_298_add_reanalyze.py
T
eomsangdonandClaude Opus 5.5 281c71b55b refactor(logic): 348-1 순수 계산 TS 49 파일 <페이지>/logic/ 로 옮김 — 이름 앞머리 그대로 · 동작 고침 없음
- 옮김: B04 2 · B05 23 · B06 11 · A00_Common/design 13 (9,299 줄) · 이미 logic/ 에 있던 16 파일 그대로
- 안 옮김: 묶음 입구(package.json build:*) · 파이썬 · 순수지만 계산 아닌 8 (보기 결선 · 콜백 · API 타입 · 갈아끼움 알림판)
- import 266 줄(142 파일) · 시험 · 주석 · 짝 표시 문자열 경로 고침 · B05 · B06 CLAUDE.md 짝 줄 logic/
- 시험 도우미 새 경로: 가짜 require 열쇠 · B05()/B06() 부름 · 형제 import 자리 · tsc 묶어 돌리는 시험 · 정규식 · rglob
- design_view_cross 줄 한도 시험 685 → 688 (긴 import 경로 줄바꿈 3줄)
- 확인: typecheck 0 · 서버 묶음 넷(corridor · server-calc · b07-extent · b07-note) 다시 지어 코드 같음(//#region 경로 주석만 다름) · 관련 시험 270 파일 — 남은 실패 19 는 옮기기 전과 같음

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014utrjfoW831Vc5wJUd13kL
2026-10-16 13:44:50 +09:00

175 lines
7.7 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")
# 세부유역 요청 감싸기 — 347-2 에 판에서 나눔(`createDetailRunner` · 판이 `label` · `apply` 넘김)
RUN = _read("B05_Profile", "B05_Profile_UI_Drainage_Run.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 loadSequence = 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 = RUN[RUN.index("async 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 = RUN[RUN.index("async run(") :]
assert "const seq = ++requestSeq;" in run
assert run.index("if (seq !== requestSeq) return false;") < run.index("deps.apply(response)")
# 지도 칸 안 작은 표시(문구 · 지도 가운데 서클)만 — 화면 전체 잠금 없음
assert 'status.textContent = label("B05_Drainage_Status_Analyzing");' in run
assert 'showProgress(null, label("B05_Drainage_Status_Analyzing"));' in run
wire = _body(PANEL, "const runner = createDetailRunner({", "});")
assert " apply," in wire and " label: L," in wire
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 = RUN[RUN.index("async run(") :]
assert (
'status.textContent = analyzeFailed(error, label("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()) <= 688