"""Тесты granddoc_lookup (#1061) — read-side lookup граддокументации из planning_projects.""" from __future__ import annotations from datetime import date from typing import Any from sqlalchemy.exc import OperationalError, ProgrammingError from app.services.site_finder.granddoc_lookup import parcel_granddoc # ── Моки БД (аналог test_reservation_lookup.py) ──────────────────────────── 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 _FakeDB: def __init__( self, rows: list[dict[str, Any]] | None = None, raise_exc: Exception | None = None, ) -> None: self._rows = rows or [] self._raise = raise_exc def execute(self, sql: Any, params: dict[str, Any] | None = None) -> _Result: if self._raise is not None: raise self._raise return _Result(self._rows) # ── Фикстура строк ──────────────────────────────────────────────────────────── _ROW_PAGA = { "project_type": "ppt", "doc_status_name": "Действующий", "full_name": "ПАГЕ № 2792 от 16.12.2021\nРеконструкция ул. Декабристов", "doc_full_name": "Проект планировки территории ул. Декабристов", } _ROW_NO_PAGA = { "project_type": "pmt", "doc_status_name": "Недействующий", "full_name": "Проект межевания без реквизитов ПАГЕ", "doc_full_name": "ПМТ квартал 24", } _ROW_INACTIVE = { "project_type": "ppt", "doc_status_name": "Недействующий", "full_name": "ПАГЕ № 111 от 01.03.2015\nПрежний проект", "doc_full_name": "Старый ППТ", } _ROW_ACTIVE_NEWER = { "project_type": "ppt", "doc_status_name": "Действующий", "full_name": "ПАГЕ № 3500 от 20.06.2023\nНовый квартал", "doc_full_name": "ППТ 2023", } # ── Тесты: парсинг ПАГЕ ─────────────────────────────────────────────────────── def test_paga_number_and_date_parsed() -> None: """full_name с «ПАГЕ № 2792 от 16.12.2021» → paga_number='2792', paga_date=date(2021,12,16).""" result = parcel_granddoc(_FakeDB([_ROW_PAGA]), "POINT(60 56)") assert len(result) == 1 row = result[0] assert row["paga_number"] == "2792" assert row["paga_date"] == date(2021, 12, 16) def test_paga_subject_extracted() -> None: """Предмет (текст после реквизитов ПАГЕ) извлекается как subject.""" result = parcel_granddoc(_FakeDB([_ROW_PAGA]), "POINT(60 56)") assert result[0]["subject"] == "Реконструкция ул. Декабристов" def test_is_active_true_for_dejstvuyushiy() -> None: """doc_status_name='Действующий' → is_active=True.""" result = parcel_granddoc(_FakeDB([_ROW_PAGA]), "POINT(60 56)") assert result[0]["is_active"] is True def test_is_active_false_for_nedejstvuyushiy() -> None: """doc_status_name='Недействующий' → is_active=False.""" result = parcel_granddoc(_FakeDB([_ROW_INACTIVE]), "POINT(60 56)") assert result[0]["is_active"] is False def test_no_paga_in_full_name_returns_none_graceful() -> None: """full_name без «ПАГЕ № …» → paga_number=None, paga_date=None, subject=None (graceful).""" result = parcel_granddoc(_FakeDB([_ROW_NO_PAGA]), "POINT(60 56)") assert len(result) == 1 row = result[0] assert row["paga_number"] is None assert row["paga_date"] is None assert row["subject"] is None def test_all_expected_keys_present() -> None: """Каждый элемент содержит все ожидаемые ключи.""" result = parcel_granddoc(_FakeDB([_ROW_PAGA, _ROW_NO_PAGA]), "POINT(60 56)") expected = { "project_type", "doc_status_name", "is_active", "paga_number", "paga_date", "subject", "doc_full_name", } for item in result: assert set(item.keys()) == expected # ── Тесты: сортировка ──────────────────────────────────────────────────────── def test_active_first_sorting() -> None: """Действующие документы идут первыми независимо от порядка в БД.""" rows = [_ROW_INACTIVE, _ROW_PAGA] # inactive первый в БД result = parcel_granddoc(_FakeDB(rows), "POINT(60 56)") assert result[0]["is_active"] is True assert result[1]["is_active"] is False def test_active_sorted_by_date_desc() -> None: """Среди действующих: более новая дата идёт раньше (desc).""" rows = [_ROW_PAGA, _ROW_ACTIVE_NEWER] # 2021 первый в БД, 2023 второй result = parcel_granddoc(_FakeDB(rows), "POINT(60 56)") # Оба active; 2023 > 2021 → 2023 должен быть первым active_results = [r for r in result if r["is_active"]] assert len(active_results) == 2 assert active_results[0]["paga_date"] == date(2023, 6, 20) assert active_results[1]["paga_date"] == date(2021, 12, 16) # ── Тесты: graceful ────────────────────────────────────────────────────────── def test_empty_when_parcel_wkt_none() -> None: """parcel_wkt=None → пустой список, БД не вызывается.""" result = parcel_granddoc(_FakeDB([_ROW_PAGA]), None) assert result == [] def test_empty_when_parcel_wkt_empty_string() -> None: """parcel_wkt='' → пустой список (falsy guard).""" result = parcel_granddoc(_FakeDB([_ROW_PAGA]), "") assert result == [] def test_empty_when_no_rows() -> None: """Нет пересечений в БД → пустой список.""" result = parcel_granddoc(_FakeDB([]), "POINT(60 56)") assert result == [] def test_graceful_on_operational_error() -> None: """OperationalError (таблица не задеплоена) → [] без падения.""" db = _FakeDB(raise_exc=OperationalError("stmt", {}, Exception("no such table"))) result = parcel_granddoc(db, "POINT(60 56)") assert result == [] def test_graceful_on_programming_error() -> None: """ProgrammingError → [] без падения.""" db = _FakeDB(raise_exc=ProgrammingError("stmt", {}, Exception("column not found"))) result = parcel_granddoc(db, "POINT(60 56)") assert result == []