"""PLAN 317-1 — B05 [3D 업데이트]가 낡은 횡단을 먼저 다시 셈(요구 213). - 낡음 지문(`staleKey`) — 낡은 측점이 있으면 지문 · 모두 맞으면 "" - 그릴 때 잰 지문이 새것일 때만 다시 셈 예약(같은 낡음 되풀이 · 서버 폴백 반복 막음) - 그래프가 그릴 때 지문을 패널에 넘김 · 단추는 예상형상을 짓기 전에 낡은 설계부터 다시 셈 실제 TS 를 `helper_317_stale_preview.cjs` 가 Node 로 돌린다(다시 셈 창구는 흉내). """ from __future__ import annotations import json import os import subprocess import pytest HERE = os.path.dirname(__file__) ROOT = os.path.join(HERE, "..", "..") pytestmark = pytest.mark.skipif( not os.path.isdir(os.path.join(ROOT, "config", "node_modules")), reason="프론트엔드 의존성 미설치", ) def _read(*parts: str) -> str: with open(os.path.join(ROOT, *parts), encoding="utf-8") as handle: return handle.read() @pytest.fixture(scope="module") def result() -> dict: out = subprocess.run( ["node", os.path.join(HERE, "helper_317_stale_preview.cjs")], capture_output=True, text=True, encoding="utf-8", timeout=120, check=True, ) return json.loads(out.stdout) def test_stale_key_marks_only_stale_sections(result: dict) -> None: assert result["key_stale"].startswith("50:") assert result["key_fresh"] == "" def test_same_stale_key_schedules_once(result: dict) -> None: # a → 부름 · a(같음) → 안 부름 · "" → 안 부름(되돌림) · a(다시 낡음) → 부름 · b → 부름 assert result["calls"] == [1, 1, 1, 2, 3] def test_draw_and_button_wiring() -> None: panel = _read("A00_Common", "design", "design_profile.ts") assert "onStaleDesigns: crossPreview.scheduleIfStale" in panel render = _read("B05_Profile", "B05_Profile_UI_Profile_Render.ts") assert "ctx.onStaleDesigns?.(stale);" in render assert 'pendingRecalc: stale !== ""' in render corridor = _read("B05_Profile", "B05_Profile_UI_Corridor.ts") body = corridor.split("export async function refreshCorridor", 1)[1].split("\n}\n", 1)[0] assert "await refreshStaleDesigns({ projectId, routeId, detail })" in body assert body.index("refreshStaleDesigns") < body.index("ensureCorridor")