"""Единственный источник правды для curl_cffi impersonate-профиля (#3034). До этого фикса `impersonate="chrome120"` был захардкожен литералом в 9+ местах scraper_kit (`providers/avito/{serp,detail,imv,houses}.py`, `orchestration/pipeline.py` x4, `providers/cian/valuation.py`, `providers/yandex/valuation.py`) — обновить TLS-профиль значило найти и поправить их все вручную, и один пропущенный литерал молча гонял бы устаревший профиль. Теперь единственный источник — `DEFAULT_IMPERSONATE` (`providers/_base.py`); любой caller обязан читать константу. ПОЧЕМУ ГРЕП, А НЕ AST. Инвариант — «строка `chrome120` не встречается нигде в дереве» (ни в коде, ни в docstring/комментарии) — буквально то, что нужно поймать: сменится профиль — не должно остаться ни одного места, которое надо будет вспомнить и найти руками. AST увидел бы только строковые константы в выражениях (kwarg-значения, docstring), но не поймал бы упоминание в `#`-комментарии — а именно в комментарии литерал незаметнее всего переживёт следующий рефактор. Область: `packages/scraper-kit/src/scraper_kit/` — пакет, где живёт `DEFAULT_IMPERSONATE` и все providers. Legacy-дубликаты вне kit (например `app/services/cian_session.py`, ещё не мигрированный на kit, #2361 F4a) сюда намеренно не входят — отдельный, более крупный периметр вне scope #3034. """ from __future__ import annotations from pathlib import Path _BACKEND_ROOT = Path(__file__).resolve().parents[1] _KIT_SRC = _BACKEND_ROOT.parent / "packages" / "scraper-kit" / "src" / "scraper_kit" _BANNED_LITERAL = "chrome120" def test_scan_area_exists() -> None: """Область сканирования жива — иначе сторож зелен вхолостую (путь съехал).""" assert _KIT_SRC.is_dir(), f"область сканирования съехала: {_KIT_SRC}" def test_detector_actually_detects(tmp_path: Path) -> None: """Сторож обязан уметь краснеть на литерале — иначе он зелен вхолостую.""" probe = tmp_path / "probe.py" probe.write_text('impersonate = "chrome120"\n', encoding="utf-8") assert _BANNED_LITERAL in probe.read_text(encoding="utf-8") clean = tmp_path / "clean.py" clean.write_text( "from scraper_kit.providers._base import DEFAULT_IMPERSONATE\n", encoding="utf-8" ) assert _BANNED_LITERAL not in clean.read_text(encoding="utf-8") def test_default_impersonate_is_the_only_chrome_profile_literal() -> None: """Ни один файл scraper_kit не содержит строку "chrome120" — ни в коде, ни в docstring/комментарии. Единственное разрешённое место для конкретного номера профиля — значение `DEFAULT_IMPERSONATE` в `providers/_base.py`. """ offenders = [ str(path.relative_to(_KIT_SRC)) for path in sorted(_KIT_SRC.rglob("*.py")) if _BANNED_LITERAL in path.read_text(encoding="utf-8") ] assert offenders == [], ( f"{offenders}: литерал {_BANNED_LITERAL!r} обходит DEFAULT_IMPERSONATE " "(providers/_base.py) — единственный источник правды для impersonate-" "профиля. Прочитай константу вместо хардкода строки." )