- 추천 저장본: 프로젝트 접근(화면 기본 갈래)은 같은 판 · 같은 입력이면 그 파일 소스 지문으로 키를 다시 내 찾음(read_stored_any) · 지문 견줌 · 다시 셈은 전처리 체인만 - 초기 종단 저장본: 접근은 코드 지문 빼고 입력만 견줌 · 파일에 코드 지문 칸 · 지문 칸 없는 옛 파일은 그대로 읽음 - 분석 지형 고르기 목록: 설계 지형 아닌 쪽은 DB 완료 모델도 있어야 둠(없으면 숨김) - 노선 [저장] 설계 지형 바꿈에 모델 없음(LookupError) = 되돌림 + 409 사유 한 줄 - 시험 test_309_3 둘 더함 · test_505_2_terrain_sheet 새로 · test_133_1 DB 모델 맞춤 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
102 lines
3.7 KiB
Python
102 lines
3.7 KiB
Python
"""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
|