gendesign/tradein-mvp/browser/test_server_proxy_override.py
lekss361 ca473a0012
All checks were successful
Deploy Trade-In / build-browser (push) Successful in 2m13s
Deploy Trade-In / build-backend (push) Successful in 1m18s
Deploy Trade-In / deploy (push) Successful in 2m29s
Deploy Trade-In / changes (push) Successful in 13s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 1m32s
feat(proxy-pool): P4 — browser dynamic pool-proxy (acquire→/fetch→relaunch) + orchestration wiring, ship-dark (#2164)
2026-07-02 19:20:29 +00:00

153 lines
6.6 KiB
Python
Raw 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_proxy_override.py — динамический прокси из тела /fetch (#2164 P4).
Проверяет:
1. _resolve_proxy_override — берёт body["proxy"] (kind http/https), None если нет /
если proxy_kind несовместим (socks5 → camoufox не умеет);
2. политика relaunch «только при реальной смене прокси» через _ensure_browser:
- инстанса нет → launch с proxy_override;
- тот же прокси → НЕ релончим (launch не зовётся повторно);
- другой прокси → close + relaunch с новым;
- proxy_override=None + инстанс на env → НЕ релончим (golden-parity).
camoufox НЕ запускается: _launch_browser мокается заглушкой, которая ставит
_browsers/_launched_proxy как реальный launch.
Запуск (из tradein-mvp/browser/)::
python -m pytest test_server_proxy_override.py -q
"""
from __future__ import annotations
import asyncio
import importlib.util
from pathlib import Path
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, "_browsers", {})
monkeypatch.setattr(server, "_browser_cms", {})
monkeypatch.setattr(server, "_page_counters", {})
monkeypatch.setattr(server, "_locks", {})
monkeypatch.setattr(server, "_retry_tasks", {})
monkeypatch.setattr(server, "_last_goto_at", {})
monkeypatch.setattr(server, "_launched_proxy", {})
monkeypatch.setattr(server, "_locks_guard", asyncio.Lock())
# ── _resolve_proxy_override ──────────────────────────────────────────────────────
def test_resolve_proxy_override_http() -> None:
body = {"proxy": "http://u:p@pool:8080", "proxy_kind": "http"}
assert server._resolve_proxy_override(body, "avito") == "http://u:p@pool:8080"
def test_resolve_proxy_override_missing_returns_none() -> None:
assert server._resolve_proxy_override({"url": "x"}, "avito") is None
assert server._resolve_proxy_override({"proxy": ""}, "avito") is None
def test_resolve_proxy_override_kind_defaults_http_when_absent() -> None:
"""proxy без proxy_kind трактуем как http (в пуле kind=http)."""
assert server._resolve_proxy_override({"proxy": "http://pool:8080"}, "cian") == (
"http://pool:8080"
)
def test_resolve_proxy_override_socks5_ignored() -> None:
"""socks5 camoufox не умеет → None (env-fallback)."""
body = {"proxy": "socks5://u:p@pool:1080", "proxy_kind": "socks5"}
assert server._resolve_proxy_override(body, "avito") is None
# ── relaunch policy: _ensure_browser ─────────────────────────────────────────────
def _install_fake_launch(monkeypatch: pytest.MonkeyPatch) -> list[tuple[str, str | None]]:
"""Мок _launch_browser: пишет _browsers/_launched_proxy как реальный, пишет calls."""
calls: list[tuple[str, str | None]] = []
async def _fake_launch(provider: str, proxy_override: str | None = None) -> None:
calls.append((provider, proxy_override))
server._browsers[provider] = object()
server._launched_proxy[provider] = (
proxy_override if proxy_override is not None else server._provider_proxy(provider)
)
monkeypatch.setattr(server, "_launch_browser", _fake_launch)
return calls
def test_ensure_browser_launches_with_override(monkeypatch: pytest.MonkeyPatch) -> None:
calls = _install_fake_launch(monkeypatch)
async def _run() -> bool:
return await server._ensure_browser("avito", proxy_override="http://p1:8080")
ok = asyncio.run(_run())
assert ok is True
assert calls == [("avito", "http://p1:8080")]
assert server._launched_proxy["avito"] == "http://p1:8080"
def test_ensure_browser_same_proxy_no_relaunch(monkeypatch: pytest.MonkeyPatch) -> None:
calls = _install_fake_launch(monkeypatch)
async def _run() -> None:
await server._ensure_browser("avito", proxy_override="http://p1:8080")
await server._ensure_browser("avito", proxy_override="http://p1:8080")
asyncio.run(_run())
# Только один launch — второй вызов с тем же прокси НЕ релончит.
assert calls == [("avito", "http://p1:8080")]
def test_ensure_browser_changed_proxy_relaunches(monkeypatch: pytest.MonkeyPatch) -> None:
calls = _install_fake_launch(monkeypatch)
async def _run() -> None:
await server._ensure_browser("avito", proxy_override="http://p1:8080")
await server._ensure_browser("avito", proxy_override="http://p2:8080")
asyncio.run(_run())
assert calls == [("avito", "http://p1:8080"), ("avito", "http://p2:8080")]
assert server._launched_proxy["avito"] == "http://p2:8080"
def test_ensure_browser_none_override_env_no_relaunch(monkeypatch: pytest.MonkeyPatch) -> None:
"""proxy_override=None (флаг off) + инстанс на env → НЕ релончим (golden-parity)."""
for var in ("BROWSER_PROXY_AVITO", "AVITO_PROXY_URL"):
monkeypatch.setenv(var, "http://env:8080")
calls = _install_fake_launch(monkeypatch)
async def _run() -> None:
await server._ensure_browser("avito") # lazy launch на env
await server._ensure_browser("avito") # второй раз — env тот же
asyncio.run(_run())
assert calls == [("avito", None)]
assert server._launched_proxy["avito"] == "http://env:8080"
def test_ensure_browser_env_then_pool_relaunches(monkeypatch: pytest.MonkeyPatch) -> None:
"""env-launched инстанс + пришёл pool-proxy → relaunch (реальная смена)."""
for var in ("BROWSER_PROXY_AVITO", "AVITO_PROXY_URL"):
monkeypatch.setenv(var, "http://env:8080")
calls = _install_fake_launch(monkeypatch)
async def _run() -> None:
await server._ensure_browser("avito") # env
await server._ensure_browser("avito", proxy_override="http://pool:8080") # смена
asyncio.run(_run())
assert calls == [("avito", None), ("avito", "http://pool:8080")]