fix(tradein/scraper): дожать guard #2616 до реальных прод-путей (ревью PR)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI Trade-In / changes (pull_request) Successful in 8s
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 / backend-tests (pull_request) Successful in 2m40s

Ревью нашло: guard NoProxyAvailableError был мёртв ровно там, где нужен —
(1) avito-пути pipeline.py конструируют BrowserFetcher напрямую, минуя
build_browser_fetcher(), и не прокидывали environment → дефолт dev, отказ
никогда не срабатывал; добавлен environment=getattr(config, ...) во все 3
construction-site + source-level тест-гард на новые сайты;
(2) browser-сайдкар: ENVIRONMENT не задан в docker-compose.prod.yml → IS_PROD
guard server.py читал dev; добавлен ENVIRONMENT: production в environment-блок.
This commit is contained in:
bot-backend 2026-08-04 23:23:42 +05:00
parent 2fc4b1b0fc
commit d4e4dd7124
3 changed files with 59 additions and 0 deletions

View file

@ -0,0 +1,46 @@
"""#2616 шаг 1: каждый прямой BrowserFetcher(...) в pipeline.py обязан прокидывать
environment=.
Ревью PR #2634 нашло: build_browser_fetcher() (providers/_base.py) — единственное
место, где environment прокидывался, а avito-пути pipeline.py конструируют
BrowserFetcher напрямую guard NoProxyAvailableError был мёртв ровно для
источника, ради которого писался (#2613). Юнит-тесты этого не ловили, потому что
конструировали фетчер сами, а не путём pipeline.
Source-level гард дешевле интеграционного прогона pipeline и падает на первом же
новом construction-site без environment=.
"""
from __future__ import annotations
import re
from pathlib import Path
_PIPELINE = (
Path(__file__).resolve().parents[2]
/ "packages"
/ "scraper-kit"
/ "src"
/ "scraper_kit"
/ "orchestration"
/ "pipeline.py"
)
def test_every_direct_browserfetcher_construction_threads_environment() -> None:
src = _PIPELINE.read_text(encoding="utf-8")
# Все вызовы конструктора BrowserFetcher( ... ) — балансировку скобок не пишем,
# берём консервативное окно в 15 строк после открытия вызова.
sites = [m.start() for m in re.finditer(r"BrowserFetcher\(\n", src)]
assert sites, "pipeline.py больше не конструирует BrowserFetcher напрямую? Обнови/удали тест."
missing: list[str] = []
for pos in sites:
window = src[pos : pos + 700]
if "environment=" not in window:
line_no = src[:pos].count("\n") + 1
missing.append(f"pipeline.py:{line_no}")
assert not missing, (
f"Прямые BrowserFetcher(...) без environment= ({missing}): guard "
"NoProxyAvailableError (#2616) для них мёртв — добавь "
'environment=getattr(config, "environment", "dev").'
)

View file

@ -67,6 +67,10 @@ services:
# Сколько /fetch параллельно. Дефолт 4; ops поднимет до 8 в .env.runtime после смоука.
BROWSER_CONCURRENCY: ${BROWSER_CONCURRENCY:-4}
# SCRAPER_PROXY_URL читается из .env.runtime (см. env_file выше)
# #2616 шаг 1: server.py IS_PROD-guard (отказ 503 вместо direct-IP camoufox при
# отсутствии прокси) активен только при ENVIRONMENT=production — без этой
# строки guard молча спит (дефолт "dev").
ENVIRONMENT: production
expose:
- "3000"
restart: unless-stopped

View file

@ -561,6 +561,9 @@ async def run_avito_pipeline(
endpoint=config.browser_http_endpoint,
proxy_provider=proxy_provider,
use_pool=config.use_proxy_pool_browser,
# #2616 шаг 1: прямое конструирование обходит build_browser_fetcher() —
# без environment guard NoProxyAvailableError мёртв для avito.
environment=getattr(config, "environment", "dev"),
)
await browser_fetcher.__aenter__()
own_browser = True
@ -1039,6 +1042,9 @@ async def run_avito_city_sweep(
endpoint=config.browser_http_endpoint,
proxy_provider=proxy_provider,
use_pool=config.use_proxy_pool_browser,
# #2616 шаг 1: см. run_avito_pipeline — environment обязателен
# при прямом конструировании (guard иначе мёртв).
environment=getattr(config, "environment", "dev"),
)
)
else:
@ -1727,6 +1733,9 @@ async def run_avito_newbuilding_sweep(
endpoint=config.browser_http_endpoint,
proxy_provider=proxy_provider,
use_pool=config.use_proxy_pool_browser,
# #2616 шаг 1: см. run_avito_pipeline — environment обязателен
# при прямом конструировании (guard иначе мёртв).
environment=getattr(config, "environment", "dev"),
)
)
else: