test: 更新 AgentScope 相关单元测试与集成测试
- 重命名 test_react_runner.py 为 test_runner.py - 新增 test_utils.py 测试工具函数 - 更新现有测试用例适配新架构
This commit is contained in:
@@ -45,6 +45,12 @@ async def test_snapshot_message_returns_raw_db_columns() -> None:
|
||||
seq=7,
|
||||
role=AgentChatMessageRole.TOOL,
|
||||
content='{"offloaded":true}',
|
||||
model_code=None,
|
||||
tool_name=None,
|
||||
input_tokens=0,
|
||||
output_tokens=0,
|
||||
cost=0,
|
||||
latency_ms=None,
|
||||
metadata_json={"tool_call_id": "call-1"},
|
||||
created_at=now,
|
||||
)
|
||||
@@ -71,8 +77,7 @@ async def test_persist_user_message_sets_session_title_when_empty() -> None:
|
||||
|
||||
await repository.persist_user_message(
|
||||
session_id=session_id,
|
||||
run_id="run-1",
|
||||
content_text=" 请帮我安排明天下午开会 ",
|
||||
content=" 请帮我安排明天下午开会 ",
|
||||
metadata=None,
|
||||
)
|
||||
|
||||
@@ -94,10 +99,68 @@ async def test_persist_user_message_keeps_existing_session_title() -> None:
|
||||
|
||||
await repository.persist_user_message(
|
||||
session_id=session_id,
|
||||
run_id="run-2",
|
||||
content_text="新的消息内容",
|
||||
content="新的消息内容",
|
||||
metadata=None,
|
||||
)
|
||||
|
||||
assert session_row.title == "已有标题"
|
||||
assert session_row.message_count == 2
|
||||
|
||||
|
||||
class _ScalarRows:
|
||||
def __init__(self, rows: list[object]) -> None:
|
||||
self._rows = rows
|
||||
|
||||
def all(self) -> list[object]:
|
||||
return self._rows
|
||||
|
||||
|
||||
class _ExecuteRowsResult:
|
||||
def __init__(self, rows: list[object]) -> None:
|
||||
self._rows = rows
|
||||
|
||||
def scalars(self) -> _ScalarRows:
|
||||
return _ScalarRows(self._rows)
|
||||
|
||||
|
||||
class _FakeHistorySession:
|
||||
def __init__(self) -> None:
|
||||
self._execute_count = 0
|
||||
|
||||
async def execute(self, stmt): # noqa: ANN001
|
||||
del stmt
|
||||
self._execute_count += 1
|
||||
if self._execute_count == 1:
|
||||
return _ExecuteResult(datetime(2026, 3, 16, 11, 0, tzinfo=timezone.utc))
|
||||
if self._execute_count == 2:
|
||||
message = SimpleNamespace(
|
||||
id=uuid4(),
|
||||
seq=1,
|
||||
role=AgentChatMessageRole.USER,
|
||||
content="hello",
|
||||
model_code=None,
|
||||
tool_name=None,
|
||||
input_tokens=0,
|
||||
output_tokens=0,
|
||||
cost=0,
|
||||
latency_ms=None,
|
||||
metadata_json=None,
|
||||
created_at=datetime(2026, 3, 16, 11, 0, tzinfo=timezone.utc),
|
||||
)
|
||||
return _ExecuteRowsResult([message])
|
||||
return _ExecuteResult(uuid4())
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_history_day_uses_target_day_queries_only() -> None:
|
||||
session = _FakeHistorySession()
|
||||
repository = AgentRepository(session=session) # type: ignore[arg-type]
|
||||
|
||||
payload = await repository.get_history_day(session_id=str(uuid4()), before=None)
|
||||
|
||||
assert payload is not None
|
||||
assert payload["day"] == "2026-03-16"
|
||||
assert payload["hasMore"] is True
|
||||
messages = payload["messages"]
|
||||
assert isinstance(messages, list)
|
||||
assert len(messages) == 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
from typing import cast
|
||||
from urllib.parse import quote
|
||||
from uuid import UUID
|
||||
|
||||
@@ -11,6 +12,7 @@ import pytest
|
||||
import v1.agent.service as agent_service_module
|
||||
from core.auth.models import CurrentUser
|
||||
from core.config.settings import config
|
||||
from schemas.messages.chat_message import AgentChatMessageMetadata
|
||||
from v1.agent.service import AgentService
|
||||
|
||||
|
||||
@@ -50,15 +52,13 @@ class _FakeRepository:
|
||||
self,
|
||||
*,
|
||||
session_id: str,
|
||||
run_id: str,
|
||||
content_text: str,
|
||||
metadata: dict[str, object] | None,
|
||||
content: str,
|
||||
metadata: AgentChatMessageMetadata | None,
|
||||
) -> None:
|
||||
self.persisted_user_messages.append(
|
||||
{
|
||||
"session_id": session_id,
|
||||
"run_id": run_id,
|
||||
"content_text": content_text,
|
||||
"content": content,
|
||||
"metadata": metadata,
|
||||
}
|
||||
)
|
||||
@@ -199,12 +199,17 @@ async def test_enqueue_run_persists_attachment_and_queue_without_user_token(
|
||||
|
||||
assert accepted.task_id == "task-1"
|
||||
persisted = repository.persisted_user_messages[0]
|
||||
metadata = persisted["metadata"]
|
||||
assert isinstance(metadata, dict)
|
||||
attachment = metadata["user_message_attachments"]
|
||||
assert attachment["bucket"] == "agent-test-bucket"
|
||||
metadata = cast(AgentChatMessageMetadata | None, persisted["metadata"])
|
||||
assert metadata is not None
|
||||
attachment = metadata.user_message_attachments
|
||||
assert attachment is not None
|
||||
assert attachment.bucket == "agent-test-bucket"
|
||||
command = queue.commands[0]
|
||||
assert "user_token" not in command
|
||||
run_input = command["run_input"]
|
||||
assert isinstance(run_input, dict)
|
||||
assert run_input["threadId"] == "00000000-0000-0000-0000-000000000001"
|
||||
assert run_input["runId"] == "run-1"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from uuid import uuid4
|
||||
|
||||
from v1.agent.utils import convert_message_to_history
|
||||
|
||||
|
||||
class _FakeMessage:
|
||||
def __init__(self, *, role: str, metadata: dict[str, object] | None) -> None:
|
||||
self.id = uuid4()
|
||||
self.seq = 1
|
||||
self.role = role
|
||||
self.content = "content"
|
||||
self.metadata = metadata
|
||||
self.timestamp = datetime.now(timezone.utc)
|
||||
|
||||
|
||||
def test_convert_message_to_history_uses_ui_schema_key_for_tool_message() -> None:
|
||||
message = _FakeMessage(
|
||||
role="tool",
|
||||
metadata={
|
||||
"tool_agent_output": {
|
||||
"ui_schema": {"version": "2.0", "root": {"type": "stack"}}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = convert_message_to_history(message) # type: ignore[arg-type]
|
||||
|
||||
assert "ui_schema" in result
|
||||
assert "uiSchema" not in result
|
||||
assert result["ui_schema"] == {"version": "2.0", "root": {"type": "stack"}}
|
||||
|
||||
|
||||
def test_convert_message_to_history_uses_ui_schema_key_for_assistant_message() -> None:
|
||||
message = _FakeMessage(
|
||||
role="assistant",
|
||||
metadata={
|
||||
"worker_agent_output": {
|
||||
"ui_schema": {"version": "2.0", "root": {"type": "stack"}}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
result = convert_message_to_history(message) # type: ignore[arg-type]
|
||||
|
||||
assert "ui_schema" in result
|
||||
assert "uiSchema" not in result
|
||||
assert result["ui_schema"] == {"version": "2.0", "root": {"type": "stack"}}
|
||||
@@ -340,3 +340,31 @@ class TestSupabaseAuthGateway:
|
||||
|
||||
assert exc_info.value.status_code == 503
|
||||
assert exc_info.value.detail == "Auth service temporarily unavailable"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_get_user_by_email_uses_in_memory_cache(
|
||||
self,
|
||||
gateway: tuple[SupabaseAuthGateway, MagicMock, MagicMock],
|
||||
monkeypatch: pytest.MonkeyPatch,
|
||||
) -> None:
|
||||
sut, _, _ = gateway
|
||||
user = SimpleNamespace(
|
||||
id="user-1",
|
||||
email="cached@example.com",
|
||||
created_at="2026-03-16T00:00:00Z",
|
||||
email_confirmed_at=None,
|
||||
)
|
||||
list_calls = {"count": 0}
|
||||
|
||||
def _fake_list_auth_users(_client: object) -> list[SimpleNamespace]:
|
||||
list_calls["count"] += 1
|
||||
return [user]
|
||||
|
||||
monkeypatch.setattr("v1.auth.gateway._list_auth_users", _fake_list_auth_users)
|
||||
|
||||
first = await sut.get_user_by_email("cached@example.com")
|
||||
second = await sut.get_user_by_email("CACHED@example.com")
|
||||
|
||||
assert first.id == "user-1"
|
||||
assert second.email == "cached@example.com"
|
||||
assert list_calls["count"] == 1
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
from typing import cast
|
||||
from unittest.mock import AsyncMock, MagicMock
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
@@ -10,6 +11,7 @@ from fastapi import HTTPException
|
||||
from core.auth.models import CurrentUser
|
||||
from models.friendships import Friendship, FriendshipStatus
|
||||
from models.inbox_messages import InboxMessage, InboxMessageStatus, InboxMessageType
|
||||
from models.profile import Profile
|
||||
from v1.friendships.repository import FriendshipRepository
|
||||
from v1.friendships.schemas import (
|
||||
FriendRequestCreate,
|
||||
@@ -22,14 +24,14 @@ def _create_mock_profile(
|
||||
user_id: UUID = UUID("00000000-0000-0000-0000-000000000001"),
|
||||
username: str = "testuser",
|
||||
avatar_url: str | None = None,
|
||||
) -> MagicMock:
|
||||
) -> Profile:
|
||||
"""Create a mock Profile ORM object."""
|
||||
profile = MagicMock()
|
||||
profile.id = user_id
|
||||
profile.username = username
|
||||
profile.avatar_url = avatar_url
|
||||
profile.bio = None
|
||||
return profile
|
||||
return cast(Profile, profile)
|
||||
|
||||
|
||||
class FakeFriendshipRepo:
|
||||
@@ -65,7 +67,7 @@ class FakeFriendshipRepo:
|
||||
inbox.status = InboxMessageStatus.PENDING
|
||||
inbox.message_type = InboxMessageType.FRIEND_REQUEST
|
||||
inbox.friendship_id = friendship.id
|
||||
inbox.content = content
|
||||
inbox.content = {"type": "request", "message": content}
|
||||
self._inbox_messages.append(inbox)
|
||||
|
||||
return friendship, inbox
|
||||
@@ -92,7 +94,7 @@ class FakeFriendshipRepo:
|
||||
inbox.status = InboxMessageStatus.PENDING
|
||||
inbox.message_type = InboxMessageType.FRIEND_REQUEST
|
||||
inbox.friendship_id = friendship.id
|
||||
inbox.content = content
|
||||
inbox.content = {"type": "request", "message": content}
|
||||
self._inbox_messages.append(inbox)
|
||||
|
||||
return friendship, inbox
|
||||
@@ -121,6 +123,16 @@ class FakeFriendshipRepo:
|
||||
return f
|
||||
return None
|
||||
|
||||
async def get_friendships_by_ids(
|
||||
self, friendship_ids: list[UUID]
|
||||
) -> dict[UUID, Friendship]:
|
||||
friendship_set = set(friendship_ids)
|
||||
return {
|
||||
f.id: f
|
||||
for f in self._friendships
|
||||
if getattr(f, "id", None) in friendship_set
|
||||
}
|
||||
|
||||
async def get_inbox_messages_for_user(
|
||||
self, user_id: UUID, status: InboxMessageStatus | None = None
|
||||
) -> list[InboxMessage]:
|
||||
@@ -148,12 +160,41 @@ class FakeFriendshipRepo:
|
||||
class FakeUserRepo:
|
||||
"""Fake user repository for testing."""
|
||||
|
||||
def __init__(self, profiles: dict[UUID, MagicMock] | None = None) -> None:
|
||||
def __init__(self, profiles: dict[UUID, Profile] | None = None) -> None:
|
||||
self._profiles = profiles or {}
|
||||
|
||||
async def get_by_user_id(self, user_id: UUID) -> MagicMock | None:
|
||||
async def get_by_user_id(self, user_id: UUID) -> Profile | None:
|
||||
return self._profiles.get(user_id)
|
||||
|
||||
async def get_by_user_ids(self, user_ids: list[UUID]) -> dict[UUID, Profile]:
|
||||
user_id_set = set(user_ids)
|
||||
return {
|
||||
uid: profile
|
||||
for uid, profile in self._profiles.items()
|
||||
if uid in user_id_set
|
||||
}
|
||||
|
||||
async def get_by_username(self, username: str) -> Profile | None:
|
||||
for profile in self._profiles.values():
|
||||
if profile.username == username:
|
||||
return profile
|
||||
return None
|
||||
|
||||
async def update_by_user_id(
|
||||
self, user_id: UUID, update_data: dict[str, str | None]
|
||||
) -> Profile | None:
|
||||
del update_data
|
||||
return self._profiles.get(user_id)
|
||||
|
||||
async def search_users(self, query: str, limit: int = 20) -> list[Profile]:
|
||||
del limit
|
||||
query_lower = query.lower()
|
||||
return [
|
||||
profile
|
||||
for profile in self._profiles.values()
|
||||
if query_lower in profile.username.lower()
|
||||
]
|
||||
|
||||
|
||||
_repo_check: FriendshipRepository = FakeFriendshipRepo()
|
||||
_user_repo_check: UserRepository = FakeUserRepo()
|
||||
@@ -208,7 +249,9 @@ class TestSendRequest:
|
||||
current_user=current_user,
|
||||
)
|
||||
|
||||
result = await service.send_request(FriendRequestCreate(target_user_id=USER_B))
|
||||
result = await service.send_request(
|
||||
FriendRequestCreate(target_user_id=USER_B, content=None)
|
||||
)
|
||||
|
||||
assert result is not None
|
||||
mock_session.commit.assert_awaited_once()
|
||||
@@ -233,7 +276,7 @@ class TestSendRequest:
|
||||
FriendRequestCreate(target_user_id=USER_B, content=content)
|
||||
)
|
||||
|
||||
assert result.content == content
|
||||
assert result.content == {"type": "request", "message": content}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_send_request_to_self_raises_400(
|
||||
@@ -252,7 +295,7 @@ class TestSendRequest:
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service.send_request(
|
||||
FriendRequestCreate(target_user_id=current_user.id)
|
||||
FriendRequestCreate(target_user_id=current_user.id, content=None)
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
@@ -280,7 +323,9 @@ class TestSendRequest:
|
||||
)
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service.send_request(FriendRequestCreate(target_user_id=USER_B))
|
||||
await service.send_request(
|
||||
FriendRequestCreate(target_user_id=USER_B, content=None)
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
@@ -307,7 +352,9 @@ class TestSendRequest:
|
||||
)
|
||||
|
||||
with pytest.raises(HTTPException) as exc_info:
|
||||
await service.send_request(FriendRequestCreate(target_user_id=USER_B))
|
||||
await service.send_request(
|
||||
FriendRequestCreate(target_user_id=USER_B, content=None)
|
||||
)
|
||||
|
||||
assert exc_info.value.status_code == 400
|
||||
|
||||
|
||||
Reference in New Issue
Block a user