chore: 清理测试文件和上传目录
This commit is contained in:
@@ -1,283 +0,0 @@
|
|||||||
"""
|
|
||||||
健康管家 - 全流程端到端测试
|
|
||||||
模拟真实用户操作:注册→登录→各Agent对话→数据录入→查询验证
|
|
||||||
"""
|
|
||||||
import urllib.request, urllib.parse, json, sys, time, os
|
|
||||||
|
|
||||||
BASE = "http://localhost:5000"
|
|
||||||
PASSED = 0
|
|
||||||
FAILED = 0
|
|
||||||
TOKEN = None
|
|
||||||
|
|
||||||
def api(method, path, data=None, token=None, files=None):
|
|
||||||
"""调用后端 API"""
|
|
||||||
url = f"{BASE}{path}"
|
|
||||||
headers = {"Content-Type": "application/json"}
|
|
||||||
if token:
|
|
||||||
headers["Authorization"] = f"Bearer {token}"
|
|
||||||
|
|
||||||
body = None
|
|
||||||
if data:
|
|
||||||
body = json.dumps(data, ensure_ascii=False).encode("utf-8")
|
|
||||||
|
|
||||||
req = urllib.request.Request(url, data=body, headers=headers, method=method)
|
|
||||||
try:
|
|
||||||
resp = urllib.request.urlopen(req, timeout=30)
|
|
||||||
return json.loads(resp.read().decode("utf-8"))
|
|
||||||
except Exception as e:
|
|
||||||
return {"error": str(e), "code": -1}
|
|
||||||
|
|
||||||
def check(name, condition, detail=""):
|
|
||||||
global PASSED, FAILED
|
|
||||||
if condition:
|
|
||||||
PASSED += 1
|
|
||||||
print(f" [PASS] {name}")
|
|
||||||
else:
|
|
||||||
FAILED += 1
|
|
||||||
print(f" [FAIL] {name} {detail}")
|
|
||||||
|
|
||||||
def login(phone="13800000001"):
|
|
||||||
"""发送验证码 + 登录,返回 token"""
|
|
||||||
sms = api("POST", "/api/auth/send-sms", {"phone": phone})
|
|
||||||
code = sms.get("data", {}).get("devCode", "")
|
|
||||||
if not code:
|
|
||||||
return None
|
|
||||||
result = api("POST", "/api/auth/login", {"phone": phone, "smsCode": code})
|
|
||||||
return result.get("data", {}).get("accessToken")
|
|
||||||
|
|
||||||
def sse_stream(token, agent_type, message):
|
|
||||||
"""连接 SSE 端点,返回所有事件"""
|
|
||||||
url = f"{BASE}/api/ai/{agent_type}/chat?message={urllib.parse.quote(message)}&token={urllib.parse.quote(token or '')}"
|
|
||||||
req = urllib.request.Request(url)
|
|
||||||
events = []
|
|
||||||
try:
|
|
||||||
resp = urllib.request.urlopen(req, timeout=60)
|
|
||||||
for line_bytes in resp:
|
|
||||||
line = line_bytes.decode("utf-8").strip()
|
|
||||||
if line.startswith("data: "):
|
|
||||||
data = line[6:]
|
|
||||||
if data == "[DONE]":
|
|
||||||
break
|
|
||||||
try:
|
|
||||||
events.append(json.loads(data))
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
except Exception as e:
|
|
||||||
events.append({"error": str(e)})
|
|
||||||
return events
|
|
||||||
|
|
||||||
def section(title):
|
|
||||||
print(f"\n{'='*60}")
|
|
||||||
print(f" {title}")
|
|
||||||
print(f"{'='*60}")
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("1. 认证流程")
|
|
||||||
# ============================================================
|
|
||||||
print(" 1.1 发送验证码...")
|
|
||||||
sms = api("POST", "/api/auth/send-sms", {"phone": "13800000001"})
|
|
||||||
check("发送验证码", sms.get("code") == 0, str(sms.get("message","")))
|
|
||||||
code = sms.get("data", {}).get("devCode", "")
|
|
||||||
|
|
||||||
print(" 1.2 验证码登录...")
|
|
||||||
login_result = api("POST", "/api/auth/login", {"phone": "13800000001", "smsCode": code})
|
|
||||||
check("登录成功", login_result.get("code") == 0)
|
|
||||||
TOKEN = login_result.get("data", {}).get("accessToken", "")
|
|
||||||
REFRESH = login_result.get("data", {}).get("refreshToken", "")
|
|
||||||
check("返回accessToken", len(TOKEN) > 50)
|
|
||||||
check("返回refreshToken", len(REFRESH) > 20)
|
|
||||||
|
|
||||||
print(" 1.3 刷新Token...")
|
|
||||||
refresh_result = api("POST", "/api/auth/refresh", {"refreshToken": REFRESH})
|
|
||||||
check("刷新Token成功", refresh_result.get("code") == 0)
|
|
||||||
check("下发新Token", len(refresh_result.get("data", {}).get("accessToken", "")) > 50)
|
|
||||||
|
|
||||||
print(" 1.4 登出...")
|
|
||||||
logout_result = api("POST", "/api/auth/logout", {"refreshToken": REFRESH})
|
|
||||||
check("登出成功", logout_result.get("code") == 0)
|
|
||||||
|
|
||||||
# 重新登录获取token
|
|
||||||
TOKEN = login()
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("2. 用户与档案")
|
|
||||||
# ============================================================
|
|
||||||
profile = api("GET", "/api/user/profile", token=TOKEN)
|
|
||||||
check("获取个人信息", profile.get("code") == 0)
|
|
||||||
|
|
||||||
archive = api("GET", "/api/user/health-archive", token=TOKEN)
|
|
||||||
check("获取健康档案", archive.get("code") == 0)
|
|
||||||
check("档案有诊断信息", archive.get("data", {}).get("diagnosis") is not None if archive.get("data") else False,
|
|
||||||
"诊断=" + str(archive.get("data", {}).get("diagnosis", "无")))
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("3. 健康数据 CRUD")
|
|
||||||
# ============================================================
|
|
||||||
print(" 3.1 录入血压...")
|
|
||||||
bp = api("POST", "/api/health-records", token=TOKEN, data={
|
|
||||||
"type": "BloodPressure", "systolic": 128, "diastolic": 82,
|
|
||||||
"unit": "mmHg", "source": "Manual"
|
|
||||||
})
|
|
||||||
check("录入血压", bp.get("code") == 0, str(bp.get("message","")))
|
|
||||||
|
|
||||||
print(" 3.2 录入心率...")
|
|
||||||
hr = api("POST", "/api/health-records", token=TOKEN, data={
|
|
||||||
"type": "HeartRate", "value": 72, "unit": "次/分", "source": "Manual"
|
|
||||||
})
|
|
||||||
check("录入心率", hr.get("code") == 0, str(hr.get("message","")))
|
|
||||||
|
|
||||||
print(" 3.3 录入血糖...")
|
|
||||||
glu = api("POST", "/api/health-records", token=TOKEN, data={
|
|
||||||
"type": "Glucose", "value": 5.5, "unit": "mmol/L", "source": "Manual"
|
|
||||||
})
|
|
||||||
check("录入血糖", glu.get("code") == 0, str(glu.get("message","")))
|
|
||||||
|
|
||||||
print(" 3.4 录入血氧...")
|
|
||||||
spo2 = api("POST", "/api/health-records", token=TOKEN, data={
|
|
||||||
"type": "SpO2", "value": 98, "unit": "%", "source": "Manual"
|
|
||||||
})
|
|
||||||
check("录入血氧", spo2.get("code") == 0, str(spo2.get("message","")))
|
|
||||||
|
|
||||||
print(" 3.5 获取最新数据...")
|
|
||||||
latest = api("GET", "/api/health-records/latest", token=TOKEN)
|
|
||||||
check("获取最新数据", latest.get("code") == 0)
|
|
||||||
check("血压存在", latest.get("data", {}).get("BloodPressure") is not None)
|
|
||||||
|
|
||||||
print(" 3.6 获取趋势数据...")
|
|
||||||
trend = api("GET", "/api/health-records/trend?type=HeartRate&period=7", token=TOKEN)
|
|
||||||
check("获取趋势数据", trend.get("code") == 0)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("4. 用药管理")
|
|
||||||
# ============================================================
|
|
||||||
print(" 4.1 获取用药列表...")
|
|
||||||
meds = api("GET", "/api/medications", token=TOKEN)
|
|
||||||
check("获取用药列表", meds.get("code") == 0)
|
|
||||||
|
|
||||||
print(" 4.2 添加用药...")
|
|
||||||
new_med = api("POST", "/api/medications", token=TOKEN, data={
|
|
||||||
"name": "阿司匹林", "dosage": "100mg", "frequency": "Daily",
|
|
||||||
"timeOfDay": ["08:00"], "source": "Manual", "startDate": "2026-06-02"
|
|
||||||
})
|
|
||||||
check("添加用药", new_med.get("code") == 0, str(new_med.get("message","")))
|
|
||||||
med_id = new_med.get("data", {}).get("id", "")
|
|
||||||
|
|
||||||
print(" 4.3 服药打卡...")
|
|
||||||
if med_id:
|
|
||||||
confirm = api("POST", f"/api/medications/{med_id}/confirm", token=TOKEN)
|
|
||||||
check("服药打卡", confirm.get("code") == 0, str(confirm.get("message","")))
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("5. 饮食记录")
|
|
||||||
# ============================================================
|
|
||||||
diet = api("GET", "/api/diet-records?date=2026-06-02", token=TOKEN)
|
|
||||||
check("查询饮食记录", diet.get("code") == 0)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("6. 运动计划")
|
|
||||||
# ============================================================
|
|
||||||
print(" 6.1 获取当前计划...")
|
|
||||||
plan = api("GET", "/api/exercise-plans/current", token=TOKEN)
|
|
||||||
check("获取当前计划", plan.get("code") == 0)
|
|
||||||
|
|
||||||
print(" 6.2 创建运动计划...")
|
|
||||||
new_plan = api("POST", "/api/exercise-plans", token=TOKEN, data={
|
|
||||||
"weekStartDate": "2026-06-02",
|
|
||||||
"items": [
|
|
||||||
{"dayOfWeek": 1, "exerciseType": "散步", "durationMinutes": 30, "isRestDay": False},
|
|
||||||
{"dayOfWeek": 3, "exerciseType": "太极", "durationMinutes": 40, "isRestDay": False},
|
|
||||||
{"dayOfWeek": 5, "exerciseType": "散步", "durationMinutes": 30, "isRestDay": False},
|
|
||||||
]
|
|
||||||
})
|
|
||||||
check("创建运动计划", new_plan.get("code") == 0, str(new_plan.get("message","")))
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("7. 医生与问诊")
|
|
||||||
# ============================================================
|
|
||||||
print(" 7.1 医生列表...")
|
|
||||||
docs = api("GET", "/api/doctors", token=TOKEN)
|
|
||||||
check("获取医生列表", docs.get("code") == 0)
|
|
||||||
check("有医生数据", len(docs.get("data", [])) > 0, f"共{len(docs.get('data',[]))}位医生")
|
|
||||||
|
|
||||||
print(" 7.2 问诊配额...")
|
|
||||||
quota = api("GET", "/api/user/consultation-quota", token=TOKEN)
|
|
||||||
check("获取问诊配额", quota.get("code") == 0)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("8. AI 智能体对话")
|
|
||||||
# ============================================================
|
|
||||||
agents_to_test = [
|
|
||||||
("default", "你好,介绍一下你自己"),
|
|
||||||
("health", "我血压128/82"),
|
|
||||||
("medication", "我现在在吃什么药"),
|
|
||||||
("consultation", "最近胸口有点不舒服"),
|
|
||||||
("diet", "我中午吃了红烧肉和米饭"),
|
|
||||||
("exercise", "帮我查询运动计划"),
|
|
||||||
]
|
|
||||||
for agent_name, msg in agents_to_test:
|
|
||||||
print(f" 8.{agents_to_test.index((agent_name,msg))+1} {agent_name} Agent: \"{msg[:30]}...\"")
|
|
||||||
TOKEN = login() # fresh token
|
|
||||||
events = sse_stream(TOKEN, agent_name, msg)
|
|
||||||
|
|
||||||
has_answer = any(e.get("action") == "answer" for e in events)
|
|
||||||
has_tool = any(e.get("action") == "tool_result" for e in events)
|
|
||||||
has_conv_id = any(e.get("action") == "conversation_id" for e in events)
|
|
||||||
errors = [e for e in events if e.get("action") == "error"]
|
|
||||||
|
|
||||||
check(f"{agent_name}: 对话建立", has_conv_id)
|
|
||||||
check(f"{agent_name}: 有回复", has_answer or has_tool,
|
|
||||||
f"(events: {len(events)}, tools: {has_tool}, answer: {has_answer})")
|
|
||||||
check(f"{agent_name}: 无错误", len(errors) == 0,
|
|
||||||
f"errors: {[e.get('message','') for e in errors]}" if errors else "")
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("9. 对话历史")
|
|
||||||
# ============================================================
|
|
||||||
convs = api("GET", "/api/ai/conversations", token=TOKEN)
|
|
||||||
check("获取对话列表", convs.get("code") == 0)
|
|
||||||
check("有对话记录", len(convs.get("data", [])) > 0, f"共{len(convs.get('data',[]))}条")
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("10. VLM 食物识别")
|
|
||||||
# ============================================================
|
|
||||||
# 尝试上传测试图片
|
|
||||||
test_img = "D:/health_project/食堂三菜一饭热量估算.png"
|
|
||||||
if os.path.exists(test_img):
|
|
||||||
# Use subprocess for multipart upload
|
|
||||||
import subprocess
|
|
||||||
cmd = [
|
|
||||||
'curl', '-s', '--max-time', '30', '-X', 'POST',
|
|
||||||
f'{BASE}/api/ai/analyze-food-image',
|
|
||||||
'-H', f'Authorization: Bearer {TOKEN}',
|
|
||||||
'-F', f'images=@{test_img}'
|
|
||||||
]
|
|
||||||
r = subprocess.run(cmd, capture_output=True, text=True)
|
|
||||||
try:
|
|
||||||
vlm = json.loads(r.stdout)
|
|
||||||
check("VLM食物识别调通", vlm.get("code") == 0, str(vlm.get("message","")))
|
|
||||||
has_data = bool(vlm.get("data", ""))
|
|
||||||
check("VLM返回数据", has_data, f"data长度: {len(str(vlm.get('data','')))}")
|
|
||||||
except:
|
|
||||||
check("VLM食物识别", False, "JSON解析失败")
|
|
||||||
else:
|
|
||||||
check("VLM测试图片存在", False, f"{test_img} 不存在")
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("11. 报告列表")
|
|
||||||
# ============================================================
|
|
||||||
reports = api("GET", "/api/reports", token=TOKEN)
|
|
||||||
check("获取报告列表", reports.get("code") == 0)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
section("12. 通知偏好")
|
|
||||||
# ============================================================
|
|
||||||
notifs = api("GET", "/api/notifications/preferences", token=TOKEN)
|
|
||||||
check("获取通知偏好", notifs.get("code") == 0)
|
|
||||||
|
|
||||||
# ============================================================
|
|
||||||
print(f"\n{'='*60}")
|
|
||||||
print(f" 测试结果: PASS={PASSED} FAIL={FAILED} TOTAL={PASSED+FAILED}")
|
|
||||||
print(f"{'='*60}")
|
|
||||||
|
|
||||||
if FAILED > 0:
|
|
||||||
sys.exit(1)
|
|
||||||
Reference in New Issue
Block a user