refactor: align backend layout and supabase infra
Consolidate backend modules/tests under the backend package while syncing Supabase compose/env config and related plans.
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Callable
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import HTTPException
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app import app
|
||||
from core.auth.models import CurrentUser
|
||||
from v1.profile.dependencies import get_current_user, get_profile_service
|
||||
from v1.profile.schemas import ProfileResponse, ProfileUpdateRequest
|
||||
from v1.profile.service import ProfileService
|
||||
|
||||
|
||||
class FakeProfileService:
|
||||
"""Fake service for integration testing."""
|
||||
|
||||
def __init__(self, profile: ProfileResponse) -> None:
|
||||
self._profile = profile
|
||||
|
||||
async def get_me(self) -> ProfileResponse:
|
||||
if self._profile.id is None:
|
||||
raise HTTPException(status_code=404, detail="Profile not found")
|
||||
return self._profile
|
||||
|
||||
async def update_me(self, update: ProfileUpdateRequest) -> ProfileResponse:
|
||||
if self._profile.id is None:
|
||||
raise HTTPException(status_code=404, detail="Profile not found")
|
||||
return ProfileResponse(
|
||||
id=self._profile.id,
|
||||
username=self._profile.username,
|
||||
display_name=(
|
||||
update.display_name
|
||||
if update.display_name is not None
|
||||
else self._profile.display_name
|
||||
),
|
||||
avatar_url=(
|
||||
update.avatar_url
|
||||
if update.avatar_url is not None
|
||||
else self._profile.avatar_url
|
||||
),
|
||||
bio=update.bio if update.bio is not None else self._profile.bio,
|
||||
)
|
||||
|
||||
async def get_by_username(self, username: str) -> ProfileResponse:
|
||||
if username != self._profile.username:
|
||||
raise HTTPException(status_code=404, detail="Profile not found")
|
||||
return self._profile
|
||||
|
||||
|
||||
def _override_profile_service(
|
||||
service: FakeProfileService,
|
||||
) -> Callable[[], ProfileService]:
|
||||
def _get_service() -> ProfileService:
|
||||
return service # type: ignore[return-value]
|
||||
|
||||
return _get_service
|
||||
|
||||
|
||||
def _override_current_user(user_id: UUID) -> Callable[[], CurrentUser]:
|
||||
def _get_user() -> CurrentUser:
|
||||
return CurrentUser(id=user_id)
|
||||
|
||||
return _get_user
|
||||
|
||||
|
||||
def test_get_me_returns_profile() -> None:
|
||||
user_id = UUID("00000000-0000-0000-0000-000000000001")
|
||||
profile = ProfileResponse(
|
||||
id=str(user_id),
|
||||
username="demo",
|
||||
display_name="Demo User",
|
||||
avatar_url=None,
|
||||
bio=None,
|
||||
)
|
||||
app.dependency_overrides[get_profile_service] = _override_profile_service(
|
||||
FakeProfileService(profile)
|
||||
)
|
||||
app.dependency_overrides[get_current_user] = _override_current_user(user_id)
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.get("/api/v1/profile/me")
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["username"] == "demo"
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_patch_me_updates_profile() -> None:
|
||||
user_id = UUID("00000000-0000-0000-0000-000000000001")
|
||||
profile = ProfileResponse(
|
||||
id=str(user_id),
|
||||
username="demo",
|
||||
display_name="Demo User",
|
||||
avatar_url=None,
|
||||
bio=None,
|
||||
)
|
||||
app.dependency_overrides[get_profile_service] = _override_profile_service(
|
||||
FakeProfileService(profile)
|
||||
)
|
||||
app.dependency_overrides[get_current_user] = _override_current_user(user_id)
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.patch(
|
||||
"/api/v1/profile/me",
|
||||
json={"display_name": "Updated"},
|
||||
)
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["display_name"] == "Updated"
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_get_profile_by_username() -> None:
|
||||
profile = ProfileResponse(
|
||||
id="00000000-0000-0000-0000-000000000001",
|
||||
username="demo",
|
||||
display_name="Demo User",
|
||||
avatar_url=None,
|
||||
bio=None,
|
||||
)
|
||||
app.dependency_overrides[get_profile_service] = _override_profile_service(
|
||||
FakeProfileService(profile)
|
||||
)
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.get("/api/v1/profile/demo")
|
||||
assert response.status_code == 200
|
||||
body = response.json()
|
||||
assert body["username"] == "demo"
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_profile_not_found_returns_problem_details() -> None:
|
||||
profile = ProfileResponse(
|
||||
id="00000000-0000-0000-0000-000000000001",
|
||||
username="demo",
|
||||
display_name="Demo User",
|
||||
avatar_url=None,
|
||||
bio=None,
|
||||
)
|
||||
app.dependency_overrides[get_profile_service] = _override_profile_service(
|
||||
FakeProfileService(profile)
|
||||
)
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.get("/api/v1/profile/unknown")
|
||||
assert response.status_code == 404
|
||||
assert response.headers["content-type"].startswith("application/problem+json")
|
||||
body = response.json()
|
||||
assert body["title"] == "Not Found"
|
||||
assert body["status"] == 404
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
|
||||
|
||||
def test_patch_me_validation_error_returns_problem_details() -> None:
|
||||
user_id = UUID("00000000-0000-0000-0000-000000000001")
|
||||
profile = ProfileResponse(
|
||||
id=str(user_id),
|
||||
username="demo",
|
||||
display_name="Demo User",
|
||||
avatar_url=None,
|
||||
bio=None,
|
||||
)
|
||||
app.dependency_overrides[get_profile_service] = _override_profile_service(
|
||||
FakeProfileService(profile)
|
||||
)
|
||||
app.dependency_overrides[get_current_user] = _override_current_user(user_id)
|
||||
|
||||
client = TestClient(app)
|
||||
try:
|
||||
response = client.patch("/api/v1/profile/me", json={})
|
||||
assert response.status_code == 422
|
||||
assert response.headers["content-type"].startswith("application/problem+json")
|
||||
body = response.json()
|
||||
assert body["title"] == "Unprocessable Content"
|
||||
assert body["status"] == 422
|
||||
finally:
|
||||
app.dependency_overrides = {}
|
||||
Reference in New Issue
Block a user