From 2fcca8114488eb52a40c724fd5ac104014c4af02 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Thu, 6 Aug 2026 16:06:46 +0500 Subject: [PATCH] =?UTF-8?q?fix(tradein/browser):=20=D1=87=D0=B5=D1=82?= =?UTF-8?q?=D1=8B=D1=80=D0=B5=20=D1=82=D0=B5=D1=81=D1=82=D0=B0=20=D1=81?= =?UTF-8?q?=D0=B0=D0=B9=D0=B4=D0=BA=D0=B0=D1=80=D0=B0=20=D0=B4=D0=BE=D0=B3?= =?UTF-8?q?=D0=BE=D0=BD=D1=8F=D1=8E=D1=82=20=D0=BA=D0=BE=D0=B4=20=E2=80=94?= =?UTF-8?q?=20=D1=82=D0=B5=D1=81=D1=82=20=D0=BE=D1=82=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BB,=20=D0=BD=D0=B5=20=D0=BA=D0=BE=D0=B4=20=D1=81=D0=BB?= =?UTF-8?q?=D0=BE=D0=BC=D0=B0=D0=BD=20(#2722)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Все четыре падения на main — устаревшие тесты, поведение сервиса корректно. Проверено мутацией: возврат кода к старому виду снова красит эти тесты. 1) test_pace_provider_disabled_when_zero (красный с 2026-06-20, 3f30c1c9). Тот коммит перевёл _pace_provider на _MIN_PAGE_INTERVAL_BY_PROVIDER (per-provider override), добавил под это новый test_server_pacing.py — и не тронул старый тест, который патчит модульный BROWSER_MIN_PAGE_INTERVAL_S. Глобал теперь читается только как фолбэк для провайдера ВНЕ словаря, поэтому на "avito" патч не влияет ни на что: тест ставил 0.0 и получал дефолтные 2.0. Через окружение выключение работает (BROWSER_MIN_PAGE_INTERVAL_S=0 → словарь собирается нулями на импорте), то есть заявленное поведение живо. Соседние четыре пейсинг-теста патчили тот же мёртвый символ, но ставили 2.0 — ровно дефолт словаря, и потому зеленели вхолостую: они не управляли тем, что проверяли. Переведены на setitem туда же. 2-4) test_fetch_returns_503_when_browser_unavailable, test_fetch_opens_and_closes_own_page, test_fetch_recycles_after_threshold (красные с 2026-07-02, ca473a00 / #2164 P4). Тот коммит добавил _ensure_browser(proxy_override=...) и обновил подделки в test_server.py и test_server_fetch_json.py, а четыре подделки в test_server_smoke.py остались одноаргументными → TypeError на server.py:709. Подписи подделок приведены к реальной. Подделка в /login-тесте тоже, хотя login_handler зовёт _ensure_browser позиционно: расхождение подписи мока с оригиналом и есть причина этой поломки, чинить её стоит целиком. Файл test_server_smoke.py правился дважды ПОСЛЕ 2026-07-02 (#2430, #2433) и краснота никого не остановила — тесты сайдкара не бежит ни один пайплайн. Включение их в CI — отдельным PR, после этой починки (порядок из #2722). Refs #2722 --- tradein-mvp/browser/test_server.py | 17 +++++++++++------ tradein-mvp/browser/test_server_smoke.py | 8 ++++---- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tradein-mvp/browser/test_server.py b/tradein-mvp/browser/test_server.py index 399f6722..2f1438eb 100644 --- a/tradein-mvp/browser/test_server.py +++ b/tradein-mvp/browser/test_server.py @@ -372,7 +372,11 @@ def test_resource_block_handler_degrades_to_continue(monkeypatch: pytest.MonkeyP def test_pace_provider_first_call_no_sleep(monkeypatch: pytest.MonkeyPatch) -> None: """Первый goto провайдера — без ожидания, но timestamp пишется.""" - monkeypatch.setattr(server, "BROWSER_MIN_PAGE_INTERVAL_S", 2.0) + # Интервал задаём в _MIN_PAGE_INTERVAL_BY_PROVIDER — ИМЕННО его читает + # _pace_provider (per-provider override, #1812 follow-up). Модульный + # BROWSER_MIN_PAGE_INTERVAL_S там только фолбэк для провайдера вне словаря, + # так что патч глобала на известном провайдере не влияет ни на что. + monkeypatch.setitem(server._MIN_PAGE_INTERVAL_BY_PROVIDER, "avito", 2.0) slept: list[float] = [] async def _fake_sleep(d: float) -> None: @@ -386,7 +390,7 @@ def test_pace_provider_first_call_no_sleep(monkeypatch: pytest.MonkeyPatch) -> N def test_pace_provider_waits_remaining_interval(monkeypatch: pytest.MonkeyPatch) -> None: """Второй goto в пределах интервала → sleep на остаток (interval - elapsed).""" - monkeypatch.setattr(server, "BROWSER_MIN_PAGE_INTERVAL_S", 2.0) + monkeypatch.setitem(server._MIN_PAGE_INTERVAL_BY_PROVIDER, "cian", 2.0) slept: list[float] = [] async def _fake_sleep(d: float) -> None: @@ -415,7 +419,7 @@ def test_pace_provider_waits_remaining_interval(monkeypatch: pytest.MonkeyPatch) def test_pace_provider_no_sleep_when_interval_elapsed(monkeypatch: pytest.MonkeyPatch) -> None: """Если с прошлого goto прошло >= интервала → не спим.""" - monkeypatch.setattr(server, "BROWSER_MIN_PAGE_INTERVAL_S", 2.0) + monkeypatch.setitem(server._MIN_PAGE_INTERVAL_BY_PROVIDER, "yandex", 2.0) slept: list[float] = [] async def _fake_sleep(d: float) -> None: @@ -440,8 +444,8 @@ def test_pace_provider_no_sleep_when_interval_elapsed(monkeypatch: pytest.Monkey def test_pace_provider_disabled_when_zero(monkeypatch: pytest.MonkeyPatch) -> None: - """BROWSER_MIN_PAGE_INTERVAL_S <= 0 → пейсинг выключен, timestamp не пишется.""" - monkeypatch.setattr(server, "BROWSER_MIN_PAGE_INTERVAL_S", 0.0) + """Эффективный интервал провайдера <= 0 → пейсинг выключен, timestamp не пишется.""" + monkeypatch.setitem(server._MIN_PAGE_INTERVAL_BY_PROVIDER, "avito", 0.0) slept: list[float] = [] async def _fake_sleep(d: float) -> None: @@ -460,7 +464,8 @@ def test_pace_provider_disabled_when_zero(monkeypatch: pytest.MonkeyPatch) -> No def test_pace_provider_independent_per_provider(monkeypatch: pytest.MonkeyPatch) -> None: """Timestamp независим по провайдерам — пейсинг avito не влияет на cian.""" - monkeypatch.setattr(server, "BROWSER_MIN_PAGE_INTERVAL_S", 2.0) + monkeypatch.setitem(server._MIN_PAGE_INTERVAL_BY_PROVIDER, "avito", 2.0) + monkeypatch.setitem(server._MIN_PAGE_INTERVAL_BY_PROVIDER, "cian", 2.0) slept: list[float] = [] async def _fake_sleep(d: float) -> None: diff --git a/tradein-mvp/browser/test_server_smoke.py b/tradein-mvp/browser/test_server_smoke.py index 43208a73..13c8527d 100644 --- a/tradein-mvp/browser/test_server_smoke.py +++ b/tradein-mvp/browser/test_server_smoke.py @@ -89,7 +89,7 @@ def test_fetch_returns_503_when_browser_unavailable( Никакого реального camoufox-launch'а / direct-fetch. """ - async def _no_ensure(provider: str) -> bool: + async def _no_ensure(provider: str, proxy_override: str | None = None) -> bool: return False # прокси всё ещё недоступен → браузер остаётся None monkeypatch.setattr(server, "_ensure_browser", _no_ensure) @@ -111,7 +111,7 @@ def test_login_returns_503_when_browser_unavailable( ) -> None: """/login тоже отдаёт 503 когда инстанс не поднят (без credential-логирования).""" - async def _no_ensure(provider: str) -> bool: + async def _no_ensure(provider: str, proxy_override: str | None = None) -> bool: return False monkeypatch.setattr(server, "_ensure_browser", _no_ensure) @@ -193,7 +193,7 @@ def test_fetch_opens_and_closes_own_page(monkeypatch: pytest.MonkeyPatch) -> Non server._browsers["avito"] = _FakeBrowser(tracker) monkeypatch.setattr(server, "BROWSER_RECYCLE_PAGES", 10_000) - async def _ensure(provider: str) -> bool: + async def _ensure(provider: str, proxy_override: str | None = None) -> bool: return True monkeypatch.setattr(server, "_ensure_browser", _ensure) @@ -221,7 +221,7 @@ def test_fetch_recycles_after_threshold(monkeypatch: pytest.MonkeyPatch) -> None relaunched["n"] += 1 server._page_counters[provider] = 0 - async def _ensure(provider: str) -> bool: + async def _ensure(provider: str, proxy_override: str | None = None) -> bool: return True monkeypatch.setattr(server, "_relaunch_browser", _fake_relaunch) -- 2.45.3