117 lines
3.4 KiB
Python
117 lines
3.4 KiB
Python
"""pytest 配置 (T6.1).
|
||
|
||
提供 fixture:
|
||
- settings: 内存 SQLite + 安全 JWT 密钥 + 短过期
|
||
- app: FastAPI 实例(lifespan 已运行 → admin 表已建 + bootstrap 用户)
|
||
- client: httpx TestClient
|
||
- auth_token: 登录后的 Bearer JWT
|
||
- auth_headers: {"Authorization": f"Bearer ..."}
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import sys
|
||
from pathlib import Path
|
||
|
||
import pytest
|
||
|
||
# 确保项目根在 sys.path
|
||
_ROOT = Path(__file__).resolve().parent.parent.parent
|
||
if str(_ROOT) not in sys.path:
|
||
sys.path.insert(0, str(_ROOT))
|
||
|
||
|
||
@pytest.fixture
|
||
def secure_secret() -> str:
|
||
"""64-char 安全 JWT 密钥。"""
|
||
return "x" * 64 # 确定性,便于测试断言
|
||
|
||
|
||
@pytest.fixture
|
||
def settings(tmp_path, secure_secret, monkeypatch):
|
||
"""隔离的 Settings 实例:tmp_path SQLite + 安全密钥 + 短过期。
|
||
|
||
用真实文件而不是 :memory: 是因为 admin/db.py 中每次 get_connection
|
||
都新建连接,:memory: 在 SQLite 下不共享状态。
|
||
"""
|
||
db_path = str(tmp_path / "admin.db")
|
||
orders_db_path = str(tmp_path / "orders.db")
|
||
share_db_path = str(tmp_path / "short_links.db")
|
||
share_report_dir = str(tmp_path / "share_reports")
|
||
monkeypatch.setenv("GAOKAO_ENV", "dev")
|
||
monkeypatch.setenv("GAOKAO_DB_PATH", db_path)
|
||
monkeypatch.setenv("GAOKAO_ORDERS_DB_PATH", orders_db_path)
|
||
monkeypatch.setenv("GAOKAO_SHARE_DB_PATH", share_db_path)
|
||
monkeypatch.setenv("GAOKAO_SHARE_REPORT_DIR", share_report_dir)
|
||
monkeypatch.setenv("GAOKAO_ORDERS_FERNET_KEY", "test-secret-for-web-self-service")
|
||
monkeypatch.setenv("GAOKAO_JWT_SECRET", secure_secret)
|
||
monkeypatch.setenv("GAOKAO_JWT_EXP_MIN", "5")
|
||
monkeypatch.setenv("GAOKAO_ADMIN_USER", "admin")
|
||
monkeypatch.setenv("GAOKAO_ADMIN_PASS", "test-pass-123")
|
||
|
||
from admin.config import load_settings
|
||
|
||
return load_settings()
|
||
|
||
|
||
@pytest.fixture
|
||
def orders_db(settings):
|
||
"""T6.2 起:管理后台统计端点会读 orders 表,因此 conftest 顺带建一个
|
||
空 orders DB (T4.1 schema),后续 dashboard 测试可在里面塞 fixture 数据。
|
||
"""
|
||
from data.orders.schema import apply_schema
|
||
|
||
conn = apply_schema(settings.orders_db_path)
|
||
conn.close()
|
||
return settings.orders_db_path
|
||
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def _auto_orders_db(settings, orders_db):
|
||
"""所有测试都默认有空 orders DB 可用,避免 T6.2 端点在不相关测试里
|
||
报 'no such table: orders'。``orders_db`` 显式依赖确保已建。
|
||
"""
|
||
return orders_db
|
||
|
||
|
||
@pytest.fixture(autouse=True)
|
||
def _reset_login_rate_limit():
|
||
from admin.routes.auth import reset_login_rate_limit_for_tests
|
||
|
||
reset_login_rate_limit_for_tests()
|
||
yield
|
||
reset_login_rate_limit_for_tests()
|
||
|
||
|
||
@pytest.fixture
|
||
def app(settings):
|
||
"""FastAPI 实例(lifespan 已运行)。"""
|
||
from admin.app import create_app
|
||
|
||
return create_app(settings)
|
||
|
||
|
||
@pytest.fixture
|
||
def client(app):
|
||
"""httpx TestClient。"""
|
||
from fastapi.testclient import TestClient
|
||
|
||
with TestClient(app) as c:
|
||
yield c
|
||
|
||
|
||
@pytest.fixture
|
||
def auth_token(client) -> str:
|
||
"""登录后返回 Bearer JWT。"""
|
||
resp = client.post(
|
||
"/api/auth/login",
|
||
json={"username": "admin", "password": "test-pass-123"},
|
||
)
|
||
assert resp.status_code == 200, resp.text
|
||
return resp.json()["access_token"]
|
||
|
||
|
||
@pytest.fixture
|
||
def auth_headers(auth_token) -> dict:
|
||
return {"Authorization": f"Bearer {auth_token}"}
|