From d815d7ba831f3ffe7751f1a933766df6df82db3a Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 30 May 2026 22:07:53 +0300 Subject: [PATCH] fixup(scrapers): bind SCRAPER_PROXY_URL via validation_alias + env-binding test (#806) --- tradein-mvp/backend/app/core/config.py | 5 ++- .../backend/tests/test_scraper_proxy.py | 40 +++++++++++++++++++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/tradein-mvp/backend/app/core/config.py b/tradein-mvp/backend/app/core/config.py index 2c92f3f4..08681852 100644 --- a/tradein-mvp/backend/app/core/config.py +++ b/tradein-mvp/backend/app/core/config.py @@ -1,5 +1,6 @@ """Минимальный settings для standalone trade-in MVP.""" +from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict @@ -142,7 +143,9 @@ class Settings(BaseSettings): # 2. AVITO_PROXY_URL — legacy ENV; fallback, чтобы prod-серверы с уже # настроенным AVITO_PROXY_URL работали без изменений .env.runtime (#806). # property `scraper_proxy_url` реализует эту логику; используй его везде. - scraper_proxy_url_env: str | None = None # ENV: SCRAPER_PROXY_URL (новый общий) + # validation_alias привязывает поле к env SCRAPER_PROXY_URL (без него + # pydantic-settings читал бы SCRAPER_PROXY_URL_ENV по имени поля — #806 fixup). + scraper_proxy_url_env: str | None = Field(default=None, validation_alias="SCRAPER_PROXY_URL") avito_proxy_url: str | None = None # ENV: AVITO_PROXY_URL (legacy fallback) @property diff --git a/tradein-mvp/backend/tests/test_scraper_proxy.py b/tradein-mvp/backend/tests/test_scraper_proxy.py index 8feee9e6..108e2b01 100644 --- a/tradein-mvp/backend/tests/test_scraper_proxy.py +++ b/tradein-mvp/backend/tests/test_scraper_proxy.py @@ -100,3 +100,43 @@ def test_avito_proxies_fallback_via_avito_proxy_url(): result = _avito_proxies() assert result == {"http": avito_url, "https": avito_url} + + +# ── Real pydantic env-binding (regression for #806 fixup) ───────────────────── +# The inline-replica tests above prove the OR-logic but NOT that the field +# actually binds to env SCRAPER_PROXY_URL. `validation_alias` is what makes that +# work — without it pydantic-settings reads SCRAPER_PROXY_URL_ENV (the field +# name), so setting SCRAPER_PROXY_URL would be a silent no-op. These tests +# construct a real Settings and assert end-to-end env binding. + + +def _fresh_settings(): + from app.core.config import Settings + + return Settings(_env_file=None) + + +def test_settings_binds_scraper_proxy_url_env(monkeypatch): + monkeypatch.setenv("SCRAPER_PROXY_URL", "http://scraper-proxy:1234") + monkeypatch.delenv("AVITO_PROXY_URL", raising=False) + assert _fresh_settings().scraper_proxy_url == "http://scraper-proxy:1234" + + +def test_settings_scraper_env_overrides_avito(monkeypatch): + monkeypatch.setenv("SCRAPER_PROXY_URL", "http://scraper-proxy:1234") + monkeypatch.setenv("AVITO_PROXY_URL", "http://avito-proxy:5678") + assert _fresh_settings().scraper_proxy_url == "http://scraper-proxy:1234" + + +def test_settings_falls_back_to_avito_env(monkeypatch): + monkeypatch.delenv("SCRAPER_PROXY_URL", raising=False) + monkeypatch.setenv("AVITO_PROXY_URL", "http://avito-proxy:5678") + assert _fresh_settings().scraper_proxy_url == "http://avito-proxy:5678" + + +def test_settings_ignores_misnamed_scraper_proxy_url_env(monkeypatch): + """Regression: the old wrong env name SCRAPER_PROXY_URL_ENV must NOT bind.""" + monkeypatch.delenv("SCRAPER_PROXY_URL", raising=False) + monkeypatch.delenv("AVITO_PROXY_URL", raising=False) + monkeypatch.setenv("SCRAPER_PROXY_URL_ENV", "http://wrong-name:1111") + assert _fresh_settings().scraper_proxy_url is None