gendesign/backend/tests/services/test_ird_analyze.py
lekss361 7b40e7e480
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 1m33s
Deploy / build-worker (push) Successful in 2m49s
Deploy / deploy (push) Successful in 1m11s
feat(sf): wire ИРД-слой в analyze за флагом enable_ird_analyze (#1101)
D9b capstone — поле `ird` в ответе analyze за флагом enable_ird_analyze (default OFF): parcel_ird_overlaps (м.132, incl opportunity) + функц.зона/КРТ (геопортал WFS) + ПЗЗ-регламент (C8b cache-first). Логика в self-contained ird_analyze.py; parcels.py +13 строк flag-gated, defense-in-depth ×3 graceful. Schema additive (ird Optional). Замыкает ИРД-эпик #1067 (#1078/#1090/#1092/#1099/#1100/#1058/#1060). 4 теста.

Refs #1067.
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-06-06 19:50:57 +00:00

113 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Тесты build_ird_analyze_block (#1067 D9b analyze-wiring).
DB/сеть не дёргаются: мокаем parcel_ird_overlaps + get_or_fetch_zone_regulation (module-level)
и передаём fake EKBGeoportalClient. Проверяем сборку блока, split opportunity_*, graceful-деградацию
каждого источника и skip geoportal при отсутствии centroid.
"""
from __future__ import annotations
from typing import Any
from app.services.site_finder import ird_analyze
class _Feat:
def __init__(self, properties: dict[str, Any]) -> None:
self.properties = properties
class _FakeClient:
def __init__(self, *, fz: list[_Feat], krt: list[_Feat], boom: bool = False) -> None:
self._fz = fz
self._krt = krt
self._boom = boom
self.calls: list[tuple[str, float, float]] = []
def features_at_point(self, layer: str, lon: float, lat: float) -> list[_Feat]:
self.calls.append((layer, lon, lat))
if self._boom:
raise RuntimeError("geoportal down")
return self._fz if layer == "functional_zone" else self._krt
_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},
}
def _patch_overlay(monkeypatch: Any, overlay: dict[str, Any]) -> None:
monkeypatch.setattr(ird_analyze, "parcel_ird_overlaps", lambda db, wkt: overlay)
def test_full_block_assembly(monkeypatch: Any) -> None:
"""Все источники → overlaps + split opportunity + функц.зона + КРТ + регламент."""
_patch_overlay(monkeypatch, _OVERLAY)
monkeypatch.setattr(
ird_analyze,
"get_or_fetch_zone_regulation",
lambda db, lon, lat, client=None: {"zone_index": "Ц-1"},
)
client = _FakeClient(fz=[_Feat({"name": "Ж-зона"})], krt=[_Feat({"num_oks_krt": "КРТ-7"})])
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",
]
assert block["functional_zone"] == {"name": "Ж-зона"}
assert block["krt"] == [{"num_oks_krt": "КРТ-7"}]
assert block["zone_regulation"] == {"zone_index": "Ц-1"}
def test_geoportal_failure_is_graceful(monkeypatch: Any) -> None:
"""Сбой геопортала → функц.зона None, КРТ [], регламент None; overlaps уцелели."""
_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)
client = _FakeClient(fz=[], krt=[], 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
assert block["krt"] == []
assert block["zone_regulation"] is None
def test_no_centroid_skips_geoportal(monkeypatch: Any) -> None:
"""lon/lat None → только DB-overlay, geoportal не зовётся (client.calls пуст)."""
_patch_overlay(monkeypatch, _OVERLAY)
client = _FakeClient(fz=[_Feat({"x": 1})], krt=[])
block = ird_analyze.build_ird_analyze_block(object(), "POINT(60 56)", None, None, client=client)
assert client.calls == []
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": {}})
client = _FakeClient(fz=[], krt=[])
monkeypatch.setattr(ird_analyze, "get_or_fetch_zone_regulation", lambda *a, **kw: None)
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"] == {}