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
153 lines
6.6 KiB
Python
153 lines
6.6 KiB
Python
"""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")]
|