gendesign/backend/tests/workers/test_riasurt_sverdl_harvest.py
bot-backend 0a72ef9491
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 2m33s
CI / backend-tests (pull_request) Successful in 10m10s
merge main + renumber 163→167 + guard riasurt harvest (#108 review)
2026-06-17 22:52:15 +03:00

167 lines
6 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.

"""Тесты 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) == {
"Берёзовский",
"Верхняя Пышма",
"Среднеуральск",
"Арамиль",
"Сысерть",
}