Files
Aislo/resources/tester/test_b06_save_channels.py
T
eomsangdonandClaude Opus 5 0ef32b5279 chore(tester): 시험을 resources/tester/ 로 옮김 — 창끼리 건너가게
⚠ **뿌리** — `tmp/` 는 창 사이에 안 건너감(실측 확인: 상대 창이 놓은 `tmp/_sync_probe.txt`
가 시간을 두고 두 번 봐도 안 보임). 그래서 **정본(등록부 스키마)만 건너가고 그것을 읽는
시험은 안 건너가** 오늘 두 번, 같은 시험이 **연 창은 통과·받은 창은 실패**가 됐음.

- `tmp/tests/*` 를 `resources/tester/` 로 **복사**(127 파일). 내용은 **한 줄도 안 고침**
- `tmp/tests` 는 **남겨 둠** — 되돌릴 자리(사용자 지시)
- 실행: `./venv/Scripts/python.exe -m pytest resources/tester/ -q`
  옮기기 전과 **같은 수**: 617 통과 / 22 건너뜀 / 실패 0

⇒ 이제 시험·예외·까닭이 **정본과 함께** 움직임. 오늘 세운 「예외는 정본 스키마에」와 짝임.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-09 17:12:30 +09:00

77 lines
3.1 KiB
Python

"""[저장]이 여는 창구 넷이 **비어 있으면 안 나가는지**.
왜 (2026-09-07 조사) — [저장]은 창구 다섯을 순서대로 연다(관 옵션·상단측·관 목록·구조물·
`sections/save`). 넷은 초안이 비면 **요청 없이 즉시 돌아와야** 한다. 그래야 보통 저장이
1~2건으로 끝나고, 하나가 실패해도 나머지가 나간다(2026-08-29 사고 뒤 일부러 만든 구조).
이 성질이 깨지면 저장마다 빈 요청이 붙고, 「초안을 한 덩어리로 합치자」는 명분이 되살아난다.
합치지 않기로 한 근거(0-6)가 이 성질에 기대고 있으므로 그물을 남긴다.
코드를 읽어 지키는 시험이다 — 화면 왕복 없이 무너짐을 잡는 것이 목적이다.
"""
import re
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
#: (파일, 함수 이름, 비었을 때 돌아가는 표시)
CHANNELS = [
(
ROOT / "B05_Profile" / "B05_Profile_Api_Pipes_Draft.ts",
"flushPendingPipes",
"관 목록",
),
(
ROOT / "B05_Profile" / "B05_Profile_Api_Fetch.ts",
"flushUphillOverrides",
"상단측(측구 방향)",
),
(
ROOT / "B05_Profile" / "B05_Profile_Api_Structures.ts",
"flushPendingStructures",
"구조물",
),
(
ROOT / "B06_Section" / "B06_Section_Api_Culvert_Options.ts",
"flushCulvertOptions",
"관 옵션",
),
]
#: 비었을 때 빠져나가는 모양 — `return;` 만 있는 이른 반환.
_EARLY_RETURN = re.compile(r"\breturn\s*;")
def _body(path: Path, name: str) -> str:
"""함수 머리부터 다음 최상위 선언 전까지 — 이른 반환이 있는지만 보면 되므로 넉넉히 자른다."""
text = path.read_text(encoding="utf-8")
match = re.search(rf"(?:async\s+)?(?:export\s+)?(?:async\s+)?function\s+{name}\s*\(", text)
if match is None:
match = re.search(rf"\b{name}\s*\([^)]*\)\s*(?::[^{{]+)?\{{", text)
assert match is not None, f"{path.name} 에서 {name} 을 못 찾음"
return text[match.start() : match.start() + 900]
def test_창구_넷은_초안이_비면_요청을_안_낸다():
problems = []
for path, name, label in CHANNELS:
body = _body(path, name)
if not _EARLY_RETURN.search(body):
problems.append(f"{label}({path.name}:{name}) — 빈 초안에서 빠져나가는 자리가 없음")
assert not problems, "\n".join(problems)
def test_저장이_창구를_다섯_다_거친다():
"""순서·개수가 바뀌면 위 성질의 뜻도 바뀐다 — 자리를 못박는다."""
persist = (ROOT / "B06_Section" / "B06_Section_UI_Page_Persist.ts").read_text(encoding="utf-8")
for token in (
"flushCulvertOptions()",
"flushUphillOverrides(",
"flushPendingPipes(",
"flushPendingStructures(",
):
assert token in persist, f"{token} 가 저장 앞단에서 사라짐"
# 하나가 실패해도 나머지가 나가야 한다 — 관·구조물은 `catch` 로 감싸 둔다.
assert persist.count(".catch(") >= 3, "실패 격리(catch)가 줄었음"