Files
012-kaopeilian/backend/tests/test_coze_api.py
111 998211c483 feat: 初始化考培练系统项目
- 从服务器拉取完整代码
- 按框架规范整理项目结构
- 配置 Drone CI 测试环境部署
- 包含后端(FastAPI)、前端(Vue3)、管理端

技术栈: Vue3 + TypeScript + FastAPI + MySQL
2026-01-24 19:33:28 +08:00

307 lines
10 KiB
Python

"""
Coze API 网关单元测试
"""
import pytest
from unittest.mock import Mock, AsyncMock, patch
from fastapi.testclient import TestClient
from fastapi import FastAPI
from sse_starlette.sse import ServerSentEvent
from app.api.v1.coze_gateway import router
from app.services.ai.coze.models import (
CreateSessionResponse, EndSessionResponse,
StreamEvent, StreamEventType, ContentType, MessageRole
)
from app.services.ai.coze.exceptions import CozeAPIError, CozeAuthError
# 创建测试应用
app = FastAPI()
app.include_router(router)
client = TestClient(app)
@pytest.fixture
def mock_user():
"""模拟已登录用户"""
with patch("app.api.v1.coze_gateway.get_current_user") as mock_get_user:
mock_get_user.return_value = {
"user_id": "test-user-123",
"username": "test_user"
}
yield mock_get_user
@pytest.fixture
def mock_coze_service():
"""模拟 Coze 服务"""
with patch("app.api.v1.coze_gateway.get_coze_service") as mock_get_service:
mock_service = Mock()
mock_get_service.return_value = mock_service
yield mock_service
class TestCourseChat:
"""测试课程对话 API"""
def test_create_course_chat_session_success(self, mock_user, mock_coze_service):
"""测试成功创建课程对话会话"""
# Mock 服务响应
mock_coze_service.create_session = AsyncMock(
return_value=CreateSessionResponse(
session_id="session-123",
conversation_id="conv-123",
bot_id="bot-123",
created_at="2024-01-01T10:00:00"
)
)
response = client.post(
"/api/v1/course-chat/sessions",
json={"course_id": "course-456"}
)
assert response.status_code == 200
data = response.json()
assert data["code"] == 200
assert data["message"] == "success"
assert data["data"]["session_id"] == "session-123"
assert data["data"]["conversation_id"] == "conv-123"
def test_create_course_chat_session_auth_error(self, mock_user, mock_coze_service):
"""测试认证错误"""
mock_coze_service.create_session = AsyncMock(
side_effect=CozeAuthError(
message="认证失败",
code="AUTH_ERROR",
status_code=401
)
)
response = client.post(
"/api/v1/course-chat/sessions",
json={"course_id": "course-456"}
)
assert response.status_code == 401
data = response.json()
assert data["detail"]["code"] == "AUTH_ERROR"
assert data["detail"]["message"] == "认证失败"
def test_create_course_chat_session_server_error(self, mock_user, mock_coze_service):
"""测试服务器错误"""
mock_coze_service.create_session = AsyncMock(
side_effect=Exception("Unexpected error")
)
response = client.post(
"/api/v1/course-chat/sessions",
json={"course_id": "course-456"}
)
assert response.status_code == 500
data = response.json()
assert data["detail"]["code"] == "INTERNAL_ERROR"
class TestTraining:
"""测试陪练 API"""
def test_create_training_session_with_topic(self, mock_user, mock_coze_service):
"""测试创建带主题的陪练会话"""
mock_coze_service.create_session = AsyncMock(
return_value=CreateSessionResponse(
session_id="training-123",
conversation_id="conv-456",
bot_id="training-bot",
created_at="2024-01-01T11:00:00"
)
)
response = client.post(
"/api/v1/training/sessions",
json={"training_topic": "客诉处理技巧"}
)
assert response.status_code == 200
data = response.json()
assert data["data"]["session_id"] == "training-123"
# 验证服务调用
call_args = mock_coze_service.create_session.call_args[0][0]
assert call_args.training_topic == "客诉处理技巧"
def test_create_training_session_without_topic(self, mock_user, mock_coze_service):
"""测试创建不带主题的陪练会话"""
mock_coze_service.create_session = AsyncMock(
return_value=CreateSessionResponse(
session_id="training-456",
conversation_id="conv-789",
bot_id="training-bot",
created_at="2024-01-01T12:00:00"
)
)
response = client.post("/api/v1/training/sessions", json={})
assert response.status_code == 200
data = response.json()
assert data["data"]["session_id"] == "training-456"
def test_end_training_session_success(self, mock_user, mock_coze_service):
"""测试成功结束陪练会话"""
mock_coze_service.end_session = AsyncMock(
return_value=EndSessionResponse(
session_id="training-123",
ended_at="2024-01-01T13:00:00",
duration_seconds=1800,
message_count=25
)
)
response = client.post(
"/api/v1/training/sessions/training-123/end",
json={
"reason": "练习完成",
"feedback": {"rating": 5, "helpful": True}
}
)
assert response.status_code == 200
data = response.json()
assert data["data"]["duration_seconds"] == 1800
assert data["data"]["message_count"] == 25
def test_end_nonexistent_session(self, mock_user, mock_coze_service):
"""测试结束不存在的会话"""
mock_coze_service.end_session = AsyncMock(
side_effect=CozeAPIError(
message="会话不存在",
code="SESSION_NOT_FOUND",
status_code=404
)
)
response = client.post(
"/api/v1/training/sessions/nonexistent/end",
json={}
)
assert response.status_code == 404
class TestChatMessages:
"""测试消息发送 API"""
def test_send_message_non_stream(self, mock_user, mock_coze_service):
"""测试非流式消息发送"""
# Mock 异步生成器
async def mock_generator():
yield StreamEvent(
event=StreamEventType.MESSAGE_DELTA,
data={},
content="Hello",
content_type=ContentType.TEXT,
role=MessageRole.ASSISTANT
)
yield StreamEvent(
event=StreamEventType.MESSAGE_COMPLETED,
data={"usage": {"tokens": 10}},
message_id="msg-123",
content="Hello, how can I help you?",
content_type=ContentType.TEXT,
role=MessageRole.ASSISTANT
)
yield StreamEvent(
event=StreamEventType.DONE,
data={"session_id": "session-123"}
)
mock_coze_service.send_message = AsyncMock(return_value=mock_generator())
response = client.post(
"/api/v1/chat/messages",
json={
"session_id": "session-123",
"content": "Hello",
"stream": False
}
)
assert response.status_code == 200
data = response.json()
assert data["data"]["content"] == "Hello, how can I help you?"
assert data["data"]["content_type"] == "text"
assert data["data"]["role"] == "assistant"
def test_send_message_with_files(self, mock_user, mock_coze_service):
"""测试带附件的消息发送"""
async def mock_generator():
yield StreamEvent(
event=StreamEventType.MESSAGE_COMPLETED,
data={},
message_id="msg-456",
content="File received",
content_type=ContentType.TEXT,
role=MessageRole.ASSISTANT
)
yield StreamEvent(
event=StreamEventType.DONE,
data={"session_id": "session-123"}
)
mock_coze_service.send_message = AsyncMock(return_value=mock_generator())
response = client.post(
"/api/v1/chat/messages",
json={
"session_id": "session-123",
"content": "Please analyze this file",
"file_ids": ["file-123", "file-456"],
"stream": False
}
)
assert response.status_code == 200
# 验证服务调用
call_args = mock_coze_service.send_message.call_args[0][0]
assert call_args.file_ids == ["file-123", "file-456"]
def test_get_message_history(self, mock_user, mock_coze_service):
"""测试获取消息历史"""
from app.services.ai.coze.models import CozeMessage
mock_messages = [
CozeMessage(
message_id="msg-1",
session_id="session-123",
role=MessageRole.USER,
content="Hello"
),
CozeMessage(
message_id="msg-2",
session_id="session-123",
role=MessageRole.ASSISTANT,
content="Hi there!"
)
]
mock_coze_service.get_session_messages = AsyncMock(
return_value=mock_messages
)
response = client.get(
"/api/v1/sessions/session-123/messages?limit=10&offset=0"
)
assert response.status_code == 200
data = response.json()
assert len(data["data"]["messages"]) == 2
assert data["data"]["messages"][0]["content"] == "Hello"
assert data["data"]["messages"][1]["content"] == "Hi there!"
assert data["data"]["limit"] == 10
assert data["data"]["offset"] == 0