"""pytest 配置文件和 fixtures""" import asyncio import pytest import pytest_asyncio from typing import AsyncGenerator from httpx import AsyncClient, ASGITransport from app.api import create_app from app.core.db import init_db, get_db from app.core.plugin_system.registry import registry from app.plugins import ( Fate0Plugin, ProxyListDownloadPlugin, Ip3366Plugin, Ip89Plugin, KuaiDaiLiPlugin, SpeedXPlugin, YunDaiLiPlugin, ProxyScrapePlugin, ) from app.repositories.proxy_repo import ProxyRepository from app.models.domain import ProxyRaw @pytest_asyncio.fixture(scope="function") async def app(): """创建应用实例""" # 初始化测试数据库并清空历史数据 await init_db() async with get_db() as db: await db.execute("DELETE FROM proxies") await db.execute("DELETE FROM settings") await db.commit() # 清理并重新注册插件,防止跨测试污染 registry.clear() for plugin_cls in [ Fate0Plugin, ProxyListDownloadPlugin, Ip3366Plugin, Ip89Plugin, KuaiDaiLiPlugin, SpeedXPlugin, YunDaiLiPlugin, ProxyScrapePlugin, ]: registry.register(plugin_cls) test_app = create_app() async with test_app.router.lifespan_context(test_app): yield test_app # 给 aiosqlite / aiohttp 后台线程留出收尾时间 await asyncio.sleep(0.1) @pytest_asyncio.fixture async def client(app) -> AsyncGenerator[AsyncClient, None]: """创建异步 HTTP 客户端""" transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as client: yield client @pytest_asyncio.fixture async def db(): """获取数据库连接""" async with get_db() as db: yield db @pytest_asyncio.fixture async def proxy_repo(): """获取代理仓库""" return ProxyRepository() @pytest_asyncio.fixture async def sample_proxy(db, proxy_repo): """创建一个测试代理""" await proxy_repo.insert_or_update(db, "192.168.1.1", 8080, "http", 50) yield {"ip": "192.168.1.1", "port": 8080, "protocol": "http", "score": 50} # 清理 await proxy_repo.delete(db, "192.168.1.1", 8080) @pytest_asyncio.fixture(autouse=True) async def mock_external_requests(monkeypatch): """ 自动在所有测试中 mock 外部网络请求: 1. 插件爬取返回固定测试代理,避免真实 HTTP 请求 2. 代理验证瞬间成功,避免连接超时等待 """ from app.services.plugin_runner import PluginRunner from app.services.validator_service import ValidatorService async def _mock_run(self, plugin): from app.models.domain import CrawlResult return CrawlResult( plugin_name=plugin.name, proxies=[ProxyRaw("192.168.100.10", 8080, "http")], success_count=1, ) async def _mock_validate(self, ip: str, port: int, protocol: str = "http"): return True, 1.23 monkeypatch.setattr(PluginRunner, "run", _mock_run) monkeypatch.setattr(ValidatorService, "validate", _mock_validate)