feat(agent): migrate to native CrewAI tool loop and async resume enqueue
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
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]
|
||||
Reference in New Issue
Block a user