gendesign/backend/tests/workers/test_ird_harvest.py
lekss361 a15265c594
Some checks failed
Deploy / changes (push) Successful in 6s
Deploy / build-frontend (push) Has been skipped
Deploy / build-backend (push) Successful in 1m54s
Deploy / build-worker (push) Successful in 2m35s
Deploy / deploy (push) Has been cancelled
feat(sf): ИРД-harvest Celery task → ird_overlays (#1090)
ird_harvest.py — периодический harvest готовых НСПД ИРД-слоёв (ЗОУИТ/ПЗЗ/ОКН/ООПТ/лесничества/береговые) по кварталам ЕКБ → upsert в ird_overlays (м.132) по (source_layer_id, geom_data_id). SAVEPOINT per-feature, content_* не перетирается. Beat ird-harvest-weekly (Mon 05:00 МСК) + celery include. Keystone overlay-движка #1067 B5; прод-прогон деферится в B6 smoke.

Refs #1067.
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-06-06 18:52:04 +00:00

133 lines
4.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_ird_overlays (#1067 B5) — НСПД ИРД-harvest → ird_overlays.
Сеть/БД не дёргаются: мокаем NSPDClient + SessionLocal, проверяем grid-walk loop,
skip битых фич и upsert-параметры.
"""
from __future__ import annotations
from contextlib import contextmanager
from typing import Any
from app.workers.tasks import ird_harvest
class _Feat:
def __init__(self, geometry: dict[str, Any] | None, properties: dict[str, Any]) -> None:
self.geometry = geometry
self.properties = properties
class _SearchRes:
def __init__(self, feat: _Feat | None) -> None:
self.first = feat
class _FakeClient:
"""NSPDClient-двойник: квартал → полигон (3857), grid-walk → 1 фича на слой."""
def __init__(self, *a: Any, **kw: Any) -> None:
self.grid_calls: list[int] = []
def search_by_cad(self, cad: str, thematic_id: int = 1) -> _SearchRes:
ring = [[0, 0], [100, 0], [100, 100], [0, 100], [0, 0]]
geom = {"type": "Polygon", "coordinates": [ring]}
return _SearchRes(_Feat(geom, {"cad": cad}))
def get_features_in_bbox_grid(self, layer_id: int, bbox: Any, **kw: Any) -> list[_Feat]:
self.grid_calls.append(layer_id)
return [
_Feat(
{"type": "Polygon", "coordinates": [[[1, 1], [2, 1], [2, 2], [1, 1]]]},
{
"geom_data_id": 1000 + layer_id,
"reg_numb_border": "66:41-6.1",
"category": 36940,
"subcategory": 5,
},
)
]
class _FakeDB:
"""Session-двойник: пишет execute-вызовы, begin_nested как no-op CM."""
def __init__(self) -> None:
self.executed: list[tuple[Any, dict[str, Any] | None]] = []
self.committed = False
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.committed = True
def close(self) -> None:
self.closed = True
def _patch(monkeypatch: Any, db: _FakeDB) -> _FakeClient:
client = _FakeClient()
monkeypatch.setattr(ird_harvest, "NSPDClient", lambda *a, **kw: client)
monkeypatch.setattr(ird_harvest, "SessionLocal", lambda: db)
return client
def test_harvest_explicit_quarters_upserts_all_layers(monkeypatch: Any) -> None:
"""1 квартал × N ИРД-слоёв → upsert по фиче на слой; commit + close."""
db = _FakeDB()
client = _patch(monkeypatch, db)
n_layers = len(ird_harvest.IRD_HARVEST_LAYER_KEYS)
result = ird_harvest.harvest_ird_overlays(quarters=["66:41:0204016"])
assert result == {"quarters": 1, "features": n_layers}
assert len(client.grid_calls) == n_layers # grid-walk по каждому слою
assert len(db.executed) == n_layers # upsert по фиче на слой
assert db.committed is True
assert db.closed is True
# параметры upsert: geom как JSON-строка, layer_kind из ключей
_, params = db.executed[0]
assert params is not None
assert params["layer_kind"] in ird_harvest.IRD_HARVEST_LAYER_KEYS
assert isinstance(params["geojson"], str)
assert params["reg_numb_border"] == "66:41-6.1"
def test_harvest_skips_empty_quarter(monkeypatch: Any) -> None:
"""Квартал без геометрии в НСПД → 0 кварталов/фич, без падения."""
db = _FakeDB()
client = _patch(monkeypatch, db)
monkeypatch.setattr(client, "search_by_cad", lambda cad, thematic_id=1: _SearchRes(None))
result = ird_harvest.harvest_ird_overlays(quarters=["66:41:9999999"])
assert result == {"quarters": 0, "features": 0}
assert db.executed == []
assert db.committed is True
def test_upsert_feature_skips_missing_geom_data_id(monkeypatch: Any) -> None:
"""Фича без geom_data_id / geometry → не пишется (defensive)."""
db = _FakeDB()
no_id = _Feat({"type": "Polygon", "coordinates": []}, {"reg_numb_border": "x"})
no_geom = _Feat(None, {"geom_data_id": 5})
assert (
ird_harvest._upsert_feature(
db, layer_id=37577, layer_kind="zouit_okn", feature=no_id, fetched_at="t"
)
is False
)
assert (
ird_harvest._upsert_feature(
db, layer_id=37577, layer_kind="zouit_okn", feature=no_geom, fetched_at="t"
)
is False
)
assert db.executed == []