Files
Cyrene/debug/cache/test_chat_e2e.py
T
AskaEth a058b0ab8e fix: 第一轮修复 - 记忆管理/IoT操控/历史消息持久化/动作消息/链路优化/安全配置
- 修复记忆管理数据库连接不可用 (ai-core重编译+Unicode修复)
- 修复IoT子会话工具调用链路日志缺失
- 新增最终审查子会话(review_provider) 支持消息格式解析拆分
- 实现历史消息持久化(后端存储+前端分页加载)
- 前端新增动作消息(ActionMessage)类型和渲染
- 优化对话链路速度(非阻塞子会话+快速问候通道)
- JWT密钥环境变量化(无默认值启动panic)
- Token自动刷新机制(401拦截器+refresh接口)
- WebSocket指数退避重连(jitter+最大10次)
- localStorage清理一致性(cyrene_前缀+版本检查)
- IoT环境变量统一为IOT_SERVICE_URL
2026-05-21 23:10:07 +08:00

165 lines
6.1 KiB
Python

#!/usr/bin/env python3
"""Round 12: 聊天 E2E + 子服务连通性测试"""
import json, urllib.request, urllib.error, sys
BASE = "http://localhost:8080"
TOKEN_FILE = "/tmp/cyrene_test_token.txt"
SID_FILE = "/tmp/cyrene_test_sid.txt"
def req(method, path, body=None, token=None, timeout=10):
url = f"{BASE}{path}"
data = json.dumps(body).encode() if body else None
r = urllib.request.Request(url, data=data, method=method)
r.add_header("Content-Type", "application/json")
if token:
r.add_header("Authorization", f"Bearer {token}")
try:
resp = urllib.request.urlopen(r, timeout=timeout)
return resp.status, resp.read().decode()
except urllib.error.HTTPError as e:
return e.code, e.read().decode()
except Exception as e:
return 0, str(e)
# ====== Step 1: Admin Login ======
print("=== Step 1: Admin Login ===")
status, body = req("POST", "/api/v1/auth/login",
{"username":"yeij0942","password":"Jiang1143218570"})
print(f" status={status}")
d = json.loads(body)
token = d.get("token", "")
print(f" user_id={d.get('user_id')} has_token={'token' in d} token_len={len(token)}")
# ====== Step 2: 所有服务健康检查 ======
print("\n=== Step 2: 子服务健康检查 ===")
services = [
("ai-core (8081)", "http://localhost:8081/api/v1/health"),
("memory-service (8091)", "http://localhost:8091/api/v1/health"),
("tool-engine (8092)", "http://localhost:8092/api/v1/health"),
("voice-service (8093)", "http://localhost:8093/api/v1/health"),
("gateway (8080)", "http://localhost:8080/api/v1/health"),
]
for name, url in services:
r = urllib.request.Request(url)
try:
resp = urllib.request.urlopen(r, timeout=5)
body = resp.read().decode()
print(f" [{resp.status}] {name}: {body[:120]}")
except Exception as e:
print(f" [FAIL] {name}: {e}")
# ====== Step 3: 获取/创建 Session ======
print("\n=== Step 3: Session 管理 ===")
status, body = req("GET", "/api/v1/sessions", token=token)
print(f" GET /sessions status={status}")
try:
sessions = json.loads(body)
if isinstance(sessions, list) and len(sessions) > 0:
sid = sessions[0].get("id", "")
print(f" existing session: {sid}")
else:
# 创建
status, body = req("POST", "/api/v1/sessions", {"user_id": "admin"}, token=token)
print(f" POST /sessions status={status} body={body[:200]}")
sid = json.loads(body).get("id", "")
print(f" new session: {sid}")
except Exception as e:
print(f" parse error: {e}")
sid = ""
# ====== Step 4: 聊天消息发送 (测试 ai-core 转发) ======
print(f"\n=== Step 4: 聊天消息发送 (session={sid}) ===")
if sid and token:
msg_body = {
"session_id": sid,
"message": "你好,请简单介绍一下你自己",
"mode": "text"
}
status, body = req("POST", "/api/v1/chat", msg_body, token=token)
print(f" POST /api/v1/chat status={status}")
if status == 404:
print(f" WARNING: /api/v1/chat 端点返回 404 - 端点可能未注册!")
# 检查 gateway 日志
import subprocess
result = subprocess.run(["grep", "-i", "chat", "/tmp/gateway.log"], capture_output=True, text=True)
print(f" gateway log (chat): {result.stdout[:500]}")
else:
print(f" response: {body[:500]}")
else:
print(" SKIPPED: no session or token")
# ====== Step 5: 直接调用 ai-core chat ======
print("\n=== Step 5: 直接调用 ai-core /api/v1/chat ===")
ai_body = {
"user_id": "admin",
"session_id": sid or "test_session_001",
"message": "Hello",
"mode": "text"
}
r = urllib.request.Request("http://localhost:8081/api/v1/chat",
data=json.dumps(ai_body).encode(),
method="POST")
r.add_header("Content-Type", "application/json")
r.add_header("Accept", "text/event-stream")
try:
resp = urllib.request.urlopen(r, timeout=30)
print(f" ai-core status={resp.status}")
# 读取前5行 SSE
lines = []
for i in range(5):
line = resp.readline().decode().strip()
lines.append(line)
print(f" first 5 SSE lines: {lines}")
except Exception as e:
print(f" ai-core FAIL: {e}")
# ====== Step 6: memory-service 直调测试 ======
print("\n=== Step 6: memory-service 直调测试 ===")
r = urllib.request.Request("http://localhost:8091/api/v1/memory/search",
data=json.dumps({"query": "test", "user_id": "admin", "limit": 5}).encode(),
method="POST")
r.add_header("Content-Type", "application/json")
try:
resp = urllib.request.urlopen(r, timeout=5)
print(f" memory/search status={resp.status} body={resp.read().decode()[:200]}")
except Exception as e:
print(f" memory/search FAIL: {e}")
# ====== Step 7: tool-engine 直调测试 ======
print("\n=== Step 7: tool-engine 直调测试 ===")
r = urllib.request.Request("http://localhost:8092/api/v1/tools",
data=json.dumps({"action": "list"}).encode(),
method="POST")
r.add_header("Content-Type", "application/json")
try:
resp = urllib.request.urlopen(r, timeout=5)
print(f" tools/list status={resp.status} body={resp.read().decode()[:200]}")
except Exception as e:
print(f" tools/list FAIL: {e}")
# ====== Step 8: Gateway 路由检查 ======
print("\n=== Step 8: Gateway 路由检查 (哪些chat端点注册了) ===")
for path in ["/api/v1/chat", "/api/v1/chat/stream", "/api/v1/chat/send"]:
status, _ = req("POST", path, {"message": "test"}, token=token)
print(f" POST {path}: {status}")
# ====== Step 9: WebSocket 端点检查 ======
print("\n=== Step 9: WebSocket 端点基本测试 ===")
import socket
# 先检查 /ws/chat 是否可访问 (HTTP层面)
r = urllib.request.Request("http://localhost:8080/ws/chat")
try:
resp = urllib.request.urlopen(r, timeout=3)
print(f" GET /ws/chat (no upgrade): {resp.status}")
except urllib.error.HTTPError as e:
print(f" GET /ws/chat HTTP status: {e.code} body={e.read().decode()[:100]}")
except Exception as e:
print(f" GET /ws/chat: {e}")
# 保存 token 和 sid
with open(TOKEN_FILE, "w") as f:
f.write(token)
with open(SID_FILE, "w") as f:
f.write(sid or "")
print(f"\n[DONE] Token saved to {TOKEN_FILE}, sid={sid}")