- 화면 [저장] · [확정]이 면적 · 관 길이 9칸을 안 보냄 - 서버가 받아도 _AREA_KEYS 걷음(옛 화면 · 위조 값 정본에 안 남음) - 서버 재계산 실패(상세 못 받음 · Node 실패)를 삼키지 않음 → 응답 recomputed:false - 화면 경고 한 줄(저장은 됨 · 옛 서버 값 남음 · 다시 [저장]) - 새 test_384_s_area_keys · 옛 시험 셋 새 동작에 맞춤 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VmC34fsxxbweEG3RUjs3Mn
126 lines
4.9 KiB
Python
126 lines
4.9 KiB
Python
"""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
|