All checks were successful
CI Trade-In / changes (pull_request) Successful in 10s
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 1m43s
app.services.scrape_pipeline (3734 строк) имел 0 runtime-импортёров — Part C убрал
последнего вызывающего (admin.py, scheduler_main.py уже на
scraper_kit.orchestration.pipeline). Удаление:
- backend/app/services/scrape_pipeline.py — удалён целиком
Тесты (18 файлов, импортировавших scrape_pipeline):
DELETE (чистая legacy-оркестрация, kit-эквивалент покрыт в
test_scraper_kit_pipeline_parity{,2}.py):
- test_scrape_pipeline.py, test_scrape_pipeline_proxy_pool.py (proxy-pool DB-lease
вытеснен RealProxyProvider DI-адаптером, покрытым test_scraper_adapters_contracts.py)
- test_anchor_watchdog.py, test_ban_rotation_1790.py, test_provider_rotation_1848.py
- test_scraper_resilience_1949_1950.py, test_sweep_drain.py
CONVERT (parity-сравнение убрано, kit-only regression оставлен/усилен):
- test_scraper_kit_pipeline_parity.py, test_scraper_kit_pipeline_parity2.py —
_drive_old убран, _drive_new стал единственной веткой; ассёрты на конкретные
значения counters сохранены (это теперь основное regression-покрытие kit
orchestration)
- test_sweep_imv_phase.py — 6 IMV-phase тестов (enrich_imv true/false/no-touched/
crash/failed-counter/cancel) переведены на run_avito_city_sweep (kit) с DI
(config/matcher/enrichment); enrichment.process_houses_imv_batch — инжектируемый
Protocol-метод вместо прямого импорта app.services.house_imv_backfill
PARTIAL-EDIT (легаси sweep-behavior вырезан, admin/kit-DI/несвязанные тесты
оставлены):
- test_city_sweep.py — только retarget 4 импортов на scraper_kit.orchestration.pipeline
- test_cian_city_sweep.py, test_yandex_city_sweep.py — sweep-behavior секции удалены,
admin endpoint тесты (уже kit-DI) + anchor-timeout/watchdog формулы (retarget import)
оставлены
- test_domclick_sweep.py — sweep-wiring тесты удалены, Counters + _map_item оставлены
- test_avito_newbuilding_sweep.py — sweep-wiring тест #5 удалён, retarget
NewbuildingSweepCounters
- test_pipeline_browser_routing.py — 3 run_avito_pipeline теста удалены, browser-routing
на уровне fetch_detail/fetch_house_catalog/AvitoScraper не тронут
- test_scraper_proxy.py, tests/scrapers/test_avito_anti_bot.py — _avito_proxies()
переведён на kit (DI-параметр вместо settings-patch); pipeline-level тесты удалены
Stale-комментарии (scrape_pipeline.py как текущая реальность) поправлены в
admin.py, avito_detail_backfill.py, house_imv_backfill.py, yandex_price_history.py,
scrapers/avito_detail.py + kit-пакете (orchestration/__init__.py, orchestration
/pipeline.py, providers/avito/detail.py — strangler завершён, легаси удалён).
grep -rn scrape_pipeline app/ scripts/ packages/ — только past-tense упоминания
("удалён в Part E1", "перед его удалением") + один historical footnote
(contracts.py:138, provenance-заметка "собраны grep'ом по ... .py").
Backend suite: 3088 passed, 6 skipped (известный flake test_search_cache_hit #2208 —
не регрессия). ruff check чист на всех изменённых файлах.
85 lines
3.3 KiB
Python
85 lines
3.3 KiB
Python
"""Offline unit tests for DomClickCitySweepCounters + DomClick parser.
|
||
|
||
Legacy `run_domclick_city_sweep` orchestration behavior (sweep phases, cooperative
|
||
cancel, honest mark_done/mark_failed statuses) удалён вместе с
|
||
`app.services.scrape_pipeline` (#2397 Part E1) — эквивалентная regression-coverage
|
||
теперь в `test_scraper_kit_pipeline_parity2.py::test_domclick_city_sweep_{done,
|
||
blocked_failed}` (kit `run_domclick_city_sweep`).
|
||
|
||
Остались:
|
||
- DomClickCitySweepCounters (dataclass, из scraper_kit) — defaults + to_dict
|
||
- DomClickScraper._map_item — BFF offer-item → ScrapedLot маппинг (не зависит
|
||
от оркестрации, `app.services.scrapers.domclick` живёт до Part E2)
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import os
|
||
|
||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
||
|
||
import pytest
|
||
|
||
from app.services.scrapers.domclick import DomClickScraper
|
||
|
||
# ── DomClickCitySweepCounters ───────────────────────────────────────────────
|
||
|
||
|
||
def test_domclick_sweep_counters_defaults() -> None:
|
||
from scraper_kit.orchestration.pipeline import DomClickCitySweepCounters
|
||
|
||
c = DomClickCitySweepCounters()
|
||
assert c.lots_fetched == 0
|
||
assert c.lots_inserted == 0
|
||
assert c.lots_updated == 0
|
||
assert c.pages_fetched == 0
|
||
assert c.errors_count == 0
|
||
assert c.blocked == 0
|
||
assert c.geo_filtered == 0
|
||
|
||
|
||
def test_domclick_sweep_counters_to_dict_all_keys() -> None:
|
||
from dataclasses import fields
|
||
|
||
from scraper_kit.orchestration.pipeline import DomClickCitySweepCounters
|
||
|
||
c = DomClickCitySweepCounters()
|
||
d = c.to_dict()
|
||
expected = {f.name for f in fields(c)}
|
||
assert set(d.keys()) == expected
|
||
|
||
|
||
# ── _map_item ─────────────────────────────────────────────────────────────────
|
||
|
||
|
||
def test_map_item_basic_mapping() -> None:
|
||
"""DomClickScraper._map_item маппит BFF offer-item → ScrapedLot корректно."""
|
||
scraper = DomClickScraper()
|
||
item = {
|
||
"id": 111222333,
|
||
"path": "https://domclick.ru/card/sale__flat__111222333",
|
||
"location": {"lat": 56.838, "lon": 60.612},
|
||
"address": {"displayName": "Екатеринбург, улица Ленина, 10"},
|
||
"objectInfo": {"area": 52.3, "rooms": 2, "floor": 5},
|
||
"house": {"floors": 16, "buildYear": 2010},
|
||
"price": 6_500_000,
|
||
"squarePrice": 124282,
|
||
"flatComplex": None,
|
||
"isRosreestrApproved": False,
|
||
"publishedDate": "2026-06-01T10:00:00+05:00",
|
||
"updatedDate": None,
|
||
"lastPriceHistoryState": None,
|
||
"offerRegionName": "Екатеринбург",
|
||
}
|
||
lot = scraper._map_item(item)
|
||
assert lot is not None
|
||
assert lot.source == "domklik"
|
||
assert lot.source_id == "111222333"
|
||
assert lot.rooms == 2
|
||
assert lot.area_m2 == pytest.approx(52.3)
|
||
assert lot.floor == 5
|
||
assert lot.total_floors == 16
|
||
assert lot.price_rub == 6_500_000
|
||
assert lot.listing_segment == "vtorichka"
|
||
assert lot.lat == pytest.approx(56.838)
|
||
assert lot.lon == pytest.approx(60.612)
|