2026-04-08 15:37:09 +08:00
|
|
|
|
"""pytest 共享fixtures 与约定入口(可按 backend-testing-strategy 逐步扩展)。"""
|
|
|
|
|
|
|
|
|
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
2026-05-22 13:44:50 +08:00
|
|
|
|
pytest_plugins = ["tests.support.auth_async_sqlite"]
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
import pytest
|
|
|
|
|
|
|
|
|
|
|
|
# TOML SSOT:OTEL_ENABLED env 已无效;在 pytest 最早阶段关闭 deploy.otel_enabled。
|
|
|
|
|
|
os.environ.setdefault("APP_ENV", "development")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pytest_configure(config: pytest.Config) -> None:
|
|
|
|
|
|
from app.core.app_config import get_app_config
|
|
|
|
|
|
|
|
|
|
|
|
get_app_config().deploy.otel_enabled = False
|
|
|
|
|
|
|
2026-04-08 15:37:09 +08:00
|
|
|
|
import uuid
|
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
|
from typing import Callable
|
|
|
|
|
|
|
2026-05-22 13:44:50 +08:00
|
|
|
|
from fastapi import FastAPI
|
2026-04-08 15:37:09 +08:00
|
|
|
|
|
|
|
|
|
|
from app.features.user.models import User
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-05-22 13:44:50 +08:00
|
|
|
|
def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None:
|
|
|
|
|
|
from app.core.telemetry import shutdown_telemetry
|
|
|
|
|
|
|
|
|
|
|
|
shutdown_telemetry()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def install_test_error_handlers(app: FastAPI) -> FastAPI:
|
|
|
|
|
|
"""为最小测试 FastAPI 应用挂载与生产一致的异常处理。"""
|
|
|
|
|
|
from app.core.errors import register_exception_handlers
|
|
|
|
|
|
from app.core.middleware import RequestIdMiddleware
|
|
|
|
|
|
|
|
|
|
|
|
app.add_middleware(RequestIdMiddleware)
|
|
|
|
|
|
register_exception_handlers(app)
|
|
|
|
|
|
return app
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-04-08 15:37:09 +08:00
|
|
|
|
@pytest.fixture
|
|
|
|
|
|
def unique_phone() -> str:
|
|
|
|
|
|
"""避免与测试库中已存在手机号冲突(11 位)。"""
|
|
|
|
|
|
return f"138{uuid.uuid4().int % 100_000_000:08d}"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@pytest.fixture
|
|
|
|
|
|
def make_test_user(unique_phone: str) -> Callable[..., User]:
|
|
|
|
|
|
"""工厂:构造仅用于响应序列化的 User 行(未入库)。"""
|
|
|
|
|
|
|
|
|
|
|
|
def _make(
|
|
|
|
|
|
*,
|
|
|
|
|
|
user_id: str | None = None,
|
|
|
|
|
|
phone: str | None = None,
|
|
|
|
|
|
nickname: str = "测试用户",
|
|
|
|
|
|
) -> User:
|
|
|
|
|
|
return User(
|
|
|
|
|
|
id=user_id or str(uuid.uuid4()),
|
|
|
|
|
|
phone=phone or unique_phone,
|
|
|
|
|
|
password_hash="x",
|
|
|
|
|
|
nickname=nickname,
|
|
|
|
|
|
subscription_type="free",
|
|
|
|
|
|
created_at=datetime.now(timezone.utc),
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
return _make
|