gendesign/tradein-mvp/backend/tests/test_yandex_valuation_save.py
bot-backend 17d558b18c
All checks were successful
Deploy Trade-In / changes (push) Successful in 12s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m27s
Deploy Trade-In / build-backend (push) Successful in 1m33s
Deploy Trade-In / deploy (push) Successful in 2m12s
fix(tradein/yandex): снести признак «панорама» — его нет на площадке (#2851)
2026-08-12 20:44:11 +00:00

214 lines
7 KiB
Python
Raw Permalink 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.

"""Tests for _save_yandex_history_items — house_id linking + new columns.
Легаси `app.services.scrapers.yandex_valuation` удалён (#2277 финальный шаг
scraper_kit-миграции) — `result` типы переведены на kit-эквивалент
`scraper_kit.providers.yandex.valuation`, тот же тип, что `estimator.py` реально
принимает в `_save_yandex_history_items`.
"""
from __future__ import annotations
import os
# Settings requires DATABASE_URL at init time. Set dummy DSN before any app import.
os.environ.setdefault("DATABASE_URL", "postgresql://test:test@localhost/test_db")
from datetime import date
from unittest.mock import MagicMock, patch
import pytest
from scraper_kit.providers.yandex.valuation import (
ValuationHistoryItem,
ValuationHouseMeta,
YandexValuationResult,
)
from app.services.estimator import _save_yandex_history_items
def _history_rows(db) -> list[dict]:
"""Строки батча house_placement_history из мока сессии.
#2674: раньше тесты брали `db.execute.call_args_list[0]` — позиционно. Позиция
сломалась, как только у функции появился второй execute перед вставкой истории.
Фильтруем по SQL: тест переживёт любой новый вызов.
"""
for call in db.execute.call_args_list:
if "INSERT INTO house_placement_history" in str(call.args[0]):
return call.args[1]
return []
def _make_result(items: list[ValuationHistoryItem]) -> YandexValuationResult:
return YandexValuationResult(
address="Россия, Свердловская область, Екатеринбург, улица Куйбышева, 106",
offer_category="APARTMENT",
offer_type="SELL",
page=1,
source_url=(
"https://realty.yandex.ru/otsenka-kvartiry-po-adresu-onlayn/?address=test&page=1"
),
house=ValuationHouseMeta(year_built=2005, total_floors=25, has_lift=True),
history_items=items,
)
def _two_items() -> list[ValuationHistoryItem]:
return [
ValuationHistoryItem(
area_m2=50.0,
rooms=2,
floor=3,
start_price=9_000_000,
last_price=9_000_000,
publish_date=date(2024, 5, 10),
removed_date=None,
exposure_days=30,
status="В продаже",
),
ValuationHistoryItem(
area_m2=55.0,
rooms=2,
floor=5,
start_price=10_000_000,
last_price=9_500_000,
publish_date=date(2024, 3, 1),
removed_date=date(2024, 6, 1),
exposure_days=92,
status="Снято",
),
]
def test_save_resolves_house_once_per_page():
"""match_or_create_house must be called exactly once per page (not per item)."""
db = MagicMock()
result = _make_result(_two_items())
with patch(
"app.services.estimator.match_or_create_house",
return_value=(12345, 0.9, "fingerprint"),
) as m:
saved = _save_yandex_history_items(db, result)
assert m.call_count == 1, "match_or_create_house must be called once per page"
assert saved == 2
def test_save_row_contains_house_id_and_confidence():
"""Each INSERT row dict carries house_id and source_confidence."""
db = MagicMock()
result = _make_result(_two_items())
with patch(
"app.services.estimator.match_or_create_house",
return_value=(54321, 0.9, "fingerprint"),
):
_save_yandex_history_items(db, result)
# История — один execute со list-of-dicts (executemany, один round-trip).
rows = _history_rows(db)
assert isinstance(rows, list) and len(rows) == 2
for row in rows:
assert row["house_id"] == 54321
assert row["confidence"] == pytest.approx(0.9)
assert row["notes"] == "match_method=fingerprint"
assert row["total_floors"] == 25
def test_save_row_contains_removed_date():
"""Item with removed_date set → row dict has removed_date populated."""
db = MagicMock()
items = [
ValuationHistoryItem(
area_m2=60.0,
rooms=2,
floor=4,
start_price=11_000_000,
last_price=10_500_000,
publish_date=date(2024, 1, 15),
removed_date=date(2024, 5, 20),
exposure_days=125,
status="Снято",
),
]
result = _make_result(items)
with patch(
"app.services.estimator.match_or_create_house",
return_value=(99, 1.0, "new"),
):
_save_yandex_history_items(db, result)
# args[1] is now the list-of-dicts passed to executemany
rows = _history_rows(db)
assert rows[0]["removed_date"] == date(2024, 5, 20)
def test_save_row_removed_date_none_when_active():
"""Active listing (status В продаже) → removed_date=None in row."""
db = MagicMock()
items = [
ValuationHistoryItem(
area_m2=42.0,
rooms=1,
floor=2,
start_price=7_500_000,
last_price=7_500_000,
publish_date=date(2024, 6, 1),
removed_date=None,
exposure_days=10,
status="В продаже",
),
]
result = _make_result(items)
with patch(
"app.services.estimator.match_or_create_house",
return_value=(7, 1.0, "new"),
):
_save_yandex_history_items(db, result)
rows = _history_rows(db)
assert rows[0]["removed_date"] is None
def test_save_handles_match_failure_gracefully():
"""If match_or_create_house raises, save continues with house_id=None, confidence=0."""
db = MagicMock()
items = [
ValuationHistoryItem(
area_m2=42.0,
rooms=1,
floor=2,
start_price=7_500_000,
last_price=7_500_000,
publish_date=date(2024, 6, 1),
removed_date=None,
exposure_days=10,
status="В продаже",
),
]
result = _make_result(items)
with patch(
"app.services.estimator.match_or_create_house",
side_effect=RuntimeError("simulated lookup failure"),
):
saved = _save_yandex_history_items(db, result)
assert saved == 1
rows = _history_rows(db)
assert rows[0]["house_id"] is None
assert rows[0]["confidence"] == pytest.approx(0.0)
assert rows[0]["notes"] is None
def test_save_match_called_with_address_and_year():
"""match_or_create_house receives correct address and year_built from result.house."""
db = MagicMock()
result = _make_result(_two_items())
with patch(
"app.services.estimator.match_or_create_house",
return_value=(1, 1.0, "new"),
) as m:
_save_yandex_history_items(db, result)
kwargs = m.call_args.kwargs
assert kwargs["address"] == result.address
assert kwargs["year_built"] == result.house.year_built
assert kwargs["ext_source"] == "yandex_valuation"