from __future__ import annotations from dataclasses import dataclass from uuid import uuid4 import pytest from core.auth.models import CurrentUser from v1.users.schemas import UserUpdateRequest from v1.users.service import UserService @dataclass class _FakeProfile: id: object username: str avatar_url: str | None bio: str | None class _FakeRepository: def __init__(self, profile: _FakeProfile | None) -> None: self._profile = profile self.update_calls: list[tuple[object, dict[str, str | None]]] = [] async def update_by_user_id( self, user_id: object, update_data: dict[str, str | None] ): self.update_calls.append((user_id, update_data)) if self._profile is None: return None return _FakeProfile( id=self._profile.id, username=update_data.get("username") or self._profile.username, avatar_url=update_data.get("avatar_url") if "avatar_url" in update_data else self._profile.avatar_url, bio=update_data.get("bio") if "bio" in update_data else self._profile.bio, ) class _FakeSession: def __init__(self) -> None: self.commit_called = 0 self.rollback_called = 0 async def commit(self) -> None: self.commit_called += 1 async def rollback(self) -> None: self.rollback_called += 1 class _FakeUserContextCache: def __init__(self, *, should_fail: bool = False) -> None: self.should_fail = should_fail self.invalidated_user_ids: list[object] = [] async def invalidate_user(self, *, user_id: object) -> int: self.invalidated_user_ids.append(user_id) if self.should_fail: raise RuntimeError("cache down") return 1 @pytest.mark.asyncio async def test_update_me_invalidates_user_context_cache() -> None: user_id = uuid4() repo = _FakeRepository( _FakeProfile(id=user_id, username="old", avatar_url=None, bio=None) ) session = _FakeSession() cache = _FakeUserContextCache() service = UserService( repository=repo, session=session, # type: ignore[arg-type] current_user=CurrentUser(id=user_id), user_context_cache=cache, # type: ignore[arg-type] ) result = await service.update_me(UserUpdateRequest(username="new-name")) assert result.username == "new-name" assert session.commit_called == 1 assert cache.invalidated_user_ids == [user_id] @pytest.mark.asyncio async def test_update_me_succeeds_when_cache_invalidation_fails() -> None: user_id = uuid4() repo = _FakeRepository( _FakeProfile(id=user_id, username="old", avatar_url=None, bio=None) ) session = _FakeSession() cache = _FakeUserContextCache(should_fail=True) service = UserService( repository=repo, session=session, # type: ignore[arg-type] current_user=CurrentUser(id=user_id), user_context_cache=cache, # type: ignore[arg-type] ) result = await service.update_me(UserUpdateRequest(username="new-name")) assert result.username == "new-name" assert session.commit_called == 1 assert cache.invalidated_user_ids == [user_id]