"""测试夹具:每个测试用独立的临时 SQLite 库。""" import os import tempfile from pathlib import Path # 必须在导入 app 之前设置环境变量(config 是模块级单例) _tmpdir = tempfile.mkdtemp(prefix="item-bank-test-") os.environ["DATABASE_URL"] = f"sqlite+aiosqlite:///{_tmpdir}/test.db" os.environ["DEBUG"] = "false" os.environ["RATE_LIMIT_ENABLED"] = "false" # 默认关掉,限流测试里单独开 # 需满足 config.check_production_secrets 的要求(≥32 字节、非默认值) os.environ["JWT_SECRET"] = "test-secret-long-enough-for-hs256-abcdefghij" import pytest # noqa: E402 import pytest_asyncio # noqa: E402 from cryptography.fernet import Fernet # noqa: E402 from httpx import ASGITransport, AsyncClient # noqa: E402 # 生成合法的 Fernet key 并在导入 app 前写入 os.environ["FERNET_KEY"] = Fernet.generate_key().decode() import app.models # noqa: E402,F401 注册所有模型 from app.db import Base, engine # noqa: E402 from app.main import app as fastapi_app # noqa: E402 @pytest_asyncio.fixture async def db_schema(): """每个测试前重建表,测试间互不干扰。""" async with engine.begin() as conn: await conn.run_sync(Base.metadata.drop_all) await conn.run_sync(Base.metadata.create_all) yield @pytest_asyncio.fixture async def client(db_schema): transport = ASGITransport(app=fastapi_app) async with AsyncClient(transport=transport, base_url="http://test") as c: yield c @pytest_asyncio.fixture async def auth_headers(client): """注册并登录一个用户,返回可直接用的鉴权头。""" async def _make(username: str = "alice", password: str = "secret123") -> dict: await client.post( "/api/auth/register", json={"username": username, "password": password} ) resp = await client.post( "/api/auth/login", data={"username": username, "password": password} ) token = resp.json()["access_token"] return {"Authorization": f"Bearer {token}"} return _make @pytest_asyncio.fixture async def configure_ai(client): """给某个用户配好 AI 接口(api_key 会被加密存储)。""" async def _make(headers: dict, api_key: str = "sk-test-value") -> dict: resp = await client.put( "/api/settings", json={ "llm_base_url": "http://fake-llm", "llm_text_model": "text-model", "llm_vision_model": "vision-model", "api_key": api_key, }, headers=headers, ) assert resp.status_code == 200 return resp.json() return _make def pytest_configure(config): config.addinivalue_line("markers", "asyncio: async test")