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

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

351 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""
测试陪练功能API
"""
import requests
import json
import sys
BASE_URL = "http://localhost:8000"
USERNAME = "admin"
PASSWORD = "admin123"
def login():
"""登录获取token"""
print("=" * 60)
print("1. 登录获取token")
print("=" * 60)
response = requests.post(
f"{BASE_URL}/api/v1/auth/login",
json={"username": USERNAME, "password": PASSWORD}
)
if response.status_code != 200:
print(f"❌ 登录失败: {response.status_code}")
sys.exit(1)
data = response.json()
if data['code'] != 200:
print(f"❌ 登录失败: {data['message']}")
sys.exit(1)
token = data['data']['token']['access_token']
print(f"✅ 登录成功token: {token[:50]}...")
return token
def test_get_scenes(token):
"""测试获取场景列表"""
print("\n" + "=" * 60)
print("2. 测试获取场景列表")
print("=" * 60)
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{BASE_URL}/api/v1/practice/scenes", headers=headers)
if response.status_code != 200:
print(f"❌ 请求失败: {response.status_code}")
return False
data = response.json()
if data['code'] != 200:
print(f"❌ 获取失败: {data['message']}")
return False
scenes = data['data']['items']
total = data['data']['total']
print(f"✅ 成功获取 {total} 个场景")
print("\n场景列表:")
print("-" * 80)
for scene in scenes:
print(f" {scene['id']}. {scene['name']} - {scene['type']} - {scene['difficulty']}")
print("-" * 80)
return True
def test_get_scene_detail(token, scene_id=1):
"""测试获取场景详情"""
print("\n" + "=" * 60)
print(f"3. 测试获取场景详情 (ID={scene_id})")
print("=" * 60)
headers = {"Authorization": f"Bearer {token}"}
response = requests.get(f"{BASE_URL}/api/v1/practice/scenes/{scene_id}", headers=headers)
if response.status_code != 200:
print(f"❌ 请求失败: {response.status_code}")
return None
data = response.json()
if data['code'] != 200:
print(f"❌ 获取失败: {data['message']}")
return None
scene = data['data']
print(f"✅ 成功获取场景详情")
print(f"\n场景名称: {scene['name']}")
print(f"类型: {scene['type']}")
print(f"难度: {scene['difficulty']}")
print(f"时长: {scene['duration']}分钟")
print(f"\n场景背景:\n{scene['background']}")
print(f"\nAI角色:\n{scene['ai_role']}")
print(f"\n练习目标:")
for i, obj in enumerate(scene['objectives'], 1):
print(f" {i}. {obj}")
return scene
def test_sse_chat(token, scene):
"""测试SSE对话"""
print("\n" + "=" * 60)
print("4. 测试SSE流式对话")
print("=" * 60)
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# 首次消息
payload = {
"scene_id": scene['id'],
"scene_name": scene['name'],
"scene_description": scene['description'],
"scene_background": scene['background'],
"scene_ai_role": scene['ai_role'],
"scene_objectives": scene['objectives'],
"scene_keywords": scene['keywords'],
"user_message": "您好,我是某轻医美品牌的销售顾问,想占用您几分钟时间介绍一下我们的产品",
"is_first": True
}
print("\n发送首次消息(包含场景信息):")
print(f" 用户消息: {payload['user_message']}")
print(f" is_first: {payload['is_first']}")
print(f"\n等待AI回复流式...")
print("-" * 80)
try:
response = requests.post(
f"{BASE_URL}/api/v1/practice/start",
headers=headers,
json=payload,
stream=True,
timeout=180
)
if response.status_code != 200:
print(f"❌ SSE请求失败: {response.status_code}")
print(f" 响应: {response.text}")
return None
conversation_id = None
chat_id = None
ai_message = ""
# 处理SSE流
for line in response.iter_lines():
if not line:
continue
line = line.decode('utf-8')
# 解析SSE事件
if line.startswith('event: '):
event_type = line[7:].strip()
elif line.startswith('data: '):
data_str = line[6:].strip()
if data_str == '[DONE]':
print("\n\n✅ 对话流结束")
break
try:
event_data = json.loads(data_str)
if event_type == 'conversation.chat.created':
conversation_id = event_data.get('conversation_id')
chat_id = event_data.get('chat_id')
print(f"\n📝 对话已创建:")
print(f" conversation_id: {conversation_id}")
print(f" chat_id: {chat_id}")
print(f"\n🤖 AI回复: ", end="", flush=True)
elif event_type == 'message.delta':
content = event_data.get('content', '')
ai_message += content
print(content, end="", flush=True)
elif event_type == 'message.completed':
print()
elif event_type == 'conversation.completed':
token_count = event_data.get('token_count', 0)
print(f"\n\n📊 对话完成Token用量: {token_count}")
elif event_type == 'error':
error = event_data.get('error', '未知错误')
print(f"\n\n❌ 对话错误: {error}")
return None
except json.JSONDecodeError as e:
print(f"\n⚠️ JSON解析错误: {e}")
print("-" * 80)
print(f"\n✅ SSE对话测试成功")
print(f" conversation_id: {conversation_id}")
print(f" AI消息长度: {len(ai_message)} 字符")
return conversation_id
except Exception as e:
print(f"\n❌ SSE测试失败: {e}")
import traceback
traceback.print_exc()
return None
def test_follow_up_message(token, conversation_id):
"""测试后续消息(不包含场景信息)"""
print("\n" + "=" * 60)
print("5. 测试后续消息")
print("=" * 60)
headers = {
"Authorization": f"Bearer {token}",
"Content-Type": "application/json"
}
# 后续消息
payload = {
"user_message": "我们专注于轻医美行业,提供从设备到培训的一站式解决方案",
"conversation_id": conversation_id,
"is_first": False
}
print(f"\n发送后续消息(不包含场景信息):")
print(f" 用户消息: {payload['user_message']}")
print(f" is_first: {payload['is_first']}")
print(f" conversation_id: {conversation_id}")
print(f"\n等待AI回复...")
print("-" * 80)
try:
response = requests.post(
f"{BASE_URL}/api/v1/practice/start",
headers=headers,
json=payload,
stream=True,
timeout=180
)
if response.status_code != 200:
print(f"❌ SSE请求失败: {response.status_code}")
return False
ai_message = ""
# 处理SSE流
for line in response.iter_lines():
if not line:
continue
line = line.decode('utf-8')
if line.startswith('event: '):
event_type = line[7:].strip()
elif line.startswith('data: '):
data_str = line[6:].strip()
if data_str == '[DONE]':
break
try:
event_data = json.loads(data_str)
if event_type == 'message.delta':
content = event_data.get('content', '')
ai_message += content
print(content, end="", flush=True)
elif event_type == 'conversation.completed':
token_count = event_data.get('token_count', 0)
print(f"\n\n📊 对话完成Token用量: {token_count}")
except json.JSONDecodeError:
pass
print("\n" + "-" * 80)
print(f"\n✅ 后续消息测试成功")
print(f" AI消息长度: {len(ai_message)} 字符")
return True
except Exception as e:
print(f"\n❌ 后续消息测试失败: {e}")
return False
def main():
"""主测试流程"""
print("\n" + "=" * 60)
print("陪练功能API测试")
print("=" * 60)
try:
# 1. 登录
token = login()
# 2. 测试场景列表
if not test_get_scenes(token):
print("\n❌ 场景列表测试失败")
sys.exit(1)
# 3. 测试场景详情
scene = test_get_scene_detail(token, scene_id=1)
if not scene:
print("\n❌ 场景详情测试失败")
sys.exit(1)
# 4. 测试SSE对话
conversation_id = test_sse_chat(token, scene)
if not conversation_id:
print("\n❌ SSE对话测试失败")
sys.exit(1)
# 5. 测试后续消息
if not test_follow_up_message(token, conversation_id):
print("\n❌ 后续消息测试失败")
sys.exit(1)
# 全部测试通过
print("\n" + "=" * 60)
print("✅ 所有测试通过!")
print("=" * 60)
print("\n测试总结:")
print(" ✅ 登录认证")
print(" ✅ 场景列表查询")
print(" ✅ 场景详情查询")
print(" ✅ SSE流式对话首次消息")
print(" ✅ 后续消息(保持上下文)")
print("\n陪练中心入口功能开发完成!")
print("=" * 60)
except KeyboardInterrupt:
print("\n\n⚠️ 用户中断测试")
sys.exit(0)
except Exception as e:
print(f"\n❌ 测试过程出错: {e}")
import traceback
traceback.print_exc()
sys.exit(1)
if __name__ == "__main__":
main()