All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-frontend (push) Has been skipped
Deploy / deploy-caddy (push) Has been skipped
Deploy / build-backend (push) Successful in 2m15s
Deploy / build-worker (push) Successful in 3m14s
Deploy / deploy (push) Successful in 1m28s
Deploy / deploy-status (push) Successful in 1s
Deploy / perimeter-smoke (push) Successful in 10s
130 lines
5 KiB
Python
130 lines
5 KiB
Python
"""Тесты reservation_lookup (#1091, #1062) — read-side lookup из land_reservation."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from contextlib import contextmanager
|
||
from datetime import date
|
||
from typing import Any
|
||
|
||
from sqlalchemy.exc import OperationalError, ProgrammingError
|
||
|
||
from app.services.site_finder.reservation_lookup import parcel_reservations
|
||
|
||
# ── Моки БД (аналог test_ird_overlay_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:
|
||
@contextmanager
|
||
def begin_nested(self): # type: ignore[no-untyped-def]
|
||
"""#2464: lookup оборачивает свой db.execute в SAVEPOINT (сессия общая с
|
||
analyze_parcel). Здесь достаточно пустого контекст-менеджера: эти тесты про
|
||
логику самого lookup'а. Что SAVEPOINT РЕАЛЬНО откатывает aborted-транзакцию,
|
||
проверяет tests/services/site_finder/test_2464_ird_lookups_savepoint.py на
|
||
двойнике с настоящей семантикой Postgres."""
|
||
yield
|
||
|
||
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)
|
||
|
||
|
||
# ── Фикстура строк ────────────────────────────────────────────────────────────
|
||
|
||
_ROWS = [
|
||
{
|
||
"reservation_kind": "изъятие",
|
||
"basis_act": "Постановление Правительства СО № 509-ПП",
|
||
"act_number": "509-ПП",
|
||
"act_date": date(2024, 3, 12),
|
||
"purpose": "реконструкция улицы Татищева",
|
||
"doc_url": "https://pravo.gov66.ru/documents/view/509-pp",
|
||
},
|
||
{
|
||
"reservation_kind": "резервирование",
|
||
"basis_act": "Постановление Правительства СО № 210-ПП",
|
||
"act_number": "210-ПП",
|
||
"act_date": date(2024, 6, 5),
|
||
"purpose": "строительство дороги",
|
||
"doc_url": "https://pravo.gov66.ru/documents/view/210-pp",
|
||
},
|
||
]
|
||
|
||
|
||
# ── Тесты ────────────────────────────────────────────────────────────────────
|
||
|
||
|
||
def test_maps_rows_correctly() -> None:
|
||
"""Строки из БД корректно маппируются в список словарей."""
|
||
result = parcel_reservations(_FakeDB(_ROWS), "66:41:0101001:123")
|
||
assert len(result) == 2
|
||
assert result[0]["reservation_kind"] == "изъятие"
|
||
assert result[0]["act_number"] == "509-ПП"
|
||
assert result[0]["act_date"] == date(2024, 3, 12)
|
||
assert result[1]["reservation_kind"] == "резервирование"
|
||
|
||
|
||
def test_expected_keys_present() -> None:
|
||
"""Каждый элемент содержит все ожидаемые ключи."""
|
||
result = parcel_reservations(_FakeDB(_ROWS), "66:41:0101001:123")
|
||
expected_keys = {
|
||
"reservation_kind",
|
||
"basis_act",
|
||
"act_number",
|
||
"act_date",
|
||
"purpose",
|
||
"doc_url",
|
||
}
|
||
for item in result:
|
||
assert set(item.keys()) == expected_keys
|
||
|
||
|
||
def test_empty_when_no_rows() -> None:
|
||
"""Нет записей в БД → пустой список."""
|
||
result = parcel_reservations(_FakeDB([]), "66:41:0101001:999")
|
||
assert result == []
|
||
|
||
|
||
def test_empty_when_cad_num_none() -> None:
|
||
"""cad_num=None → пустой список, БД не вызывается."""
|
||
result = parcel_reservations(_FakeDB(_ROWS), None)
|
||
assert result == []
|
||
|
||
|
||
def test_empty_when_cad_num_empty_string() -> None:
|
||
"""cad_num='' → пустой список (falsy guard)."""
|
||
result = parcel_reservations(_FakeDB(_ROWS), "")
|
||
assert result == []
|
||
|
||
|
||
def test_graceful_on_operational_error() -> None:
|
||
"""OperationalError (таблица не задеплоена) → [] без падения."""
|
||
db = _FakeDB(raise_exc=OperationalError("stmt", {}, Exception("no such table")))
|
||
result = parcel_reservations(db, "66:41:0101001:123")
|
||
assert result == []
|
||
|
||
|
||
def test_graceful_on_programming_error() -> None:
|
||
"""ProgrammingError → [] без падения."""
|
||
db = _FakeDB(raise_exc=ProgrammingError("stmt", {}, Exception("column not found")))
|
||
result = parcel_reservations(db, "66:41:0101001:123")
|
||
assert result == []
|