152 lines
4.9 KiB
Python
152 lines
4.9 KiB
Python
"""Тесты GET /parcels/by-bbox (SF-B1).
|
|
|
|
Проверяют:
|
|
1. Валидный bbox → 200 + корректная структура ответа.
|
|
2. Некорректный bbox (min_lat >= max_lat) → 400.
|
|
3. user_id передан → status заполняется из mock DB; без user_id → status=null.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
import pytest
|
|
from fastapi.testclient import TestClient
|
|
|
|
from app.main import app
|
|
|
|
|
|
def _make_db_row(
|
|
cad_num: str = "66:41:0204016:10",
|
|
centroid_lat: float = 56.83,
|
|
centroid_lon: float = 60.64,
|
|
area_m2: float = 1200.0,
|
|
land_category: str | None = "land_residential",
|
|
user_status: str | None = None,
|
|
) -> dict[str, Any]:
|
|
return {
|
|
"cad_num": cad_num,
|
|
"centroid_lat": centroid_lat,
|
|
"centroid_lon": centroid_lon,
|
|
"area_m2": area_m2,
|
|
"land_category": land_category,
|
|
"user_status": user_status,
|
|
}
|
|
|
|
|
|
def _build_mock_db(rows: list[dict[str, Any]]) -> MagicMock:
|
|
"""Сконструировать mock Session, возвращающий rows при execute().mappings().all()."""
|
|
mock_db = MagicMock()
|
|
mappings_mock = MagicMock()
|
|
mappings_mock.all.return_value = rows
|
|
execute_result = MagicMock()
|
|
execute_result.mappings.return_value = mappings_mock
|
|
mock_db.execute.return_value = execute_result
|
|
return mock_db
|
|
|
|
|
|
@pytest.fixture()
|
|
def client() -> TestClient:
|
|
return TestClient(app)
|
|
|
|
|
|
# ── Test 1: валидный bbox возвращает корректную структуру ──────────────────
|
|
|
|
|
|
def test_by_bbox_valid_returns_structure(client: TestClient) -> None:
|
|
rows = [_make_db_row()]
|
|
mock_db = _build_mock_db(rows)
|
|
|
|
with patch("app.api.v1.parcels.get_db", return_value=iter([mock_db])):
|
|
resp = client.get(
|
|
"/api/v1/parcels/by-bbox",
|
|
params={
|
|
"min_lat": 56.80,
|
|
"min_lon": 60.60,
|
|
"max_lat": 56.90,
|
|
"max_lon": 60.70,
|
|
"limit": 200,
|
|
},
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
body = resp.json()
|
|
assert "parcels" in body
|
|
assert "count" in body
|
|
assert "limit" in body
|
|
assert "bbox_area_km2" in body
|
|
assert body["count"] == 1
|
|
assert body["limit"] == 200
|
|
|
|
parcel = body["parcels"][0]
|
|
assert parcel["cad_num"] == "66:41:0204016:10"
|
|
assert parcel["centroid_lat"] == pytest.approx(56.83, abs=0.01)
|
|
assert parcel["centroid_lon"] == pytest.approx(60.64, abs=0.01)
|
|
assert parcel["area_m2"] == pytest.approx(1200.0)
|
|
assert parcel["land_category"] == "land_residential"
|
|
assert parcel["status"] is None # user_id не передан
|
|
assert parcel["last_analysis_date"] is None
|
|
|
|
|
|
# ── Test 2: некорректный bbox → 400 ───────────────────────────────────────
|
|
|
|
|
|
def test_by_bbox_invalid_bbox_returns_400(client: TestClient) -> None:
|
|
resp = client.get(
|
|
"/api/v1/parcels/by-bbox",
|
|
params={
|
|
"min_lat": 56.90, # min >= max → ошибка
|
|
"min_lon": 60.60,
|
|
"max_lat": 56.80,
|
|
"max_lon": 60.70,
|
|
},
|
|
)
|
|
assert resp.status_code == 400
|
|
assert "bbox" in resp.json()["detail"].lower()
|
|
|
|
|
|
# ── Test 3: user_id → status из БД; без user_id → status null ─────────────
|
|
|
|
|
|
def test_by_bbox_status_overlay_with_user_id(client: TestClient) -> None:
|
|
rows = [_make_db_row(user_status="in_work")]
|
|
mock_db = _build_mock_db(rows)
|
|
|
|
with patch("app.api.v1.parcels.get_db", return_value=iter([mock_db])):
|
|
resp = client.get(
|
|
"/api/v1/parcels/by-bbox",
|
|
params={
|
|
"min_lat": 56.80,
|
|
"min_lon": 60.60,
|
|
"max_lat": 56.90,
|
|
"max_lon": 60.70,
|
|
"user_id": "user-abc-123",
|
|
},
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
parcel = resp.json()["parcels"][0]
|
|
assert parcel["status"] == "in_work"
|
|
|
|
|
|
def test_by_bbox_no_user_id_status_is_null(client: TestClient) -> None:
|
|
rows = [_make_db_row(user_status="favorite")] # DB вернёт статус
|
|
mock_db = _build_mock_db(rows)
|
|
|
|
with patch("app.api.v1.parcels.get_db", return_value=iter([mock_db])):
|
|
resp = client.get(
|
|
"/api/v1/parcels/by-bbox",
|
|
params={
|
|
"min_lat": 56.80,
|
|
"min_lon": 60.60,
|
|
"max_lat": 56.90,
|
|
"max_lon": 60.70,
|
|
# user_id не передан
|
|
},
|
|
)
|
|
|
|
assert resp.status_code == 200
|
|
parcel = resp.json()["parcels"][0]
|
|
# Без user_id статус принудительно null (endpoint не раскрывает чужие статусы)
|
|
assert parcel["status"] is None
|