167 lines
6 KiB
Python
167 lines
6 KiB
Python
"""Тесты harvest_riasurt_sverdl_for_mo / harvest_all_riasurt_sverdl (#108).
|
||
|
||
Сеть/БД не дёргаются: мокаем NSPDClient + SessionLocal. Проверяем delete-per-MO,
|
||
insert-параметры (layer_topic-классификация, mo_name), skip битых фич, прогон по 5 МО.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from contextlib import contextmanager
|
||
from typing import Any
|
||
|
||
from app.workers.tasks import riasurt_sverdl_harvest
|
||
|
||
|
||
class _Feat:
|
||
def __init__(self, geometry: dict[str, Any] | None, properties: dict[str, Any]) -> None:
|
||
self.geometry = geometry
|
||
self.properties = properties
|
||
|
||
|
||
class _FakeClient:
|
||
"""NSPDClient-двойник: bbox → 1 фича на каждый запрошенный слой."""
|
||
|
||
def __init__(self, *a: Any, **kw: Any) -> None:
|
||
self.bbox_calls: list[Any] = []
|
||
|
||
def get_riasurt_sverdl_in_bbox(
|
||
self, bbox: Any, layers: list[int] | None = None, **kw: Any
|
||
) -> dict[int, list[_Feat]]:
|
||
self.bbox_calls.append(bbox)
|
||
layer_ids = layers or list(riasurt_sverdl_harvest.RIASURT_SVERDL_LAYERS.keys())
|
||
return {
|
||
lid: [
|
||
_Feat(
|
||
{"type": "Polygon", "coordinates": [[[1, 1], [2, 1], [2, 2], [1, 1]]]},
|
||
{"label": f"zone-{lid}", "reg_numb_border": f"66:35-{lid}"},
|
||
)
|
||
]
|
||
for lid in layer_ids
|
||
}
|
||
|
||
|
||
class _FakeDB:
|
||
def __init__(self) -> None:
|
||
self.executed: list[tuple[Any, dict[str, Any] | None]] = []
|
||
self.commit_count = 0
|
||
self.rollback_count = 0
|
||
self.closed = False
|
||
|
||
@contextmanager
|
||
def begin_nested(self) -> Any:
|
||
yield
|
||
|
||
def execute(self, sql: Any, params: dict[str, Any] | None = None) -> Any:
|
||
self.executed.append((sql, params))
|
||
return None
|
||
|
||
def commit(self) -> None:
|
||
self.commit_count += 1
|
||
|
||
def rollback(self) -> None:
|
||
self.rollback_count += 1
|
||
|
||
def close(self) -> None:
|
||
self.closed = True
|
||
|
||
|
||
def _patch(monkeypatch: Any, db: _FakeDB) -> _FakeClient:
|
||
client = _FakeClient()
|
||
monkeypatch.setattr(riasurt_sverdl_harvest, "NSPDClient", lambda *a, **kw: client)
|
||
monkeypatch.setattr(riasurt_sverdl_harvest, "SessionLocal", lambda: db)
|
||
return client
|
||
|
||
|
||
def test_harvest_for_mo_deletes_then_inserts(monkeypatch: Any) -> None:
|
||
"""1 МО × 2 слоя → delete-MO + 2 insert; commit + close."""
|
||
db = _FakeDB()
|
||
_patch(monkeypatch, db)
|
||
|
||
res = riasurt_sverdl_harvest.harvest_riasurt_sverdl_for_mo(
|
||
"Берёзовский",
|
||
(6770000.0, 7710000.0, 6782000.0, 7722000.0),
|
||
[845274, 844478],
|
||
)
|
||
|
||
assert res == {"features": 2, "layers": 2}
|
||
assert db.commit_count == 1
|
||
assert db.closed is True
|
||
# 1 delete + 2 insert
|
||
assert len(db.executed) == 3
|
||
delete_params = db.executed[0][1]
|
||
assert delete_params == {"mo_name": "Берёзовский"}
|
||
|
||
|
||
def test_harvest_for_mo_inserts_correct_topic(monkeypatch: Any) -> None:
|
||
"""layer_topic денормализуется из riasurt_layer_topic; mo_name проставлен."""
|
||
db = _FakeDB()
|
||
_patch(monkeypatch, db)
|
||
|
||
riasurt_sverdl_harvest.harvest_riasurt_sverdl_for_mo(
|
||
"Сысерть", (0.0, 0.0, 100.0, 100.0), [845274, 845425]
|
||
)
|
||
|
||
insert_params = [p for _, p in db.executed if p and "layer_topic" in p]
|
||
topics = {p["source_layer_id"]: p["layer_topic"] for p in insert_params}
|
||
assert topics[845274] == "territorial_zone"
|
||
assert topics[845425] == "flood_zone"
|
||
assert all(p["mo_name"] == "Сысерть" for p in insert_params)
|
||
assert all(p["obshnz"] == f"66:35-{p['source_layer_id']}" for p in insert_params)
|
||
|
||
|
||
def test_harvest_for_mo_skips_geomless_feature(monkeypatch: Any) -> None:
|
||
"""Фича без geometry не пишется (skip), счётчик не растёт."""
|
||
db = _FakeDB()
|
||
client = _FakeClient()
|
||
|
||
def only_empty(bbox: Any, layers: list[int] | None = None, **kw: Any) -> dict[int, list[_Feat]]:
|
||
return {845274: [_Feat(None, {"label": "broken"})]}
|
||
|
||
client.get_riasurt_sverdl_in_bbox = only_empty # type: ignore[method-assign]
|
||
monkeypatch.setattr(riasurt_sverdl_harvest, "NSPDClient", lambda *a, **kw: client)
|
||
monkeypatch.setattr(riasurt_sverdl_harvest, "SessionLocal", lambda: db)
|
||
|
||
res = riasurt_sverdl_harvest.harvest_riasurt_sverdl_for_mo("Арамиль", (0.0, 0.0, 10.0, 10.0))
|
||
assert res["features"] == 0
|
||
# только delete-MO, никаких insert
|
||
assert len(db.executed) == 1
|
||
|
||
|
||
def test_harvest_all_iterates_5_mo(monkeypatch: Any) -> None:
|
||
"""harvest_all прогоняет все 5 МО агломерации (при включённом флаге)."""
|
||
db = _FakeDB()
|
||
_patch(monkeypatch, db)
|
||
monkeypatch.setattr(
|
||
riasurt_sverdl_harvest.settings, "enable_riasurt_harvest", True, raising=False
|
||
)
|
||
|
||
res = riasurt_sverdl_harvest.harvest_all_riasurt_sverdl([845274])
|
||
assert res["mo"] == 5
|
||
# 5 МО × 1 фича каждая
|
||
assert res["features"] == 5
|
||
|
||
|
||
def test_harvest_all_disabled_by_default(monkeypatch: Any) -> None:
|
||
"""Гейт #108: при выключенном флаге harvest_all возвращает early без WMS-вызовов."""
|
||
db = _FakeDB()
|
||
client = _patch(monkeypatch, db)
|
||
monkeypatch.setattr(
|
||
riasurt_sverdl_harvest.settings, "enable_riasurt_harvest", False, raising=False
|
||
)
|
||
|
||
res = riasurt_sverdl_harvest.harvest_all_riasurt_sverdl([845274])
|
||
assert res == {"mo": 0, "features": 0}
|
||
# ни одного WMS-запроса, ни одной записи в БД
|
||
assert client.bbox_calls == []
|
||
assert db.executed == []
|
||
|
||
|
||
def test_mo_bboxes_has_5_agglomeration_municipalities() -> None:
|
||
"""MO_BBOXES содержит ровно 5 МО окраин агломерации."""
|
||
assert set(riasurt_sverdl_harvest.MO_BBOXES) == {
|
||
"Берёзовский",
|
||
"Верхняя Пышма",
|
||
"Среднеуральск",
|
||
"Арамиль",
|
||
"Сысерть",
|
||
}
|