refactor(api): TOML 配置 SSOT、统一错误契约、Auth/事务加固与可观测性 (#33)
配置 SSOT(TOML + .env) 统一错误契约 Auth 与事务边界 Redis / Celery 可靠性:业务 Redis(DB/0)与 Celery broker/backend(DB/1)显式拆分;连接池、sync client 可观测性(OpenTelemetry + LGTM)
This commit is contained in:
@@ -18,6 +18,7 @@ from app.features.auth.deps import get_auth_service
|
||||
from app.features.auth.router import router as auth_router
|
||||
from app.features.auth.service import AuthService
|
||||
from app.features.user.models import User
|
||||
from tests.conftest import install_test_error_handlers
|
||||
|
||||
|
||||
def _mock_current_user() -> User:
|
||||
@@ -35,7 +36,7 @@ def _mock_current_user() -> User:
|
||||
|
||||
@pytest.fixture
|
||||
def preset_auth_app() -> FastAPI:
|
||||
app = FastAPI()
|
||||
app = install_test_error_handlers(FastAPI())
|
||||
app.include_router(auth_router)
|
||||
|
||||
fixed_user = _mock_current_user()
|
||||
@@ -197,10 +198,9 @@ async def test_upload_avatar_cos_calls_storage_and_presigns(
|
||||
uid = str(uuid.uuid4())
|
||||
public = f"https://{bucket}.cos.{region}.myqcloud.com/avatars/{uid}.jpg"
|
||||
for attr, val in (
|
||||
("tencent_cos_secret_id", "sid"),
|
||||
("tencent_cos_secret_key", "sk"),
|
||||
("tencent_secret_id", "sid"),
|
||||
("tencent_secret_key", "sk"),
|
||||
("tencent_cos_bucket", bucket),
|
||||
("tencent_cos_region", region),
|
||||
("tencent_cos_base_url", f"https://{bucket}.cos.{region}.myqcloud.com"),
|
||||
):
|
||||
monkeypatch.setattr(settings, attr, val, raising=False)
|
||||
@@ -214,19 +214,27 @@ async def test_upload_avatar_cos_calls_storage_and_presigns(
|
||||
fixed_user.id = uid
|
||||
fixed_user.avatar_url = None
|
||||
|
||||
async def _fake_update_avatar(u: str, url: str):
|
||||
async def _fake_upload_avatar(
|
||||
user_id: str,
|
||||
file_content: bytes,
|
||||
content_type: str,
|
||||
*,
|
||||
old_avatar_url: str | None,
|
||||
):
|
||||
_ = (user_id, file_content, content_type, old_avatar_url)
|
||||
url = mock_storage.upload(f"avatars/{uid}.jpg", b"jpeg", "image/jpeg")
|
||||
fixed_user.avatar_url = url
|
||||
return fixed_user
|
||||
|
||||
mock_service = MagicMock(spec=AuthService)
|
||||
mock_service.update_avatar_url = AsyncMock(side_effect=_fake_update_avatar)
|
||||
mock_service.upload_avatar = AsyncMock(side_effect=_fake_upload_avatar)
|
||||
|
||||
app = FastAPI()
|
||||
app = install_test_error_handlers(FastAPI())
|
||||
app.include_router(auth_router)
|
||||
app.dependency_overrides[get_auth_service] = lambda: mock_service
|
||||
app.dependency_overrides[get_current_user] = lambda: fixed_user
|
||||
|
||||
transport = ASGITransport(app=app)
|
||||
transport = ASGITransport(app=app, raise_app_exceptions=False)
|
||||
async with AsyncClient(transport=transport, base_url="http://test") as ac:
|
||||
r = await ac.post(
|
||||
"/api/auth/me/avatar",
|
||||
@@ -235,8 +243,7 @@ async def test_upload_avatar_cos_calls_storage_and_presigns(
|
||||
)
|
||||
|
||||
assert r.status_code == 200
|
||||
assert r.json()["avatar_url"] == "https://example.com/signed-avatar"
|
||||
body = r.json()
|
||||
assert body["avatar_url"] == "https://example.com/signed-avatar"
|
||||
mock_storage.upload.assert_called_once()
|
||||
assert mock_storage.upload.call_args[0][0] == f"avatars/{uid}.jpg"
|
||||
mock_storage.get_url.assert_called_once()
|
||||
assert mock_storage.get_url.call_args[0][0] == f"avatars/{uid}.jpg"
|
||||
|
||||
Reference in New Issue
Block a user