gendesign/tradein-mvp/browser/test_server_cookie_domain.py
bot-backend 18ae509f98
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
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 / browser-tests (pull_request) Successful in 1m27s
test(tradein/browser): домен инжектируемых кук — регистрируемый, не хост из url
Новый test_server_cookie_domain.py: схлопывание поддомена, хост из двух
лейблов без изменений, оба места инъекции зовут одну функцию.

Три существующих теста закрепляли как раз то поведение, которое оказалось
багом (.ekaterinburg.domclick.ru / .www.avito.ru / .realty.yandex.ru), —
переведены на новый контракт, докстринги объясняют почему.

Прогон сайдкара целиком: 206 passed.
2026-08-30 01:51:02 +03:00

112 lines
5 KiB
Python
Raw Permalink 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.

"""test_server_cookie_domain.py — на какой домен ложатся инжектируемые куки (#3261).
Сайдкар клал куки на домен из url карточки: для
``ekaterinburg.domclick.ru`` это ``.ekaterinburg.domclick.ru``. Настоящие куки
площадки живут на ``.domclick.ru``.
Куки поддомена родительские НЕ заменяют, а сосуществуют с ними: как только
площадка выдаёт свежий ``qrator_jsid2`` на ``.domclick.ru``, браузер шлёт в
одном запросе ДВЕ куки с этим именем, и первой — более специфичную, нашу
протухшую. QRATOR читает её и держит страницу на 274-байтном загрузчике.
Замер 30.08 (прод, узлы 1 и 9, чередование плеч, по 4 пробы на плечо):
прод-поведение — 0 успехов из 4, только шрифты — 0 из 4, куки на
``.domclick.ru`` — 3 из 4. Плечо «только шрифты» тут важно: оно показывает,
что чинит именно домен кук, а не сопутствующая правка образа.
Тестируется:
1. поддомен схлопывается до регистрируемого домена;
2. хост из двух лейблов остаётся как есть;
3. оба места инъекции (переиспользуемый context и одноразовая страница)
зовут одну и ту же функцию, а не считают домен по-своему.
camoufox НЕ запускается — browser/context/page подделаны.
Запуск (из tradein-mvp/browser/)::
python -m pytest test_server_cookie_domain.py -q
"""
from __future__ import annotations
import asyncio
import importlib.util
from pathlib import Path
from typing import Any
import pytest
_SERVER_PATH = Path(__file__).resolve().parent / "server.py"
_spec = importlib.util.spec_from_file_location("tradein_browser_server", _SERVER_PATH)
assert _spec is not None and _spec.loader is not None
server = importlib.util.module_from_spec(_spec)
_spec.loader.exec_module(server)
@pytest.fixture(autouse=True)
def _reset_state(monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setattr(server, "_contexts", {})
@pytest.mark.parametrize(
("url", "expected"),
[
("https://ekaterinburg.domclick.ru/card/sale__flat__1", ".domclick.ru"),
("https://domclick.ru/card/sale__flat__1", ".domclick.ru"),
("https://www.avito.ru/ekaterinburg/kvartiry/1", ".avito.ru"),
("https://spb.cian.ru/sale/flat/1/", ".cian.ru"),
# Хост без поддомена трогать нечего — возвращаем как есть.
("https://cian.ru/sale/flat/1/", ".cian.ru"),
# Несколько уровней поддоменов схлопываются до тех же двух лейблов.
("https://a.b.c.domclick.ru/card/1", ".domclick.ru"),
],
)
def test_cookie_domain_collapses_to_registrable(url: str, expected: str) -> None:
assert server._cookie_domain(url) == expected
class _FakeContext:
def __init__(self) -> None:
self.added: list[dict[str, str]] = []
async def add_cookies(self, cookies: list[dict[str, str]]) -> None:
self.added.extend(cookies)
async def new_page(self) -> Any:
raise AssertionError("не должно вызываться в этом тесте")
class _FakeBrowser:
def __init__(self, context: _FakeContext) -> None:
self._context = context
async def new_context(self) -> _FakeContext:
return self._context
def test_reusable_context_puts_cookies_on_parent_domain() -> None:
"""Переиспользуемый context (#3118) — основной путь добора карточек."""
ctx = _FakeContext()
browser = _FakeBrowser(ctx)
asyncio.run(
server._get_or_create_context(
"domclick",
browser,
{"qrator_jsid2": "x", "qrator_jsr": "y"},
"https://ekaterinburg.domclick.ru/card/sale__flat__2078475983",
)
)
assert {c["domain"] for c in ctx.added} == {".domclick.ru"}
assert {c["name"] for c in ctx.added} == {"qrator_jsid2", "qrator_jsr"}
def test_both_injection_sites_use_the_same_helper() -> None:
"""Второе место инъекции (reuse_context=false) не должно считать домен само.
Проверка структурная: если кто-то вернёт в любое из двух мест
``f".{urlparse(url).hostname}"``, тест покраснеет — а поймать это
поведенчески дороже, чем оно стоит.
"""
source = _SERVER_PATH.read_text(encoding="utf-8")
assert source.count("cookie_domain = _cookie_domain(url)") == 2
assert 'f".{urlparse(url).hostname' not in source