"""[저장]·[확정]은 **면적을 모으기 전에** 전 측점을 다시 계산한다 (2026-09-09 실측). ⚠ 잡은 자리 — 표준단면 「암 측구 상폭」을 0.69 → 0.9 로 고쳐 저장했더니 정본 한 줄에 **새 측구 기하(상폭 0.9 · 측구 0.18㎡)** 와 **옛 절토 면적(0.69 기준 3.33㎡)** 이 섞여 남았다. 브라우저는 만진 측점만 다시 계산하는데 저장은 **전 측점 면적**을 실어 보내므로, 안 만진 측점이 옛 값 그대로 정본이 된다. (같은 입력을 주면 파이썬·TS 는 같은 값을 낸다 — 거울 문제가 아니라 **입력이 갈린 것**이다.) ⇒ [저장]·[확정]이 `collectSectionEdits` **앞에서** `ctx.reconcileDesigns()` 를 부른다. 순서가 뒤집히면 옛 면적이 그대로 실린다. (계획서 24-2a · 24-3b — 한 벌(`design_shared`)이 저장 한 줄기 `saveDesign` 을 부르고, 횡단 보기 몫 `crossSaveParts` 의 `prepare` 가 재계산 · `cross` 가 면적 수집이며, `saveDesign` 이 `prepare` 를 먼저 부른다.) """ from __future__ import annotations from pathlib import Path ROOT = Path(__file__).resolve().parents[2] PERSIST = ROOT / "B06_Section" / "B06_Section_UI_Page_Persist.ts" PAGE = ROOT / "A00_Common" / "design" / "design_view_cross.ts" SHARED = ROOT / "A00_Common" / "design" / "design_shared.ts" SAVE = ROOT / "A00_Common" / "design" / "design_save.ts" def _body(source: str, marker: str) -> str: start = source.index(marker) return source[start : start + 1200] def test_저장이_면적을_모으기_전에_다시_계산한다() -> None: text = PERSIST.read_text(encoding="utf-8") body = _body(text, "export function crossSaveParts") assert "prepare: () => ctx.reconcileDesigns()" in body, "횡단 보기 몫이 재계산을 안 넘긴다" assert "collectSectionEdits(ctx)" in body[body.index("cross:") :], "면적 수집이 횡단 차례 밖" shared = _body(SHARED.read_text(encoding="utf-8"), "export async function saveSharedDesign") assert "prepare: crossSave?.prepare" in shared, "한 벌이 재계산을 저장 한 줄기에 안 넘긴다" save = SAVE.read_text(encoding="utf-8") run = _body(save, "export async function saveDesign") assert run.index("ctx.prepare?.()") < run.index("ctx.cross(confirm)"), ( "저장 한 줄기가 재계산보다 면적 수집을 먼저 부른다 — 옛 면적이 실린다" ) def test_페이지가_재계산_통로를_이어_준다() -> None: text = PAGE.read_text(encoding="utf-8") assert "reconcileDesigns: () => reconcileStaleDesigns({ force: true })" in text, ( "페이지가 통로를 안 이어 주면 저장이 옛 값 그대로 나간다" )