gendesign/tradein-mvp/backend/tests/test_serp_city_location_urls.py
lekss361 698576e77b
All checks were successful
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / build-backend (push) Successful in 1m37s
Deploy Trade-In / changes (push) Successful in 10s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 5m1s
Deploy Trade-In / deploy (push) Successful in 6m47s
fix(tradein/scraper): per-city SERP location for oblast city-sweep (#12) (#2525)
Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
2026-07-13 21:41:07 +00:00

104 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.

"""Per-city SERP location URL builders (#12 fix EKB-leak в oblast city-sweep).
Проблема: avito/cian/yandex провайдеры хардкодили ЕКБ-локацию (путь /ekaterinburg/,
CIAN_EKB_REGION_ID, _EKB_RGID) мимо anchor'а oblast-города → city-sweep целевого
города (напр. Н.Тагил) фактически скрейпил ЕКБ, n_analogs=0. Фикс — новые
kw-параметры (avito: target_city_slug уже был #2487, теперь и в ПУТИ URL;
cian: city_region_id; yandex: city_rgid), все дефолт None → ЕКБ-поведение
byte-identical прежнему (back-compat critical).
No network / no DB — чистые URL-билдеры на stub-конфиге.
"""
from __future__ import annotations
import os
from types import SimpleNamespace
from urllib.parse import parse_qs, urlparse
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
from scraper_kit.providers.avito.serp import NOVOSTROYKA_SLUG, AvitoScraper
from scraper_kit.providers.cian.serp import CIAN_EKB_REGION_ID, CianScraper
from scraper_kit.providers.yandex.serp import _EKB_RGID, YandexRealtyScraper
_STUB_CFG = SimpleNamespace()
def _query(url: str) -> dict[str, list[str]]:
return parse_qs(urlparse(url).query)
# ── Avito: city-slug в ПУТЬ URL (не только SERP-фильтр) ────────────────────────
def test_avito_web_url_uses_target_city_slug_in_path() -> None:
s = AvitoScraper(_STUB_CFG, target_city_slug="nizhniy_tagil") # type: ignore[arg-type]
url = s._build_web_url(57.910, 59.980, radius_km=2, page=1)
assert "/nizhniy_tagil/" in url
assert "/ekaterinburg/" not in url
def test_avito_citywide_url_uses_target_city_slug_in_path() -> None:
s = AvitoScraper(_STUB_CFG, target_city_slug="kamensk-uralskiy") # type: ignore[arg-type]
url = s._build_citywide_url(page=2)
assert "/kamensk-uralskiy/" in url
assert "/ekaterinburg/" not in url
def test_avito_newbuilding_url_uses_target_city_slug_in_path() -> None:
s = AvitoScraper(_STUB_CFG, target_city_slug="verhnyaya_pyshma") # type: ignore[arg-type]
url = s._build_newbuilding_url(page=1)
assert "/verhnyaya_pyshma/" in url
assert "/ekaterinburg/" not in url
assert NOVOSTROYKA_SLUG in url
def test_avito_rooms_url_uses_target_city_slug_in_path() -> None:
s = AvitoScraper(_STUB_CFG, target_city_slug="serov") # type: ignore[arg-type]
url = s._build_rooms_url("2k", page=1)
assert "/serov/" in url
assert "/ekaterinburg/" not in url
def test_avito_urls_default_to_ekaterinburg_when_no_target_city_slug() -> None:
"""Back-compat: target_city_slug=None → путь /ekaterinburg/ (ЕКБ full_load/pipeline)."""
s = AvitoScraper(_STUB_CFG, target_city_slug=None) # type: ignore[arg-type]
assert "/ekaterinburg/" in s._build_web_url(56.84, 60.60, radius_km=1, page=1)
assert "/ekaterinburg/" in s._build_citywide_url(page=1)
assert "/ekaterinburg/" in s._build_newbuilding_url(page=1)
assert "/ekaterinburg/" in s._build_rooms_url("2k", page=1)
# ── Cian: city_region_id → region= (subdomain НЕ трогается) ────────────────────
def test_cian_build_url_uses_city_region_id() -> None:
s = CianScraper(_STUB_CFG, city_region_id=4886) # type: ignore[arg-type]
url = s._build_url()
assert _query(url)["region"] == ["4886"]
assert urlparse(url).hostname == "ekb.cian.ru"
def test_cian_build_url_defaults_to_ekb_region_when_no_city_region_id() -> None:
"""Back-compat: city_region_id=None → region=CIAN_EKB_REGION_ID (4743)."""
s = CianScraper(_STUB_CFG, city_region_id=None) # type: ignore[arg-type]
url = s._build_url()
assert _query(url)["region"] == [str(CIAN_EKB_REGION_ID)]
assert urlparse(url).hostname == "ekb.cian.ru"
# ── Yandex: city_rgid → rgid= ───────────────────────────────────────────────────
def test_yandex_build_url_uses_city_rgid() -> None:
s = YandexRealtyScraper(_STUB_CFG, city_rgid=328286) # type: ignore[arg-type]
url = s._build_url()
assert _query(url)["rgid"] == ["328286"]
def test_yandex_build_url_defaults_to_ekb_rgid_when_no_city_rgid() -> None:
"""Back-compat: city_rgid=None → rgid=_EKB_RGID (559132)."""
s = YandexRealtyScraper(_STUB_CFG, city_rgid=None) # type: ignore[arg-type]
url = s._build_url()
assert _query(url)["rgid"] == [str(_EKB_RGID)]