"""PLAN 505-2 5d — 수치지형도 sheet 고르기 = DB 모델 있을 때만 · 없으면 [확인] 409 사유 한 줄. ./venv/Scripts/python.exe -m pytest resources/tester/test_505_2_terrain_sheet.py -q 고르기 목록(`/route/terrain-sources`)은 npz 파일 + DB 완료 모델을 함께 봄 — 모델 없는 sheet 는 숨김 (화면은 고르기가 숨으면 분석 지형을 안 실음). 바꿈(`switch_design_terrain`)이 모델을 못 찾으면 `_recompute` 가 `{error}` → 노선 되돌리고 409(전엔 LookupError 가 저장 통째 되돌림). """ from __future__ import annotations import asyncio import sys from contextlib import asynccontextmanager from pathlib import Path from uuid import uuid4 import pytest ROOT = Path(__file__).resolve().parents[2] if str(ROOT) not in sys.path: sys.path.insert(0, str(ROOT)) import B04_PreProcess.B04_PreProcess_Service as service # noqa: E402 import B05_Profile.B05_Profile_Router_Replan as replan # noqa: E402 import B05_Profile.B05_Profile_Router_Terrain as terrain # noqa: E402 from common_util.common_util_surface_confirmation import SHEET_ANALYSIS_FILTER # noqa: E402 LAS = {"source_filter": "classification", "method": "dtm", "smooth": True} class _Pool: @asynccontextmanager async def acquire(self): yield _Connection() class _Connection: async def begin(self): pass async def rollback(self): _Connection.rolled = True async def commit(self): raise AssertionError("모델 없으면 확정하지 않음") def _async(value): async def inner(*args, **kwargs): return value return inner def _model(source_filter: str) -> dict: return { "id": 7, "status": "COMPLETE", "model_type": "DTM", "generation_params": {"source_filter": source_filter}, } @pytest.fixture def sources(tmp_path: Path, monkeypatch: pytest.MonkeyPatch): models = tmp_path / terrain._MODELS_SUBDIR models.mkdir(parents=True) (models / f"dtm_{SHEET_ANALYSIS_FILTER}.npz").write_bytes(b"") # sheet 파일은 있음 monkeypatch.setattr(terrain, "get_db_pool", lambda: _Pool()) monkeypatch.setattr(terrain, "get_project_storage_relative_path", _async("p")) monkeypatch.setattr(terrain, "get_surface_confirmation_params", _async(dict(LAS))) monkeypatch.setattr(terrain, "_confirmed_model", _async(None)) monkeypatch.setattr(terrain, "resolve_stored_project_path", lambda stored: tmp_path) monkeypatch.setattr(terrain, "_design_grid", lambda *args: None) def run(models_in_db: list[dict]) -> list[str]: monkeypatch.setattr(service, "list_surface_models", _async(models_in_db)) return asyncio.run(terrain.read_terrain_sources(uuid4()))["sources"] return run def test_모델_없는_sheet_고르기_숨김(sources) -> None: assert sources([_model("classification")]) == ["las"] def test_모델_있는_sheet_고르기_보임(sources) -> None: assert sources([_model(SHEET_ANALYSIS_FILTER)]) == ["las", "sheet"] def test_모델_없으면_재계산_409_사유(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None: import common_util.common_util_surface_confirmation as surface _Connection.rolled = False monkeypatch.setattr(replan, "get_db_pool", lambda: _Pool()) monkeypatch.setattr(service, "get_surface_confirmation_params", _async(dict(LAS))) monkeypatch.setattr(service, "list_surface_models", _async([_model("classification")])) monkeypatch.setattr(surface, "get_surface_confirmation_params", _async(dict(LAS))) got = asyncio.run(replan._recompute(uuid4(), tmp_path, "p", terrain="sheet")) assert got == {"error": replan.TERRAIN_MODEL_MISSING} and _Connection.rolled