"""388-2 [저장] · [확정] 면적 · 관 길이 = 서버 셈만(요구 242 · 분석 1장 S3). - 브라우저가 면적 · 관 길이 9칸(`_AREA_KEYS`)을 보내도 정본에 안 적음 · 다른 칸은 그대로 - 서버 재계산 실패를 삼키지 않음 — 상세 못 받음 · Node 실패 = 던짐 → 응답 `recomputed: false` - 화면은 면적 · 관 길이를 안 보냄 · `recomputed: false` 면 경고 한 줄 """ from __future__ import annotations import asyncio from pathlib import Path from types import SimpleNamespace from uuid import uuid4 import pytest import B06_Section.B06_Section_Router_Confirm as confirm import B06_Section.B06_Section_Server_Calc_Prebuild as prebuild from B06_Section.B06_Section_Schema import SectionConfirmRequest, SectionConfirmResponse ROOT = Path(__file__).resolve().parents[2] def test_면적_보내도_정본에_안_적음(monkeypatch): written: list = [] async def merge(connection, *, route_id, entries, replace, project_id=None): written.append((replace, entries)) return 0 monkeypatch.setattr(confirm, "merge_cross_section_designs", merge) forged = {key: 999.0 for key in prebuild._AREA_KEYS} request = SectionConfirmRequest( cross_patches=[{"chainage_m": 20.0, "ground_type": "rock", **forged}] ) asyncio.run(confirm._apply_section_edits(None, 3, request, [])) patches = written[-1][1] assert patches == [(20.0, {"ground_type": "rock"})] def test_면적만_보낸_측점은_patch_없음(monkeypatch): written: list = [] async def merge(connection, *, route_id, entries, replace, project_id=None): written.append(entries) return 0 monkeypatch.setattr(confirm, "merge_cross_section_designs", merge) request = SectionConfirmRequest(cross_patches=[{"chainage_m": 40.0, "cut_area_m2": 12.5}]) asyncio.run(confirm._apply_section_edits(None, 3, request, [])) assert written[-1] == [] def test_재계산_실패는_False(monkeypatch): async def boom(project_id, route_id): raise RuntimeError("Node 실패") async def ok(project_id, route_id): return 1 monkeypatch.setattr(prebuild, "recompute_server_side", boom) assert asyncio.run(confirm._recompute_stored_designs(uuid4(), 3)) is False monkeypatch.setattr(prebuild, "recompute_server_side", ok) assert asyncio.run(confirm._recompute_stored_designs(uuid4(), 3)) is True def test_상세_못_받으면_던짐(monkeypatch): import B06_Section.B06_Section_Router as b06_router async def no_detail(project_id, route_id): return SimpleNamespace() # model_dump 없음 = JSONResponse 실패 async def run_with(fn, *args): return None async def nothing(project_id): return {} monkeypatch.setattr(b06_router, "get_section_detail", no_detail) monkeypatch.setattr(prebuild, "run_with_connection", run_with) monkeypatch.setattr(prebuild, "haul_inputs_for", nothing) with pytest.raises(RuntimeError): asyncio.run(prebuild.recompute_server_side(uuid4(), 3)) def test_Node_실패면_던짐(monkeypatch, tmp_path): import B06_Section.B06_Section_Router as b06_router import B06_Section.B06_Section_Router_Design as b06_design detail = {"cross_sections": [{"chainage_m": 0.0, "design": {}}], "longitudinal": {}} async def got(project_id, route_id): return SimpleNamespace(model_dump=lambda mode: detail) async def run_with(fn, *args): return "p" if fn is prebuild.get_project_storage_relative_path else {} async def nothing(project_id): return {} monkeypatch.setattr(b06_router, "get_section_detail", got) monkeypatch.setattr(prebuild, "run_with_connection", run_with) monkeypatch.setattr(prebuild, "haul_inputs_for", nothing) monkeypatch.setattr(prebuild, "resolve_stored_project_path", lambda path: str(tmp_path)) monkeypatch.setattr(b06_design, "enforce_design_lines", lambda *a: None) monkeypatch.setattr(prebuild, "_enforce_stored_designs", lambda *a: None) monkeypatch.setattr(prebuild, "quantity_settings", lambda root: {}) monkeypatch.setattr(prebuild, "mixed_conversion_factors", lambda settings: {}) monkeypatch.setattr(prebuild, "haul_equipment_limits", lambda settings: []) monkeypatch.setattr(prebuild, "_node_output", lambda node_input: None) with pytest.raises(RuntimeError): asyncio.run(prebuild.recompute_server_side(uuid4(), 3)) def test_응답에_recomputed(): assert SectionConfirmResponse(project_id="p", route_id=1).recomputed is True assert SectionConfirmResponse(project_id="p", route_id=1, recomputed=False).recomputed is False def test_화면은_면적_안_보내고_경고(): persist = (ROOT / "B06_Section/B06_Section_UI_Page_Persist.ts").read_text(encoding="utf-8") assert "STRUCTURE_ROW_KEYS" not in persist api = (ROOT / "B06_Section/B06_Section_Api_Fetch.ts").read_text(encoding="utf-8") assert api.count("return warnUnrecomputed(") == 2 assert "result.recomputed === false" in api