"""사용자 조작값 목록이 서버·브라우저에서 갈라지지 않는지 지킨다. 왜 (2026-09-07 발견) — 같은 목록이 세 곳에 흩어져 있었고 서로 달랐다. 브라우저는 `extra_spans`(다단 구간값)를 살렸는데 서버 두 경로는 안 살려, 그 측점이 다시 계산되면 사용자가 넣은 단별 구간값이 조용히 사라졌다. 새 조작값을 만들 때 한쪽만 고치는 것을 막는다. """ import re from pathlib import Path from B06_Section.B06_Section_Router_Design import USER_TOUCHED_KEYS ROOT = Path(__file__).resolve().parents[2] TS_PATH = ROOT / "B06_Section" / "B06_Section_Cross_Refresh.ts" # 브라우저 목록에만 있고 서버 목록에는 없는 둘 — 부르는 쪽이 따로 붙인다. _CALLER_ADDED = {"status", "pavement_suggested"} def _browser_keys() -> set[str]: """브라우저 쪽 사용자 값 목록 — 2026-09-07 부터 `USER_TOUCHED_KEYS` 한 벌이 정본이고 `PRESERVED_KEYS` 는 거기에 상태 둘을 더해 만든다.""" text = TS_PATH.read_text(encoding="utf-8") block = re.search(r"export const USER_TOUCHED_KEYS = \[(.*?)\] as const;", text, re.S) assert block, "USER_TOUCHED_KEYS 를 못 찾음" return set(re.findall(r'"([a-z_]+)"', block.group(1))) def test_다단_구간값이_서버_목록에_있다(): assert "extra_spans" in USER_TOUCHED_KEYS def test_서버와_브라우저_목록이_같다(): assert _browser_keys() - _CALLER_ADDED == set(USER_TOUCHED_KEYS) def test_브라우저_목록도_한_벌이다(): """보존 목록·저장 payload 가 같은 한 벌을 쓰는지 — 손나열이 되살아나면 깨진다.""" text = TS_PATH.read_text(encoding="utf-8") assert "...USER_TOUCHED_KEYS" in text, "PRESERVED_KEYS 가 목록을 다시 적고 있음" store = (ROOT / "B06_Section" / "B06_Section_Section_Store.ts").read_text(encoding="utf-8") assert "USER_TOUCHED_KEYS" in store, "저장 payload 가 목록을 안 씀" assert store.count('"extra_spans"') == 0, "저장 payload 에 손나열이 남아 있음" def test_확정_payload_도_손나열이_없다(): """서버 확정 경로 — 스키마를 통째로 덤프하므로 새 필드가 저절로 실린다.""" confirm = (ROOT / "B06_Section" / "B06_Section_Router_Confirm.py").read_text(encoding="utf-8") assert "patch_item.model_dump()" in confirm assert confirm.count('patch["extra_spans"]') == 0, "확정 경로에 손나열이 남아 있음" def test_서버_목록이_한_벌이다(): """라우터가 목록을 다시 적지 않고 그 한 벌을 쓴다.""" router = (ROOT / "B06_Section" / "B06_Section_Router.py").read_text(encoding="utf-8") assert "USER_TOUCHED_KEYS" in router assert router.count('"revet_follow_grade"') == 0, "라우터에 목록 사본이 남아 있음"