123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
"""Тесты контракта suggest→ФИАС (feat/tradein-fias-tier-matching).
|
||
|
||
Покрывают:
|
||
1. geocoder._dadata_suggest — fias_id прокидывается ТОЛЬКО у house-level
|
||
кандидата (DaData kind='house' ⇔ fias_level 8/9); у street/city/plot None.
|
||
2. SuggestItem — сериализует fias_id (house-тир) и None у не-DaData тиров.
|
||
3. TradeInEstimateInput — принимает target_fias_id и дефолтит None при отсутствии.
|
||
"""
|
||
|
||
import os
|
||
|
||
# Settings требует DATABASE_URL на импорте — ставим dummy DSN до app-импортов.
|
||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
||
|
||
from unittest.mock import AsyncMock, patch
|
||
|
||
from app.api.v1.geocode import SuggestItem
|
||
from app.schemas.trade_in import TradeInEstimateInput
|
||
from app.services.dadata import DadataSuggestion
|
||
from app.services.geocoder import GeocodeSuggestion, _dadata_suggest
|
||
|
||
|
||
def _sugg(kind: str, fias_id: str | None) -> DadataSuggestion:
|
||
return DadataSuggestion(
|
||
value=f"тест {kind}",
|
||
unrestricted_value=f"г Екатеринбург, тест {kind}",
|
||
lat=56.84,
|
||
lon=60.60,
|
||
fias_id=fias_id,
|
||
house="1" if kind == "house" else None,
|
||
street="Тестовая",
|
||
city="Екатеринбург",
|
||
kind=kind,
|
||
)
|
||
|
||
|
||
# ── 1. _dadata_suggest: fias_id только у house-тира ──────────────────────────
|
||
async def test_dadata_suggest_fias_only_for_house_kind():
|
||
raw = [
|
||
_sugg("house", "0a1b2c3d-0000-4000-8000-00000000aaaa"),
|
||
_sugg("street", "0a1b2c3d-0000-4000-8000-00000000bbbb"),
|
||
_sugg("city", "0a1b2c3d-0000-4000-8000-00000000cccc"),
|
||
_sugg("plot", "0a1b2c3d-0000-4000-8000-00000000dddd"),
|
||
]
|
||
with patch(
|
||
"app.services.geocoder.dadata.suggest_addresses",
|
||
new=AsyncMock(return_value=raw),
|
||
):
|
||
out = await _dadata_suggest("тест", limit=8)
|
||
|
||
assert len(out) == 4
|
||
house = next(s for s in out if s.kind == "house")
|
||
assert house.fias_id == "0a1b2c3d-0000-4000-8000-00000000aaaa"
|
||
# У всех НЕ-house тиров fias_id обнулён, даже если DaData его прислал.
|
||
for s in out:
|
||
if s.kind != "house":
|
||
assert s.fias_id is None
|
||
|
||
|
||
async def test_dadata_suggest_house_without_fias_stays_none():
|
||
"""house-кандидат без fias у DaData → fias_id None (не падаем)."""
|
||
with patch(
|
||
"app.services.geocoder.dadata.suggest_addresses",
|
||
new=AsyncMock(return_value=[_sugg("house", None)]),
|
||
):
|
||
out = await _dadata_suggest("тест", limit=8)
|
||
assert len(out) == 1
|
||
assert out[0].fias_id is None
|
||
|
||
|
||
# ── 2. SuggestItem сериализация ──────────────────────────────────────────────
|
||
def test_suggest_item_serializes_fias_id():
|
||
item = SuggestItem(
|
||
label="ул Малышева, д 30",
|
||
full_address="г Екатеринбург, ул Малышева, д 30",
|
||
lat=56.84,
|
||
lon=60.60,
|
||
kind="house",
|
||
fias_id="0a1b2c3d-0000-4000-8000-00000000aaaa",
|
||
)
|
||
dumped = item.model_dump()
|
||
assert dumped["fias_id"] == "0a1b2c3d-0000-4000-8000-00000000aaaa"
|
||
|
||
|
||
def test_suggest_item_fias_id_defaults_none_for_non_dadata_tier():
|
||
"""Не-DaData тир (Yandex/Nominatim/cadastral) не передаёт fias_id → None."""
|
||
gs = GeocodeSuggestion(
|
||
label="Малышева 30",
|
||
full_address="г Екатеринбург, ул Малышева, д 30",
|
||
lat=56.84,
|
||
lon=60.60,
|
||
kind="house",
|
||
)
|
||
assert gs.fias_id is None
|
||
item = SuggestItem(
|
||
label=gs.label,
|
||
full_address=gs.full_address,
|
||
lat=gs.lat,
|
||
lon=gs.lon,
|
||
kind=gs.kind,
|
||
fias_id=gs.fias_id,
|
||
)
|
||
assert item.model_dump()["fias_id"] is None
|
||
|
||
|
||
# ── 3. TradeInEstimateInput.target_fias_id ───────────────────────────────────
|
||
def test_estimate_input_accepts_target_fias_id():
|
||
payload = TradeInEstimateInput(
|
||
address="ул Малышева 30, Екатеринбург",
|
||
area_m2=50.0,
|
||
rooms=2,
|
||
target_fias_id="0a1b2c3d-0000-4000-8000-00000000aaaa",
|
||
)
|
||
assert payload.target_fias_id == "0a1b2c3d-0000-4000-8000-00000000aaaa"
|
||
|
||
|
||
def test_estimate_input_target_fias_id_defaults_none():
|
||
payload = TradeInEstimateInput(
|
||
address="ул Малышева 30, Екатеринбург",
|
||
area_m2=50.0,
|
||
rooms=2,
|
||
)
|
||
assert payload.target_fias_id is None
|