From b21f4cb044ec5c85562b3869d40819deb20e6b49 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 12 Sep 2026 16:33:45 +0300 Subject: [PATCH] =?UTF-8?q?perf(tradein-tests):=20=D1=83=D0=B1=D0=B8=D1=80?= =?UTF-8?q?=D0=B0=D0=B5=D0=BC=20=D1=80=D0=B5=D0=B0=D0=BB=D1=8C=D0=BD=D1=8B?= =?UTF-8?q?=D0=B5=20=D0=BF=D0=B0=D1=83=D0=B7=D1=8B=20=D0=B8=D0=B7=205=20?= =?UTF-8?q?=D0=BC=D0=B5=D0=B4=D0=BB=D0=B5=D0=BD=D0=BD=D1=8B=D1=85=20=D1=82?= =?UTF-8?q?=D0=B5=D1=81=D1=82=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit test_2924_yandex_resolve_tried_at::test_successful_resolve_does_not_mark (26с): мокнутый db.execute дублирует одну строку в retry- и fresh-выборках, backfill_house_imv видит 2 строки и делает реальный await asyncio.sleep(5) между ними (анти-бот задержка); фикс — request_delay_sec=0 в вызове теста, дефолт в проде не тронут. Тот же паттерн дублирования строки бьёт ещё 4 теста ровно на 5.01с каждый: test_house_imv_backfill_browser_flag (2 теста), test_2698_imv_sidecar_wiring, test_scraper_kit_group_c_backfill_kit_parity — тот же фикс. test_3033_avito_native_secondary_filter (2 теста, 7.82с + 5.72с): fetch_around с одной страницей всё равно делает "trailing sleep" анти-бана (request_delay_sec=5.0*jitter) после единственного мокнутого запроса — передаём delay_override_sec=0, сеть и так подменена fake_fetch. Не удалено ни одного теста и ни одного assert. Прод-задержки (anti-bot 5с, request_delay_sec=5.0 в scraper_kit) не менялись. Не успел в рамках бюджета: test_auth_api (реальный bcrypt cost=12, не sleep — требует отдельного анализа), test_3398_estimator_valuations_proxy_pool, test_alerts_become_events (10 тестов ~2.05с), test_backfill_wave2, test_estimator_event_loop_2207 (последний, вероятно, специально измеряет реальный sleep(0.3с) для доказательства non-blocking — трогать осторожно). Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01JY6iWDnGDthdvsMWgK1BMG --- .../backend/tests/test_2698_imv_sidecar_wiring.py | 4 +++- .../tests/test_2924_yandex_resolve_tried_at.py | 6 ++++++ .../test_3033_avito_native_secondary_filter.py | 15 +++++++++++++-- .../tests/test_house_imv_backfill_browser_flag.py | 7 +++++-- ...est_scraper_kit_group_c_backfill_kit_parity.py | 4 +++- 5 files changed, 30 insertions(+), 6 deletions(-) diff --git a/tradein-mvp/backend/tests/test_2698_imv_sidecar_wiring.py b/tradein-mvp/backend/tests/test_2698_imv_sidecar_wiring.py index 6c3d64cb..6551d1d1 100644 --- a/tradein-mvp/backend/tests/test_2698_imv_sidecar_wiring.py +++ b/tradein-mvp/backend/tests/test_2698_imv_sidecar_wiring.py @@ -65,7 +65,9 @@ async def test_backfill_browser_fetcher_gets_proxy_pool_wiring( db.execute.return_value.mappings.return_value.all.return_value = [ {"id": 1, "address": "ЕКБ, ул. X, 1", "full_address": None, "lat": 56.8, "lon": 60.6} ] - await hib.backfill_house_imv(db, batch_size=1) + # request_delay_sec=0: мок дублирует строку в retry+fresh (2 записи вместо 1) — реальный + # анти-бот sleep(5с) между ними тут не нужен. + await hib.backfill_house_imv(db, batch_size=1, request_delay_sec=0) captured = _CapturingFetcher.captured assert captured["source"] == "avito" diff --git a/tradein-mvp/backend/tests/test_2924_yandex_resolve_tried_at.py b/tradein-mvp/backend/tests/test_2924_yandex_resolve_tried_at.py index fbb26d18..d99b654d 100644 --- a/tradein-mvp/backend/tests/test_2924_yandex_resolve_tried_at.py +++ b/tradein-mvp/backend/tests/test_2924_yandex_resolve_tried_at.py @@ -101,6 +101,12 @@ async def _run(pending: list[dict], *, force: bool = False, resolved: str | None ), patch.object(mod, "_sleep_with_jitter", AsyncMock(return_value=None)), patch("app.services.scraper_adapters.RealScraperConfig", MagicMock()), + # Удачный резолв доходит до fetch_jk — без мока это реальный BrowserFetcher/прокси-пул + # (26с на живом прогоне); тесту нужен только факт «маркер не выставлен», не сам fetch. + patch( + "scraper_kit.providers.yandex.newbuilding.YandexNewbuildingScraper.fetch_jk", + AsyncMock(return_value=None), + ), ): await mod.enrich_yandex_newbuilding_sweep( db, city="ekaterinburg", limit=5, force=force, request_delay_sec=0 diff --git a/tradein-mvp/backend/tests/test_3033_avito_native_secondary_filter.py b/tradein-mvp/backend/tests/test_3033_avito_native_secondary_filter.py index 92a5a630..47c2b969 100644 --- a/tradein-mvp/backend/tests/test_3033_avito_native_secondary_filter.py +++ b/tradein-mvp/backend/tests/test_3033_avito_native_secondary_filter.py @@ -129,7 +129,11 @@ def _capture(method: str, **kw: object) -> list[str]: def test_anchor_sweep_uses_secondary_slug_by_default() -> None: """fetch_around по умолчанию — путь вторички, geoCoords/radius сохранены в query.""" - urls = _capture("fetch_around", lat=56.84, lon=60.6, radius_m=1000, pages=1) + # delay_override_sec=0: сеть подменена, anti-ban sleep(5с*jitter) после единственной + # страницы тут не защищает ничего реального. + urls = _capture( + "fetch_around", lat=56.84, lon=60.6, radius_m=1000, pages=1, delay_override_sec=0 + ) assert urls, "fetch_around не сделал запроса" u = urlparse(urls[0]) assert u.path == f"/ekaterinburg/kvartiry/prodam/{SECONDARY_SLUG}", u.path @@ -139,8 +143,15 @@ def test_anchor_sweep_uses_secondary_slug_by_default() -> None: def test_anchor_sweep_secondary_only_false_keeps_general_path() -> None: """Контроль: secondary_only=False — прежняя общая выдача.""" + # delay_override_sec=0: тот же anti-ban trailing sleep, что и в тесте выше. urls = _capture( - "fetch_around", lat=56.84, lon=60.6, radius_m=1000, pages=1, secondary_only=False + "fetch_around", + lat=56.84, + lon=60.6, + radius_m=1000, + pages=1, + secondary_only=False, + delay_override_sec=0, ) assert urlparse(urls[0]).path == "/ekaterinburg/kvartiry/prodam-ASgBAgICAUSSA8YQ" diff --git a/tradein-mvp/backend/tests/test_house_imv_backfill_browser_flag.py b/tradein-mvp/backend/tests/test_house_imv_backfill_browser_flag.py index f95cd6cb..0805f408 100644 --- a/tradein-mvp/backend/tests/test_house_imv_backfill_browser_flag.py +++ b/tradein-mvp/backend/tests/test_house_imv_backfill_browser_flag.py @@ -64,7 +64,9 @@ async def test_backfill_flag_on_constructs_browser_and_threads_it( monkeypatch.setattr(house_imv_backfill, "_process_one_house", _fake_process) db = _fake_db_with_one_row() - await house_imv_backfill.backfill_house_imv(db, batch_size=10) + # request_delay_sec=0: мок дублирует строку в retry+fresh (2 записи вместо 1) — реальный + # анти-бот sleep(5с) между ними тут не нужен. + await house_imv_backfill.backfill_house_imv(db, batch_size=10, request_delay_sec=0) # Ровно один BrowserFetcher(source="avito"), вошли в контекст. assert len(_FakeBrowserFetcher.instances) == 1 @@ -91,7 +93,8 @@ async def test_backfill_flag_off_no_browser_and_none_threaded( monkeypatch.setattr(house_imv_backfill, "_process_one_house", _fake_process) db = _fake_db_with_one_row() - await house_imv_backfill.backfill_house_imv(db, batch_size=10) + # request_delay_sec=0: та же дублирующая мок-строка retry+fresh, что и в тесте выше. + await house_imv_backfill.backfill_house_imv(db, batch_size=10, request_delay_sec=0) # Флаг OFF → BrowserFetcher не конструируется, browser_fetcher=None. assert _FakeBrowserFetcher.instances == [] diff --git a/tradein-mvp/backend/tests/test_scraper_kit_group_c_backfill_kit_parity.py b/tradein-mvp/backend/tests/test_scraper_kit_group_c_backfill_kit_parity.py index 58c79a0c..89eb7ac1 100644 --- a/tradein-mvp/backend/tests/test_scraper_kit_group_c_backfill_kit_parity.py +++ b/tradein-mvp/backend/tests/test_scraper_kit_group_c_backfill_kit_parity.py @@ -186,7 +186,9 @@ async def test_house_imv_backfill_browser_fetcher_uses_settings_endpoint() -> No db.execute.return_value.mappings.return_value.all.return_value = [ {"id": 1, "address": "ЕКБ, ул. X, 1", "full_address": None, "lat": 56.8, "lon": 60.6} ] - await house_imv_backfill.backfill_house_imv(db, batch_size=1) + # request_delay_sec=0: мок дублирует строку в retry+fresh (2 записи вместо 1) — + # реальный анти-бот sleep(5с) между ними тут не нужен. + await house_imv_backfill.backfill_house_imv(db, batch_size=1, request_delay_sec=0) # #2698: к endpoint= добавилась проводка пула — без неё сайдкар брал env-прокси # (узел с affinity='domclick'), и домовая оценка падала 34 дня подряд.