- H3 — 횡단 patch 바탕을 캐시 설계 사용자 칸(측점마다 통째)으로 · 세션 고침은 벽 · 단 · 쪽 열쇠마다 합침(mergeCrossPatches) - M2 — 벽 ↺ · 연동 풂 = 세션 null(안쪽 열쇠 지움) · 추가 벽 0 · 빈 전체 폭 0 을 세션에 남김 · 새 탭 − 가 저장분 단 수에서 출발 - H2 — 옮긴 관 · 구조물(정본 행 없음) = 지반 등 입력(CARRY_INPUT_KEYS)까지 실음 · 화면도 들고 감 · 서버 기본 설계가 그 입력으로 섬 - H5 — 노선 바꾼 [저장] 본문에 카드 선택 · 암선 · 암 경사 · 표준 횡단면 · 풍선 자리 - M1 — 집수정 내공 화면 범위 = 서버 1.0~2.0 - M5 — /save · /confirm 응답에 버린 patch 측점 · 기본 설계 실패 · 측점 맞춤 실패 · 화면 알림 - M7 — kindreset 을 측점 옮김 · 노선 저장에 따라 옮김 - 시험 test_445_save_patch · 기존 시험 꼴 맞춤(318 · 384 · 428 · 429 · fills_empty) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013QyNXY2TUKXCFMwAca2APG
126 lines
5.0 KiB
Python
126 lines
5.0 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, missed=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, missed=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
|