feat(tradein/scraper-kit): migrate cian/yandex price-history & session to kit (#2306) #2324

Merged
lekss361 merged 2 commits from feat/tradein-migrate-price-history-kit into main 2026-07-03 21:33:10 +00:00
Showing only changes of commit 88b93435fc - Show all commits

View file

@ -235,6 +235,7 @@ async def test_backfill_yandex_addresses_error_on_non_200():
async def test_cian_price_history_saves_changes():
"""Happy path: fetch returns DetailEnrichment with price_changes → saved."""
from app.services.cian_price_history import backfill_cian_price_history
from app.services.scraper_adapters import RealScraperConfig
from app.services.scrapers.cian_detail import DetailEnrichment
enrichment = DetailEnrichment(
@ -277,6 +278,12 @@ async def test_cian_price_history_saves_changes():
assert result.saved == 2 # 2 price_changes
assert result.errors == 0
mock_fetch.assert_called_once()
# #2306 regression guard: kit fetch_detail silently drops the Cian proxy
# (direct connection, #806 ban risk) unless config=RealScraperConfig() is
# passed at the call site — assert_called_once() alone wouldn't catch someone
# dropping that kwarg later.
_, call_kwargs = mock_fetch.call_args
assert isinstance(call_kwargs.get("config"), RealScraperConfig)
mock_save.assert_called_once()
@ -284,6 +291,7 @@ async def test_cian_price_history_saves_changes():
async def test_cian_price_history_skips_no_changes():
"""Fetch succeeds but no price_changes → skipped (not error)."""
from app.services.cian_price_history import backfill_cian_price_history
from app.services.scraper_adapters import RealScraperConfig
from app.services.scrapers.cian_detail import DetailEnrichment
enrichment = DetailEnrichment(cian_id=111, price_changes=[])
@ -296,7 +304,7 @@ async def test_cian_price_history_skips_no_changes():
"app.services.cian_price_history.fetch_detail",
new_callable=AsyncMock,
return_value=enrichment,
),
) as mock_fetch,
patch("app.services.cian_price_history.save_detail_enrichment") as mock_save,
patch("app.services.cian_price_history.get_scraper_delay", return_value=0.0),
):
@ -309,12 +317,16 @@ async def test_cian_price_history_skips_no_changes():
assert result.skipped == 1
assert result.saved == 0
mock_save.assert_not_called()
# #2306 regression guard — see test_cian_price_history_saves_changes.
_, call_kwargs = mock_fetch.call_args
assert isinstance(call_kwargs.get("config"), RealScraperConfig)
@pytest.mark.asyncio
async def test_cian_price_history_handles_fetch_none():
"""fetch_detail returns None → errors+1, no crash."""
from app.services.cian_price_history import backfill_cian_price_history
from app.services.scraper_adapters import RealScraperConfig
mock_db = MagicMock()
rows = [{"id": 30, "source_url": "https://ekb.cian.ru/sale/flat/999/"}]
@ -324,7 +336,7 @@ async def test_cian_price_history_handles_fetch_none():
"app.services.cian_price_history.fetch_detail",
new_callable=AsyncMock,
return_value=None,
),
) as mock_fetch,
patch("app.services.cian_price_history.get_scraper_delay", return_value=0.0),
):
mock_mappings = MagicMock()
@ -335,6 +347,9 @@ async def test_cian_price_history_handles_fetch_none():
assert result.errors == 1
assert result.saved == 0
# #2306 regression guard — see test_cian_price_history_saves_changes.
_, call_kwargs = mock_fetch.call_args
assert isinstance(call_kwargs.get("config"), RealScraperConfig)
# ============================================================================