"""Тесты parcel_ppt_tep (#1136 follow-up #1133) — ТЭП ППТ/ПМТ overlap по участку. БД не дёргается: фейковая сессия с .execute().mappings().all(). Проверяем graceful на отсутствие участка/таблицы и проброс строк. """ from __future__ import annotations from typing import Any from sqlalchemy.exc import ProgrammingError from app.services.site_finder.ppt_tep_lookup import parcel_ppt_tep class _Result: def __init__(self, rows: list[dict[str, Any]]) -> None: self._rows = rows def mappings(self) -> _Result: return self def all(self) -> list[dict[str, Any]]: return self._rows class _DB: def __init__(self, rows: list[dict[str, Any]] | Exception) -> None: self._rows = rows def execute(self, sql: Any, params: dict[str, Any]) -> _Result: if isinstance(self._rows, Exception): raise self._rows return _Result(self._rows) _PPT_TEP_ROW: dict[str, Any] = { "project_type": "ppt", "doc_status_name": "действующий", "full_name": "ПАГЕ № 22823 от 12.10.2018", "doc_full_name": "ППТ ул. Челюскинцев", "source_key": 22823, "doc_ref": "ppt2018_22823", "zone_balance": [{"zone_name": "Зона Ж-1", "project_ha_num": 5.0}], "tep": [{"indicator": "Площадь территории", "target": "36,16"}], "phasing": [{"phase": "I этап", "zone": "Зона Ж-1", "area_ha_num": 5.0}], "source_pdf": "https://example.com/ppt22823.pdf", "fetched_at": "2026-06-07T11:55:00Z", } def test_returns_ppt_tep_overlap() -> None: """Один match по геометрии и doc_ref↔source_key → проброс строки.""" out = parcel_ppt_tep(_DB([_PPT_TEP_ROW]), "POINT(60 56)") # type: ignore[arg-type] assert len(out) == 1 row = out[0] assert row["doc_ref"] == "ppt2018_22823" assert row["source_key"] == 22823 assert row["zone_balance"] == [{"zone_name": "Зона Ж-1", "project_ha_num": 5.0}] assert row["tep"][0]["target"] == "36,16" assert row["phasing"][0]["zone"] == "Зона Ж-1" def test_empty_wkt_returns_empty() -> None: """parcel_wkt=None → [].""" assert parcel_ppt_tep(_DB([]), None) == [] # type: ignore[arg-type] def test_empty_string_wkt_returns_empty() -> None: """parcel_wkt='' → [].""" assert parcel_ppt_tep(_DB([]), "") == [] # type: ignore[arg-type] def test_no_overlap_returns_empty() -> None: """Запрос отработал, но пересечений нет → [].""" assert parcel_ppt_tep(_DB([]), "POINT(60 56)") == [] # type: ignore[arg-type] def test_missing_table_is_graceful() -> None: """ekb_ppt_tep ещё не задеплоена → ProgrammingError → [] (analyze не падает).""" db = _DB(ProgrammingError("stmt", {}, Exception("relation ekb_ppt_tep does not exist"))) assert parcel_ppt_tep(db, "POINT(60 56)") == [] # type: ignore[arg-type] def test_unexpected_exception_is_graceful() -> None: """Любая неожиданная ошибка БД → [] (analyze не падает).""" db = _DB(RuntimeError("connection refused")) assert parcel_ppt_tep(db, "POINT(60 56)") == [] # type: ignore[arg-type] def test_multiple_rows_preserved() -> None: """Если match'ит несколько ППТ — все возвращаются.""" row2: dict[str, Any] = { **_PPT_TEP_ROW, "doc_ref": "ppt2020_99999", "source_key": 99999, "full_name": "ПАГЕ № 99999 от 01.06.2020", } out = parcel_ppt_tep(_DB([_PPT_TEP_ROW, row2]), "POINT(60 56)") # type: ignore[arg-type] assert len(out) == 2 assert {r["doc_ref"] for r in out} == {"ppt2018_22823", "ppt2020_99999"}