Files
surugaya/tests/test_browser_recycle.py
T
2026-06-08 16:31:57 +08:00

117 lines
3.5 KiB
Python

"""浏览器池主动回收逻辑的单元测试(不启动真实 Chromium)。"""
from __future__ import annotations
import asyncio
import pytest
from app.core.config import Settings
from app.services.browser_pool import BrowserPool
class _FakeBrowser:
"""最小化的浏览器替身,仅实现连接状态查询。"""
def __init__(self, connected: bool = True):
self._connected = connected
def is_connected(self) -> bool:
return self._connected
def _make_pool(**overrides) -> BrowserPool:
settings = Settings(**overrides)
pool = BrowserPool(settings)
pool._browser = _FakeBrowser()
pool._started = True
return pool
@pytest.mark.asyncio
async def test_recycle_due_by_context_count():
pool = _make_pool(browser_recycle_after_contexts=10, browser_recycle_after_seconds=0)
pool._browser_started_at = asyncio.get_running_loop().time()
pool._contexts_since_start = 9
assert pool._recycle_due() is False
pool._contexts_since_start = 10
assert pool._recycle_due() is True
@pytest.mark.asyncio
async def test_recycle_due_by_age():
pool = _make_pool(browser_recycle_after_contexts=0, browser_recycle_after_seconds=100)
now = asyncio.get_running_loop().time()
pool._browser_started_at = now - 99
assert pool._recycle_due() is False
pool._browser_started_at = now - 101
assert pool._recycle_due() is True
@pytest.mark.asyncio
async def test_recycle_disabled_when_thresholds_zero():
pool = _make_pool(browser_recycle_after_contexts=0, browser_recycle_after_seconds=0)
pool._browser_started_at = asyncio.get_running_loop().time() - 99999
pool._contexts_since_start = 99999
assert pool._recycle_due() is False
@pytest.mark.asyncio
async def test_maybe_recycle_rebuilds_when_idle(monkeypatch):
pool = _make_pool(browser_recycle_after_contexts=10, browser_recycle_after_seconds=0)
pool._browser_started_at = asyncio.get_running_loop().time()
pool._contexts_since_start = 10
pool._active_contexts = 0
calls: list[str] = []
async def fake_close():
calls.append("close")
async def fake_start():
calls.append("start")
pool._browser = _FakeBrowser()
pool._started = True
pool._contexts_since_start = 0 # start() 正常会重置计数
monkeypatch.setattr(pool, "close", fake_close)
monkeypatch.setattr(pool, "start", fake_start)
await pool._maybe_recycle()
assert calls == ["close", "start"]
@pytest.mark.asyncio
async def test_maybe_recycle_skips_when_busy(monkeypatch):
pool = _make_pool(browser_recycle_after_contexts=10, browser_recycle_after_seconds=0)
pool._browser_started_at = asyncio.get_running_loop().time()
pool._contexts_since_start = 10
pool._active_contexts = 1 # 仍有会话在用,不应回收
calls: list[str] = []
monkeypatch.setattr(pool, "close", lambda: calls.append("close"))
monkeypatch.setattr(pool, "start", lambda: calls.append("start"))
await pool._maybe_recycle()
assert calls == []
@pytest.mark.asyncio
async def test_maybe_recycle_noop_when_not_due(monkeypatch):
pool = _make_pool(browser_recycle_after_contexts=10, browser_recycle_after_seconds=0)
pool._browser_started_at = asyncio.get_running_loop().time()
pool._contexts_since_start = 3
calls: list[str] = []
monkeypatch.setattr(pool, "close", lambda: calls.append("close"))
monkeypatch.setattr(pool, "start", lambda: calls.append("start"))
await pool._maybe_recycle()
assert calls == []