diff --git a/tradein-mvp/browser/server.py b/tradein-mvp/browser/server.py index 1b9d98ea..6e22b0a5 100644 --- a/tradein-mvp/browser/server.py +++ b/tradein-mvp/browser/server.py @@ -107,10 +107,12 @@ Per-provider модель (#1793): поиск yandex.ru, а не голым goto (#3251). Ручная сессия 29.08.2026 показала эталонный человеческий путь: yandex.ru → клик по результату → выдача с Referer - yandex.ru. Дефолт "domclick" — авито/циан/яндекс - проверяются отдельно в #3251, не включать здесь. - Пустая строка = выключено везде (полный откат к - прежнему поведению). Провайдер без соответствующего + yandex.ru. Дефолт — пусто, выключено везде (#3263): + выдача Яндекса капчит конкретные выходные узлы пула, + и путь срабатывал ~2 раза из 9. Якорь поднимается + прямым goto на выдачу площадки, Referer карточке даёт + переход с неё. Включать только явным env и только + после замера на узлах пула. Провайдер без соответствующего BROWSER_ANCHOR_SEARCH_QUERY_{PROVIDER} поиск тоже не делает — см. ниже. BROWSER_ANCHOR_SEARCH_QUERY_{PROVIDER} — поисковый запрос на yandex.ru для @@ -329,12 +331,15 @@ BROWSER_PAGE_INTERVAL_JITTER_S: float = float( ) # Провайдеры, для которых якорная вкладка (#3251) заходит на origin ЧЕРЕЗ реальный -# поиск yandex.ru, а не голым goto. Дефолт "domclick" — авито/циан/яндекс проверяются -# отдельно (#3251), не расширять этот список без замера на них. Пустая строка env = -# выключено везде — полный откат на поведение до #3251. +# поиск yandex.ru, а не голым goto. Дефолт — пусто (#3263): капча на выдаче Яндекса — +# свойство выходного узла (замер владельца 29.08: узлы 1/10/11 — капча, 9 и домашний +# IP — выдача), в проде путь срабатывал ~2 из 9, а в логах 15-16.09 все 7 капч +# закончились «якорная вкладка не поднялась (Error)» через 0,2 с после отката — якоря +# не было вовсе. Без поиска якорь сразу открывает выдачу площадки, карточка идёт с +# referer=origin — переход внутри площадки. Включать только явным env после замера. _ANCHOR_VIA_SEARCH_PROVIDERS: frozenset[str] = frozenset( p.strip().lower() - for p in os.environ.get("BROWSER_ANCHOR_VIA_SEARCH", "domclick").split(",") + for p in os.environ.get("BROWSER_ANCHOR_VIA_SEARCH", "").split(",") if p.strip() ) diff --git a/tradein-mvp/browser/test_server_anchor_search.py b/tradein-mvp/browser/test_server_anchor_search.py index c1cec4ba..2ced4212 100644 --- a/tradein-mvp/browser/test_server_anchor_search.py +++ b/tradein-mvp/browser/test_server_anchor_search.py @@ -34,9 +34,12 @@ _HOSTNAME = "ekaterinburg.domclick.ru" @pytest.fixture(autouse=True) -def _reset_state() -> None: +def _reset_state(monkeypatch: pytest.MonkeyPatch) -> None: server._contexts = {} server._anchor_pages = {} + # Путь через поиск выключен по умолчанию (#3263) — тесты ниже проверяют его + # поведение при ЯВНОМ включении env'ом BROWSER_ANCHOR_VIA_SEARCH=domclick. + monkeypatch.setattr(server, "_ANCHOR_VIA_SEARCH_PROVIDERS", frozenset({"domclick"})) class _FakeLink: @@ -158,7 +161,7 @@ def _run_ensure_anchor(provider: str, origin: str, page: _FakeAnchorPage) -> boo def test_anchor_via_search_visits_yandex_and_clicks_result() -> None: - """domclick включён по умолчанию: заход на yandex.ru → клик по ссылке на хост.""" + """domclick включён env'ом: заход на yandex.ru → клик по ссылке на хост.""" page = _FakeAnchorPage(link_host=_HOSTNAME) anchored = _run_ensure_anchor("domclick", _ORIGIN, page) @@ -202,7 +205,7 @@ def test_no_link_in_serp_falls_back_without_referer() -> None: def test_provider_not_in_allowlist_skips_search_entirely() -> None: - """avito не в BROWSER_ANCHOR_VIA_SEARCH (дефолт только domclick) — поведение прежнее.""" + """avito не в BROWSER_ANCHOR_VIA_SEARCH (включён только domclick) — поведение прежнее.""" origin = "https://www.avito.ru/ekaterinburg/kvartiry" page = _FakeAnchorPage(link_host="www.avito.ru") @@ -255,3 +258,28 @@ def test_new_tab_wrong_host_falls_back_without_referer() -> None: assert origin_calls[0]["referer"] is None assert page.opened_popup is not None assert page.opened_popup.closed == 1, "попап не на том хосте должен быть закрыт" + + +def test_default_env_domclick_anchor_goes_straight_to_origin( + monkeypatch: pytest.MonkeyPatch, +) -> None: + """#3263: без env якорь Домклика открывает свою выдачу прямым goto, yandex.ru не трогает. + + Модуль грузится заново с удалённым env — проверяется именно код-дефолт, а не + значение, подставленное фикстурой. Капча на выдаче Яндекса включена в подделке: + если путь через поиск снова станет дефолтом, goto на yandex.ru попадёт в + goto_calls и тест покраснеет. + """ + monkeypatch.delenv("BROWSER_ANCHOR_VIA_SEARCH", raising=False) + spec = importlib.util.spec_from_file_location("tradein_browser_server_anchor_default", _SERVER_PATH) + assert spec is not None and spec.loader is not None + fresh = importlib.util.module_from_spec(spec) + spec.loader.exec_module(fresh) + + page = _FakeAnchorPage(link_host=_HOSTNAME, captcha=True) + fresh._contexts["domclick"] = _FakeAnchorContext(page) + anchored = asyncio.run(fresh._ensure_anchor_page("domclick", _ORIGIN)) + + assert anchored is True + assert fresh._anchor_pages["domclick"] is page + assert page.goto_calls == [{"url": _ORIGIN, "referer": None}]