"""사용자 값이 재계산에서 사라지지 않는지 — **키 이름으로** 지킨다. 왜 (2026-09-07 전수 조사) — 횡단 `design` 딕셔너리는 사용자 값과 계산 값이 한 칸에 섞여 있고, 재계산(`enforce_pavement_ranges`·`enforce_ford_surface_drops`)은 결과를 통째로 갈아 끼운다. 그래서 사용자 값이 살아남는 길은 **둘뿐**이다: ① 재계산에 **인자로 되먹여** 결과에 그대로 나오는 것 (`ground_type`·`section_mode`·`ditch_side`·`ditch_type`·`paved`· `two_stage_slope`·`rock_boundary_offset_m`) ② 계산이 만들지 않아 **목록으로 베껴 넣는** 것 (`USER_TOUCHED_KEYS`) 어느 쪽에도 안 걸린 사용자 값은 **저장할 때 조용히 사라진다**. 실제로 그렇게 사라졌던 것이 `extra_spans` 였다(`b6941bd2`). 이 시험은 그 구멍을 다시 못 나게 못박는다 — 새 사용자 값을 `CrossSectionPatch` 에 더하면서 ①·② 어느 쪽에도 안 넣으면 여기서 깨진다. """ import inspect import sys from pathlib import Path PROJECT_ROOT = Path(__file__).resolve().parents[2] if str(PROJECT_ROOT) not in sys.path: sys.path.insert(0, str(PROJECT_ROOT)) from B06_Section.B06_Section_Engine_Design import compute_cross_design # noqa: E402 from B06_Section.B06_Section_Router_Design import USER_TOUCHED_KEYS # noqa: E402 from B06_Section.B06_Section_Schema import CrossSectionPatch # noqa: E402 #: 측점을 가리키는 열쇠 — 값이 아니다. _KEY_FIELDS = {"chainage_m"} #: 사용자 값이 아니라 **계산 값**이라 재계산이 다시 내는 것이 옳은 필드. #: 브라우저가 조작 중 값을 함께 보내지만 정본은 서버가 다시 낸다(CLAUDE.md 5장). _COMPUTED_FIELDS = { "cut_area_m2", "fill_area_m2", "cut_soil_area_m2", "cut_rock_area_m2", # 배수관 연장(m, 2026-09-08) — 사용자 값이 아니라 **기하가 낸 값**이다. 면적 넷과 **같은 # 다리**(`STRUCTURE_ROW_KEYS` → `B06_Section_Server_Calc_Node` → # `recompute_server_side`)로 서버가 다시 내므로 재계산에서 안 사라진다. "pipe_length_m", } def _recompute_inputs() -> set[str]: """재계산에 되먹이는 인자 이름 — ① 갈래.""" return set(inspect.signature(compute_cross_design).parameters) def test_사용자_값은_되먹임이거나_보존목록이다(): fed = _recompute_inputs() kept = set(USER_TOUCHED_KEYS) missing = [ name for name in CrossSectionPatch.model_fields if name not in _KEY_FIELDS and name not in _COMPUTED_FIELDS and name not in fed and name not in kept ] assert not missing, ( "재계산에서 사라질 사용자 값: " + ", ".join(missing) + " — 재계산 인자로 넣거나 USER_TOUCHED_KEYS 에 더할 것" ) def test_보존목록에_계산값이_섞이지_않았다(): """반대 방향 — 계산 값을 베껴 두면 옛 값이 새 계산을 덮는다.""" overlap = sorted(set(USER_TOUCHED_KEYS) & _COMPUTED_FIELDS) assert not overlap, f"계산 값이 보존 목록에 있음: {overlap}" def test_되먹임_갈래가_실제로_여덟_가지(): """①이 줄면(인자에서 빠지면) 그 값은 조용히 기본값으로 되돌아간다 — 수를 못박는다.""" fed = _recompute_inputs() expected = { "ground_type", "section_mode", "ditch_side", "ditch_type", "paved", "two_stage_slope", "ditch_enabled", "rock_boundary_offset_m", } assert expected <= fed, f"재계산 인자에서 빠진 것: {sorted(expected - fed)}"