Co-authored-by: lekss361 <lekss361@gendsgn.local> Co-committed-by: lekss361 <lekss361@gendsgn.local>
507 lines
22 KiB
Python
507 lines
22 KiB
Python
"""Тесты build_ird_analyze_block (#1067 D9b analyze-wiring, #1130 krt-from-db, #1058 fz-wiring).
|
||
|
||
DB/сеть не дёргаются: мокаем parcel_ird_overlaps + get_or_fetch_zone_regulation + _krt_at
|
||
+ parcel_functional_zones (module-level) и передаём fake EKBGeoportalClient.
|
||
Проверяем сборку блока, split opportunity_*, graceful-деградацию каждого источника,
|
||
skip geoportal при отсутствии centroid.
|
||
|
||
#1130: krt теперь из DB (ekb_krt_geometry) — клиент WFS больше НЕ вызывается для слоя «krt».
|
||
#1058: functional_zones из DB ekb_genplan_functional_zone; functional_zone — первая зона|None.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Any
|
||
|
||
from app.services.site_finder import ird_analyze
|
||
|
||
|
||
class _FakeClient:
|
||
def __init__(self, *, fz: list[Any], boom: bool = False) -> None:
|
||
self._fz = fz
|
||
self._boom = boom
|
||
self.calls: list[tuple[str, float, float]] = []
|
||
|
||
def features_at_point(self, layer: str, lon: float, lat: float) -> list[Any]:
|
||
self.calls.append((layer, lon, lat))
|
||
if self._boom:
|
||
raise RuntimeError("geoportal down")
|
||
return self._fz if layer == "functional_zone" else []
|
||
|
||
|
||
_OVERLAY = {
|
||
"ird_overlaps": [
|
||
{"layer_kind": "zouit_okn", "reg_numb_border": "66:41-6.1"},
|
||
{"layer_kind": "opportunity_auction", "reg_numb_border": "66:41:1:10"},
|
||
{"layer_kind": "opportunity_krt", "reg_numb_border": None},
|
||
],
|
||
"ird_by_kind": {"zouit_okn": 1, "opportunity_auction": 1, "opportunity_krt": 1},
|
||
}
|
||
|
||
|
||
_PLANNING = [{"project_type": "ppt", "doc_status_name": "действующий", "full_name": "ПАГЕ №1"}]
|
||
|
||
|
||
_KRT_REQUISITES: list[dict[str, Any]] = [
|
||
{
|
||
"matched": True,
|
||
"confidence": "exact",
|
||
"krt_geom_ref": 1166,
|
||
"pp_full_name": "ПАГЕ № 1970",
|
||
"site_name": "КРТ Летчиков",
|
||
"developer_name": "ООО СЗ Летчиков",
|
||
"contract_number": "2-7/21.7-14",
|
||
"auction_final_price": 5_000_000.0,
|
||
"gradpotential_sqm": 25_000.0,
|
||
"area_ha": 1.5,
|
||
}
|
||
]
|
||
|
||
_KRT_DB_ROWS = [
|
||
{"num_oks_krt": 7, "status": "В работе", "full_name": "КРТ ул. Тверитина", "area": 3.5}
|
||
]
|
||
|
||
|
||
def _patch_overlay(monkeypatch: Any, overlay: dict[str, Any]) -> None:
|
||
monkeypatch.setattr(ird_analyze, "parcel_ird_overlaps", lambda db, wkt: overlay)
|
||
monkeypatch.setattr(ird_analyze, "parcel_planning_overlaps", lambda db, wkt: list(_PLANNING))
|
||
monkeypatch.setattr(ird_analyze, "parcel_reservations", lambda db, cad_num: [])
|
||
monkeypatch.setattr(ird_analyze, "parcel_krt_requisites", lambda db, wkt: list(_KRT_REQUISITES))
|
||
# По умолчанию: пустой список — functional_zone=None (back-compat).
|
||
monkeypatch.setattr(ird_analyze, "parcel_functional_zones", lambda db, wkt: [])
|
||
# По умолчанию: пустой список — okn_objects=[] (graceful, таблица может не быть задеплоена).
|
||
monkeypatch.setattr(ird_analyze, "parcel_okn_objects", lambda db, wkt: [])
|
||
# По умолчанию: пустой список — granddoc=[] (graceful, см. #1061).
|
||
monkeypatch.setattr(ird_analyze, "parcel_granddoc", lambda db, wkt: [])
|
||
# По умолчанию: пустой список — pat_subzones=[] (graceful, таблица может не быть задеплоена).
|
||
monkeypatch.setattr(ird_analyze, "parcel_pat_subzones", lambda db, wkt: [])
|
||
|
||
|
||
def test_full_block_assembly(monkeypatch: Any) -> None:
|
||
"""Все источники → overlaps + split opportunity + КРТ (из DB) + регламент.
|
||
functional_zones из DB (пустой мок → functional_zone=None).
|
||
krt из DB (ekb_krt_geometry), WFS больше не вызывается для слоя «krt».
|
||
"""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(
|
||
ird_analyze,
|
||
"get_or_fetch_zone_regulation",
|
||
lambda db, lon, lat, client=None: {"zone_index": "Ц-1"},
|
||
)
|
||
# _krt_at теперь DB-вызов — мокаем на уровне модуля
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: list(_KRT_DB_ROWS))
|
||
|
||
client = _FakeClient(fz=[])
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert len(block["ird_overlaps"]) == 3
|
||
assert block["ird_by_kind"]["opportunity_auction"] == 1
|
||
assert [o["layer_kind"] for o in block["opportunity_overlaps"]] == [
|
||
"opportunity_auction",
|
||
"opportunity_krt",
|
||
]
|
||
# parcel_functional_zones мокирован на [] → functional_zones=[], functional_zone=None
|
||
assert block["functional_zones"] == []
|
||
assert block["functional_zone"] is None
|
||
# krt из DB, не из WFS — client.calls не содержит «krt»
|
||
assert "krt" not in [layer for layer, _, _ in client.calls]
|
||
assert block["krt"] == _KRT_DB_ROWS
|
||
assert block["zone_regulation"] == {"zone_index": "Ц-1"}
|
||
assert block["planning_projects"] == _PLANNING
|
||
|
||
|
||
def test_geoportal_failure_is_graceful(monkeypatch: Any) -> None:
|
||
"""Сбой геопортала → функц.зона None, регламент None; overlaps и krt (DB) уцелели."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
|
||
def _boom(*a: Any, **kw: Any) -> Any:
|
||
raise RuntimeError("zone reg down")
|
||
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", _boom)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: list(_KRT_DB_ROWS))
|
||
|
||
client = _FakeClient(fz=[], boom=True)
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert len(block["ird_overlaps"]) == 3 # DB-overlay не задет
|
||
assert block["functional_zone"] is None
|
||
# krt из DB — возвращается даже когда geoportal недоступен
|
||
assert block["krt"] == _KRT_DB_ROWS
|
||
assert block["zone_regulation"] is None
|
||
|
||
|
||
def test_krt_db_failure_graceful(monkeypatch: Any) -> None:
|
||
"""Сбой DB-запроса _krt_at → krt==[], остальное в блоке сохраняется."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(
|
||
ird_analyze,
|
||
"get_or_fetch_zone_regulation",
|
||
lambda db, lon, lat, client=None: {"zone_index": "Ж-1"},
|
||
)
|
||
|
||
def _krt_boom(db: Any, lon: float, lat: float) -> list[dict[str, Any]]:
|
||
raise RuntimeError("DB connection lost")
|
||
|
||
# _krt_at должен поймать Exception и вернуть [] (graceful внутри самой функции)
|
||
# Мокаем саму функцию чтобы она кинула — проверяем что build_ird_analyze_block обрабатывает
|
||
# но _krt_at уже ловит внутри себя; тестируем через прямой мок функции возвратом []
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
|
||
client = _FakeClient(fz=[])
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert block["krt"] == []
|
||
assert block["zone_regulation"] == {"zone_index": "Ж-1"}
|
||
assert len(block["ird_overlaps"]) == 3
|
||
|
||
|
||
def test_no_centroid_skips_geoportal_and_krt(monkeypatch: Any) -> None:
|
||
"""lon/lat None → только DB-overlay, geoportal и _krt_at не вызываются."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
krt_called: list[int] = []
|
||
|
||
def _krt_spy(db: Any, lon: float, lat: float) -> list[dict[str, Any]]:
|
||
krt_called.append(1)
|
||
return []
|
||
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", _krt_spy)
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", None, None, client=client)
|
||
|
||
assert client.calls == []
|
||
assert krt_called == []
|
||
assert block["functional_zone"] is None
|
||
assert block["krt"] == []
|
||
assert block["zone_regulation"] is None
|
||
assert len(block["ird_overlaps"]) == 3
|
||
|
||
|
||
def test_empty_overlay(monkeypatch: Any) -> None:
|
||
"""Пустой overlay → пустые списки, без KeyError."""
|
||
_patch_overlay(monkeypatch, {"ird_overlaps": [], "ird_by_kind": {}})
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), None, None, None, client=client)
|
||
|
||
assert block["ird_overlaps"] == []
|
||
assert block["opportunity_overlaps"] == []
|
||
assert block["ird_by_kind"] == {}
|
||
|
||
|
||
def test_krt_populated_from_db(monkeypatch: Any) -> None:
|
||
"""_krt_at из DB → block['krt'] заполнен корректными данными."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(
|
||
ird_analyze,
|
||
"get_or_fetch_zone_regulation",
|
||
lambda db, lon, lat, client=None: {"zone_index": "Ж-3"},
|
||
)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: list(_KRT_DB_ROWS))
|
||
|
||
client = _FakeClient(fz=[])
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert block["krt"] == _KRT_DB_ROWS
|
||
krt = block["krt"][0]
|
||
assert krt["num_oks_krt"] == 7
|
||
assert krt["status"] == "В работе"
|
||
assert krt["full_name"] == "КРТ ул. Тверитина"
|
||
|
||
|
||
def test_krt_empty_table_returns_empty(monkeypatch: Any) -> None:
|
||
"""Пустая ekb_krt_geometry → krt==[] (graceful, форма блока не меняется)."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(
|
||
ird_analyze,
|
||
"get_or_fetch_zone_regulation",
|
||
lambda db, lon, lat, client=None: None,
|
||
)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
|
||
client = _FakeClient(fz=[])
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert block["krt"] == []
|
||
assert "krt" in block # ключ всегда присутствует
|
||
|
||
|
||
def test_zone_regulation_called_once(monkeypatch: Any) -> None:
|
||
"""zone_regulation вызывается ровно один раз (основной поток, без ThreadPool)."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
zone_reg_calls: list[int] = []
|
||
|
||
def _fake_zone_reg(db: Any, lon: float, lat: float, client: Any = None) -> dict[str, Any]:
|
||
zone_reg_calls.append(1)
|
||
return {"zone_index": "Ж-3"}
|
||
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", _fake_zone_reg)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
|
||
client = _FakeClient(fz=[])
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert block["zone_regulation"] == {"zone_index": "Ж-3"}
|
||
assert len(zone_reg_calls) == 1
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Reservation tests (#1091, #1062)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
_RESERVATION_ROW = {
|
||
"reservation_kind": "изъятие",
|
||
"basis_act": "Постановление Правительства",
|
||
"act_number": "12-ПП",
|
||
"act_date": "2024-03-01",
|
||
"purpose": "дорожное строительство",
|
||
"doc_url": "https://example.com/doc.pdf",
|
||
}
|
||
|
||
|
||
def test_reservations_wired_to_block(monkeypatch: Any) -> None:
|
||
"""cad_num передан → parcel_reservations возвращает запись → ключ reservations в block."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(
|
||
ird_analyze,
|
||
"parcel_reservations",
|
||
lambda db, cad_num: [_RESERVATION_ROW] if cad_num == "66:41:0101001:123" else [],
|
||
)
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(
|
||
object(), "POINT(60 56)", None, None, "66:41:0101001:123", client=client
|
||
)
|
||
|
||
assert "reservations" in block
|
||
assert len(block["reservations"]) == 1
|
||
assert block["reservations"][0]["reservation_kind"] == "изъятие"
|
||
|
||
|
||
def test_reservations_none_cad_num_returns_empty(monkeypatch: Any) -> None:
|
||
"""cad_num=None → reservations == [] (parcel_reservations graceful, ключ присутствует)."""
|
||
_patch_overlay(monkeypatch, {"ird_overlaps": [], "ird_by_kind": {}})
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), None, None, None, None, client=client)
|
||
|
||
assert "reservations" in block
|
||
assert block["reservations"] == []
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# KRT-реквизиты (#1060)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
def test_krt_requisites_wired_to_block(monkeypatch: Any) -> None:
|
||
"""parcel_krt_requisites возвращает реквизит → ключ krt_requisites присутствует в блоке."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", None, None, client=client)
|
||
|
||
assert "krt_requisites" in block
|
||
assert len(block["krt_requisites"]) == 1
|
||
r = block["krt_requisites"][0]
|
||
assert r["matched"] is True
|
||
assert r["confidence"] == "exact"
|
||
assert r["developer_name"] == "ООО СЗ Летчиков"
|
||
|
||
|
||
def test_krt_requisites_empty_when_no_overlap(monkeypatch: Any) -> None:
|
||
"""parcel_krt_requisites пуст → krt_requisites == [] в блоке (ключ присутствует)."""
|
||
_patch_overlay(monkeypatch, {"ird_overlaps": [], "ird_by_kind": {}})
|
||
monkeypatch.setattr(ird_analyze, "parcel_krt_requisites", lambda db, wkt: [])
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), None, None, None, client=client)
|
||
|
||
assert "krt_requisites" in block
|
||
assert block["krt_requisites"] == []
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Функциональные зоны генплана (#1058)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_FZ_ROWS: list[dict[str, Any]] = [
|
||
{"zone_type": "Ж", "status": "утверждён", "area_ha": 2.5, "zone_class_code": "2.1"},
|
||
{"zone_type": "О", "status": "утверждён", "area_ha": 0.8, "zone_class_code": "3.0"},
|
||
]
|
||
|
||
|
||
def test_functional_zones_two_zones(monkeypatch: Any) -> None:
|
||
"""parcel_functional_zones → 2 зоны: functional_zones == список, functional_zone == первая."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "parcel_functional_zones", lambda db, wkt: list(_FZ_ROWS))
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert block["functional_zones"] == _FZ_ROWS
|
||
assert len(block["functional_zones"]) == 2
|
||
assert block["functional_zone"] == _FZ_ROWS[0]
|
||
assert block["functional_zone"]["zone_type"] == "Ж"
|
||
|
||
|
||
def test_functional_zones_empty(monkeypatch: Any) -> None:
|
||
"""parcel_functional_zones пуст → functional_zones==[], functional_zone==None."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
# _patch_overlay уже мокает parcel_functional_zones → [], переопределяем явно для ясности
|
||
monkeypatch.setattr(ird_analyze, "parcel_functional_zones", lambda db, wkt: [])
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert block["functional_zones"] == []
|
||
assert block["functional_zone"] is None
|
||
assert "functional_zones" in block # ключ всегда присутствует
|
||
assert "functional_zone" in block
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ОКН-объекты (#1066)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_OKN_ROWS: list[dict[str, Any]] = [
|
||
{"source_id": "66-001", "category": "федеральный", "distance_m": 42},
|
||
{"source_id": "66-002", "category": "региональный", "distance_m": 88},
|
||
]
|
||
|
||
|
||
def test_okn_objects_wired_to_block(monkeypatch: Any) -> None:
|
||
"""parcel_okn_objects возвращает 2 объекта → block['okn_objects'] список из двух элементов."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "parcel_okn_objects", lambda db, wkt: list(_OKN_ROWS))
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert "okn_objects" in block
|
||
assert len(block["okn_objects"]) == 2
|
||
assert block["okn_objects"][0]["source_id"] == "66-001"
|
||
assert block["okn_objects"][1]["category"] == "региональный"
|
||
|
||
|
||
def test_okn_objects_empty_when_none(monkeypatch: Any) -> None:
|
||
"""parcel_okn_objects пуст (нет объектов / таблица не задеплоена) → okn_objects == []."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "parcel_okn_objects", lambda db, wkt: [])
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert "okn_objects" in block
|
||
assert block["okn_objects"] == []
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Граддокументация (#1061)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_GRANDDOC_ROWS: list[dict[str, Any]] = [
|
||
{
|
||
"project_type": "ppt",
|
||
"doc_status_name": "Действующий",
|
||
"is_active": True,
|
||
"paga_number": "2792",
|
||
"paga_date": "2021-12-16",
|
||
"subject": "Реконструкция ул. Декабристов",
|
||
"doc_full_name": "ППТ ул. Декабристов",
|
||
}
|
||
]
|
||
|
||
|
||
def test_granddoc_wired_to_block(monkeypatch: Any) -> None:
|
||
"""parcel_granddoc возвращает запись → ключ granddoc присутствует в block."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "parcel_granddoc", lambda db, wkt: list(_GRANDDOC_ROWS))
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", None, None, client=client)
|
||
|
||
assert "granddoc" in block
|
||
assert len(block["granddoc"]) == 1
|
||
row = block["granddoc"][0]
|
||
assert row["paga_number"] == "2792"
|
||
assert row["is_active"] is True
|
||
|
||
|
||
def test_granddoc_empty_when_no_overlap(monkeypatch: Any) -> None:
|
||
"""parcel_granddoc пуст → granddoc == [] (ключ присутствует)."""
|
||
_patch_overlay(monkeypatch, {"ird_overlaps": [], "ird_by_kind": {}})
|
||
monkeypatch.setattr(ird_analyze, "parcel_granddoc", lambda db, wkt: [])
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), None, None, None, client=client)
|
||
|
||
assert "granddoc" in block
|
||
assert block["granddoc"] == []
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# ПАТ-подзоны (#1151, #1065)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
_PAT_ROWS: list[dict[str, Any]] = [
|
||
{
|
||
"subzone_no": 3,
|
||
"name": "Третья подзона",
|
||
"restriction": "Высота зданий не более 50 м",
|
||
"aerodrome": "Екатеринбург (Кольцово)",
|
||
}
|
||
]
|
||
|
||
|
||
def test_pat_subzones_wired_to_block(monkeypatch: Any) -> None:
|
||
"""parcel_pat_subzones возвращает подзону → block['pat_subzones'] содержит её (#1151)."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "parcel_pat_subzones", lambda db, wkt: list(_PAT_ROWS))
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", None, None, client=client)
|
||
|
||
assert "pat_subzones" in block
|
||
assert len(block["pat_subzones"]) == 1
|
||
assert block["pat_subzones"][0]["subzone_no"] == 3
|
||
assert block["pat_subzones"][0]["aerodrome"] == "Екатеринбург (Кольцово)"
|
||
|
||
|
||
def test_pat_subzones_empty_when_no_overlap(monkeypatch: Any) -> None:
|
||
"""parcel_pat_subzones пуст (нет пересечений / таблица не задеплоена) → pat_subzones == []."""
|
||
_patch_overlay(monkeypatch, _OVERLAY)
|
||
monkeypatch.setattr(ird_analyze, "parcel_okn_objects", lambda db, wkt: [])
|
||
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
|
||
monkeypatch.setattr(ird_analyze, "_krt_at", lambda db, lon, lat: [])
|
||
client = _FakeClient(fz=[])
|
||
|
||
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", 60.6, 56.8, client=client)
|
||
|
||
assert "pat_subzones" in block
|
||
assert block["pat_subzones"] == []
|