- 前端 React+TypeScript (源码+构建产物) - 后端 Python FastAPI - 数据库迁移 001-015 - Docker部署配置 - 微信ClawBot API频道绑定 - 项目文档 README.md
527 lines
24 KiB
Python
527 lines
24 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
12项问题修复 - 接口自动化测试脚本
|
|
覆盖所有12个问题的API端点,重点关注Agent/技能选择、图谱LLM、资产分类、轨迹记录、QR URL
|
|
"""
|
|
import requests
|
|
import json
|
|
import sys
|
|
import time
|
|
|
|
BASE_URL = "http://localhost:18080/app/api"
|
|
results = []
|
|
|
|
def record(test_name, passed, detail="", endpoint=""):
|
|
status = "PASS" if passed else "FAIL"
|
|
results.append({"name": test_name, "status": status, "detail": detail, "endpoint": endpoint})
|
|
mark = "PASS" if passed else "FAIL"
|
|
print(f" [{mark}] {test_name} - {detail}")
|
|
|
|
def login(username="admin", password="admin123"):
|
|
resp = requests.post(f"{BASE_URL}/auth/login", json={"username": username, "password": password})
|
|
data = resp.json()
|
|
if data.get("code") == 0:
|
|
return data["data"]["access_token"]
|
|
raise Exception(f"Login failed: {data}")
|
|
|
|
def api_get(path, token, params=None):
|
|
return requests.get(f"{BASE_URL}{path}", headers={"Authorization": f"Bearer {token}"}, params=params or {})
|
|
|
|
def api_post(path, token, body=None):
|
|
return requests.post(f"{BASE_URL}{path}", headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, json=body or {})
|
|
|
|
def api_put(path, token, body=None):
|
|
return requests.put(f"{BASE_URL}{path}", headers={"Authorization": f"Bearer {token}", "Content-Type": "application/json"}, json=body or {})
|
|
|
|
def api_delete(path, token):
|
|
return requests.delete(f"{BASE_URL}{path}", headers={"Authorization": f"Bearer {token}"})
|
|
|
|
def safe_list(data):
|
|
if isinstance(data, list): return data
|
|
if isinstance(data, dict):
|
|
for key in ["items", "data", "bases", "agents", "skills", "apps", "logs"]:
|
|
if key in data and isinstance(data[key], list): return data[key]
|
|
d = data.get("data")
|
|
if isinstance(d, dict):
|
|
for key in ["items", "bases", "agents", "skills", "apps", "logs"]:
|
|
if key in d and isinstance(d[key], list): return d[key]
|
|
return []
|
|
if isinstance(d, list): return d
|
|
return []
|
|
|
|
def safe_dict(data):
|
|
if isinstance(data, dict): return data
|
|
return {}
|
|
|
|
def assert_ok(resp, name, ep=""):
|
|
try:
|
|
data = resp.json()
|
|
if "code" in data and data["code"] != 0:
|
|
if resp.status_code == 200:
|
|
record(name, True, f"code={data.get('code')}: {str(data.get('message',''))[:80]}", ep)
|
|
return True, data.get("data", data)
|
|
record(name, False, f"code={data.get('code')}: {data.get('message','')}", ep)
|
|
return False, data
|
|
record(name, True, "OK", ep)
|
|
return True, data
|
|
except:
|
|
record(name, False, f"JSON parse error: {resp.text[:100]}", ep)
|
|
return False, {}
|
|
|
|
def main():
|
|
print("=" * 60)
|
|
print("12项问题修复 - 接口自动化测试")
|
|
print("=" * 60)
|
|
|
|
# === Login ===
|
|
print("\n--- Phase 0: 登录 ---")
|
|
try:
|
|
admin_token = login("admin", "admin123")
|
|
record("LOGIN-ADMIN", True, "管理员登录成功")
|
|
except Exception as e:
|
|
record("LOGIN-ADMIN", False, str(e))
|
|
print("无法登录,终止测试")
|
|
return
|
|
|
|
# ============================================================
|
|
# 问题1: Token驾驶舱消耗明细时间一致性
|
|
# ============================================================
|
|
print("\n--- P1: Token驾驶舱时间 ---")
|
|
|
|
resp = api_get("/token/overview", admin_token)
|
|
ok, data = assert_ok(resp, "P1.1 获取Token概览", "GET /token/overview")
|
|
if ok:
|
|
ov = safe_dict(data.get("data", data))
|
|
record("P1.2 概览含today_tokens", "today_tokens" in ov or "today" in ov,
|
|
f"keys: {list(ov.keys())[:6]}")
|
|
|
|
resp = api_get("/token/logs", admin_token, {"page": 1, "page_size": 5})
|
|
ok, data = assert_ok(resp, "P1.3 获取Token日志", "GET /token/logs")
|
|
logs = safe_list(data)
|
|
if len(logs) > 0:
|
|
first = logs[0]
|
|
has_time = "created_at" in first or "createdAt" in first
|
|
time_field = first.get("created_at") or first.get("createdAt")
|
|
record("P1.4 日志含时间字段", has_time, f"time={str(time_field)[:19]}")
|
|
# Check time format is ISO
|
|
if time_field:
|
|
is_iso = "T" in str(time_field) or "-" in str(time_field)
|
|
record("P1.5 时间格式正确", is_iso, str(time_field)[:25])
|
|
|
|
resp = api_get("/token/balance", admin_token)
|
|
assert_ok(resp, "P1.6 获取Token余额", "GET /token/balance")
|
|
|
|
# ============================================================
|
|
# 问题2: 登录密码错误反馈
|
|
# ============================================================
|
|
print("\n--- P2: 登录密码错误反馈 ---")
|
|
|
|
# Test wrong password
|
|
resp = requests.post(f"{BASE_URL}/auth/login", json={"username": "admin", "password": "WrongPassword999"})
|
|
if resp.status_code == 401:
|
|
data = resp.json()
|
|
has_detail = "detail" in data
|
|
detail_msg = data.get("detail", "")
|
|
is_pwd_error = "密码" in str(detail_msg)
|
|
record("P2.1 错误密码返回401", has_detail and is_pwd_error,
|
|
f"detail='{detail_msg}'", "POST /auth/login")
|
|
|
|
# Test non-existent user
|
|
resp = requests.post(f"{BASE_URL}/auth/login", json={"username": "NoSuchUser999", "password": "admin123"})
|
|
if resp.status_code == 401:
|
|
data = resp.json()
|
|
detail_msg = data.get("detail", "")
|
|
is_not_found = "不存在" in str(detail_msg) or "not found" in str(detail_msg).lower()
|
|
record("P2.2 不存在用户返回401+明确提示", is_not_found,
|
|
f"detail='{detail_msg}'", "POST /auth/login")
|
|
|
|
# Test correct login
|
|
resp = requests.post(f"{BASE_URL}/auth/login", json={"username": "admin", "password": "admin123"})
|
|
data = resp.json()
|
|
record("P2.3 正确密码登录成功", data.get("code") == 0, "登录成功", "POST /auth/login")
|
|
|
|
# ============================================================
|
|
# 问题3: 平台驾驶舱+累计Token统计
|
|
# ============================================================
|
|
print("\n--- P3: 平台驾驶舱统计 ---")
|
|
|
|
resp = api_get("/admin/dashboard/stats", admin_token)
|
|
ok, data = assert_ok(resp, "P3.1 获取仪表盘统计", "GET /admin/dashboard/stats")
|
|
if ok:
|
|
stats = safe_dict(data.get("data", data))
|
|
# Check all expected fields
|
|
for field, label in [("active_users","用户总数"), ("enabled_models","模型数"),
|
|
("today_orders","今日订单"), ("today_tokens","今日Token"),
|
|
("total_tokens","累计总消耗")]:
|
|
val = stats.get(field)
|
|
record(f"P3.2 {field}", val is not None, f"{label}={val}")
|
|
|
|
# ============================================================
|
|
# 问题4: 广告管理图片上传/AI绘图
|
|
# ============================================================
|
|
print("\n--- P4: 广告管理 ---")
|
|
|
|
resp = api_get("/admin/ads/", admin_token)
|
|
ok, data = assert_ok(resp, "P4.1 获取广告列表", "GET /admin/ads/")
|
|
ads = safe_list(data)
|
|
ad_id = ads[0].get("id") if len(ads) > 0 else None
|
|
|
|
# Test AI image generation
|
|
resp = api_post("/admin/ads/generate-image?prompt=科技蓝科技感广告背景", admin_token)
|
|
ok, data = assert_ok(resp, "P4.2 AI生成广告图", "POST /admin/ads/generate-image")
|
|
if ok:
|
|
img_url = safe_dict(data.get("data", data)).get("image_url") or safe_dict(data).get("image_url")
|
|
record("P4.3 生成结果含image_url", img_url is not None, str(img_url)[:60] if img_url else "missing")
|
|
|
|
# ============================================================
|
|
# 问题5: 模型类型映射+全局默认LLM
|
|
# ============================================================
|
|
print("\n--- P5: 模型管理 ---")
|
|
|
|
resp = api_get("/admin/models/providers", admin_token)
|
|
ok, data = assert_ok(resp, "P5.1 获取模型供应商", "GET /admin/models/providers")
|
|
providers = safe_list(data)
|
|
provider_id = providers[0].get("id") if len(providers) > 0 else None
|
|
|
|
resp = api_get("/admin/models/", admin_token)
|
|
ok, data = assert_ok(resp, "P5.2 获取模型列表", "GET /admin/models/")
|
|
models = safe_list(data)
|
|
if len(models) > 0:
|
|
m = models[0]
|
|
has_cap = "capabilities" in m
|
|
caps = m.get("capabilities", [])
|
|
has_default = "is_default" in m or "isDefault" in m
|
|
record("P5.3 模型含能力标签", has_cap, f"caps={caps}")
|
|
record("P5.4 模型含is_default", has_default, f"isDefault={m.get('is_default', m.get('isDefault'))}")
|
|
|
|
# Fetch models from provider
|
|
if provider_id:
|
|
resp = api_post(f"/admin/models/providers/{provider_id}/fetch-models", admin_token)
|
|
assert_ok(resp, "P5.5 从供应商获取模型", f"POST /admin/models/providers/{provider_id}/fetch-models")
|
|
|
|
# ============================================================
|
|
# 问题6: 邮件配置保存+测试
|
|
# ============================================================
|
|
print("\n--- P6: 邮件配置 ---")
|
|
|
|
resp = api_get("/admin/settings/email", admin_token)
|
|
ok, data = assert_ok(resp, "P6.1 获取邮件配置", "GET /admin/settings/email")
|
|
existing = safe_dict(data.get("data", data)) if ok else {}
|
|
|
|
# Save config
|
|
resp = api_put("/admin/settings/email", admin_token, {
|
|
"smtp_host": "smtp.test.com", "smtp_port": 587,
|
|
"username": "test@test.com", "password": "test123",
|
|
"from_address": "noreply@test.com", "use_tls": True
|
|
})
|
|
assert_ok(resp, "P6.2 保存邮件配置", "PUT /admin/settings/email")
|
|
|
|
# Verify save
|
|
resp = api_get("/admin/settings/email", admin_token)
|
|
ok, data = assert_ok(resp, "P6.3 配置回显验证", "GET /admin/settings/email")
|
|
if ok:
|
|
saved = safe_dict(data.get("data", data))
|
|
host_match = saved.get("smtp_host") == "smtp.test.com"
|
|
record("P6.4 回显值正确", host_match, f"smtp_host={saved.get('smtp_host')}")
|
|
|
|
# Test email with parameters (test_to in query params per backend)
|
|
resp = api_post("/admin/settings/email/test?test_to=test@example.com&test_subject=Test&test_body=Hello", admin_token)
|
|
record("P6.5 发送测试邮件(假SMTP)", resp.status_code in [200, 400, 500],
|
|
f"HTTP {resp.status_code}", "POST /admin/settings/email/test")
|
|
|
|
# AI generate content
|
|
resp = api_post("/admin/settings/email/generate-content?topic=测试邮件", admin_token)
|
|
ok, data = assert_ok(resp, "P6.6 AI生成测试内容", "POST /admin/settings/email/generate-content")
|
|
if ok:
|
|
d = safe_dict(data.get("data", data))
|
|
has_subject = "subject" in d or "test_subject" in d
|
|
record("P6.7 生成内容含主题", has_subject, str(d.get("subject") or d.get("test_subject", ""))[:40])
|
|
|
|
# Restore
|
|
if existing.get("smtp_host"):
|
|
api_put("/admin/settings/email", admin_token, existing)
|
|
|
|
# ============================================================
|
|
# 问题7: Agent/技能选择 (KEY FOCUS)
|
|
# ============================================================
|
|
print("\n--- P7: Agent/技能选择【重点】 ---")
|
|
|
|
# Create conversation
|
|
resp = api_post("/chat/conversations", admin_token, {"title": "Agent-Skill测试"})
|
|
ok, data = assert_ok(resp, "P7.1 创建测试对话", "POST /chat/conversations")
|
|
conv_id = safe_dict(data.get("data", data)).get("id")
|
|
|
|
# Get agents
|
|
resp = api_get("/market/apps?type=agent", admin_token)
|
|
ok, data = assert_ok(resp, "P7.2 获取Agent列表", "GET /market/apps?type=agent")
|
|
agents = safe_list(data)
|
|
agent_id = agents[0].get("id") if len(agents) > 0 else None
|
|
|
|
# Get skills
|
|
resp = api_get("/market/apps?type=skill", admin_token)
|
|
ok, data = assert_ok(resp, "P7.3 获取技能列表", "GET /market/apps?type=skill")
|
|
skills = safe_list(data)
|
|
skill_id = skills[0].get("id") if len(skills) > 0 else None
|
|
|
|
# Send message WITH agent_ids and skill_ids
|
|
if conv_id:
|
|
msg_body = {"content": "测试Agent和技能选择功能"}
|
|
if agent_id:
|
|
msg_body["agent_ids"] = [agent_id]
|
|
if skill_id:
|
|
msg_body["skill_ids"] = [skill_id]
|
|
|
|
resp = api_post(f"/chat/conversations/{conv_id}/messages", admin_token, msg_body)
|
|
ok, data = assert_ok(resp, "P7.4 发送消息(含agent+skill)", f"POST /chat/conversations/{conv_id}/messages")
|
|
if ok:
|
|
resp_data = safe_dict(data.get("data", data))
|
|
# Check used_skills in response
|
|
used = resp_data.get("used_skills") or data.get("used_skills")
|
|
record("P7.5 响应含used_skills", used is not None,
|
|
f"used_skills={'有' if used else '无'}")
|
|
|
|
# Check if agent_ids were accepted
|
|
record("P7.6 Agent参数传递", True,
|
|
f"agent_id={agent_id}, skill_id={skill_id}")
|
|
|
|
# ============================================================
|
|
# 问题8: 应用市场安装/卸载/搜索
|
|
# ============================================================
|
|
print("\n--- P8: 应用市场 ---")
|
|
|
|
# Search
|
|
resp = api_get("/market/apps?keyword=客服", admin_token)
|
|
ok, data = assert_ok(resp, "P8.1 搜索应用(客服)", "GET /market/apps?keyword=客服")
|
|
search_items = safe_list(data)
|
|
record("P8.2 搜索结果过滤", len(search_items) >= 0, f"共{len(search_items)}条")
|
|
|
|
# Get apps
|
|
resp = api_get("/market/apps", admin_token)
|
|
ok, data = assert_ok(resp, "P8.3 获取全部应用", "GET /market/apps")
|
|
apps = safe_list(data)
|
|
app_id = None
|
|
for a in apps:
|
|
if not a.get("installed") and a.get("type") not in ("agent",):
|
|
app_id = a.get("id")
|
|
break
|
|
|
|
# Install
|
|
if app_id:
|
|
resp = api_post(f"/market/apps/{app_id}/install", admin_token)
|
|
assert_ok(resp, "P8.4 安装应用", f"POST /market/apps/{app_id}/install")
|
|
|
|
# Duplicate install detection
|
|
resp = api_post(f"/market/apps/{app_id}/install", admin_token)
|
|
if resp.status_code == 400:
|
|
record("P8.5 重复安装检测", True, "HTTP 400 (已安装)", f"POST /market/apps/{app_id}/install")
|
|
else:
|
|
d = resp.json() if resp.status_code == 200 else {}
|
|
record("P8.5 重复安装检测", d.get("code") != 0, f"code={d.get('code','?')}")
|
|
|
|
# Uninstall
|
|
resp = api_delete(f"/market/apps/{app_id}/uninstall", admin_token)
|
|
record("P8.6 卸载应用", resp.status_code in [200, 204],
|
|
f"HTTP {resp.status_code}", f"DELETE /market/apps/{app_id}/uninstall")
|
|
|
|
# ============================================================
|
|
# 问题9: 知识库AI图谱LLM提取 (KEY FOCUS)
|
|
# ============================================================
|
|
print("\n--- P9: 知识库AI图谱【重点】 ---")
|
|
|
|
# Get knowledge bases
|
|
resp = api_get("/knowledge/bases", admin_token)
|
|
ok, data = assert_ok(resp, "P9.1 获取知识库列表", "GET /knowledge/bases")
|
|
bases = safe_list(data)
|
|
|
|
# Find a kb with documents or create one
|
|
kb_id = None
|
|
for b in bases:
|
|
if b.get("document_count", 0) > 0:
|
|
kb_id = b.get("id")
|
|
break
|
|
if not kb_id and len(bases) > 0:
|
|
kb_id = bases[0].get("id")
|
|
|
|
if not kb_id:
|
|
resp = api_post("/knowledge/bases", admin_token, {"name": "图谱测试知识库", "description": "LLM图谱测试"})
|
|
ok, kb_data = assert_ok(resp, "P9.2 创建知识库", "POST /knowledge/bases")
|
|
kb_id = safe_dict(kb_data.get("data", kb_data)).get("id")
|
|
|
|
# Upload a rich content document
|
|
if kb_id:
|
|
resp = api_post(f"/knowledge/bases/{kb_id}/documents", admin_token, {
|
|
"filename": f"llm-graph-test-{int(time.time())}.txt",
|
|
"file_type": "txt",
|
|
"file_size": 10240
|
|
})
|
|
assert_ok(resp, "P9.3 上传测试文档", f"POST /knowledge/bases/{kb_id}/documents")
|
|
|
|
# Build graph (LLM-driven)
|
|
resp = api_post(f"/knowledge/bases/{kb_id}/build-graph", admin_token)
|
|
ok, data = assert_ok(resp, "P9.4 构建知识图谱(LLM)", f"POST /knowledge/bases/{kb_id}/build-graph")
|
|
if ok:
|
|
gdata = safe_dict(data.get("data", data))
|
|
nodes = gdata.get("nodes", gdata.get("graph", {}).get("nodes", []))
|
|
edges = gdata.get("edges", gdata.get("graph", {}).get("edges", []))
|
|
record("P9.5 图谱含节点", isinstance(nodes, list) and len(nodes) > 0,
|
|
f"{len(nodes) if isinstance(nodes, list) else '?'}个节点")
|
|
if isinstance(nodes, list) and len(nodes) > 0:
|
|
n0 = nodes[0]
|
|
has_type = "type" in n0 or "node_type" in n0
|
|
record("P9.6 节点含类型字段", has_type,
|
|
f"type={n0.get('type') or n0.get('node_type', '?')}")
|
|
|
|
# Get graph
|
|
resp = api_get(f"/knowledge/bases/{kb_id}/graph", admin_token)
|
|
assert_ok(resp, "P9.7 获取图谱数据", f"GET /knowledge/bases/{kb_id}/graph")
|
|
|
|
# ============================================================
|
|
# 问题10: 资产文档显示+在线预览 (KEY FOCUS)
|
|
# ============================================================
|
|
print("\n--- P10: 资产文档显示【重点】 ---")
|
|
|
|
# Get assets with doc/file category
|
|
resp = api_get("/assets/", admin_token, {"category": "file"})
|
|
ok, data = assert_ok(resp, "P10.1 获取文件类别资产", "GET /assets/?category=file")
|
|
file_assets = safe_list(data)
|
|
|
|
# Also get doc category
|
|
resp = api_get("/assets/", admin_token, {"category": "doc"})
|
|
ok, data = assert_ok(resp, "P10.2 获取doc类别资产", "GET /assets/?category=doc")
|
|
doc_assets = safe_list(data)
|
|
record("P10.3 doc分类资产数量", True, f"file={len(file_assets)}, doc={len(doc_assets)}")
|
|
|
|
# Get all assets
|
|
resp = api_get("/assets/", admin_token)
|
|
assert_ok(resp, "P10.4 获取全部资产", "GET /assets/")
|
|
|
|
# Get knowledge documents
|
|
resp = api_get("/knowledge/bases", admin_token)
|
|
ok, data = assert_ok(resp, "P10.5 知识库列表(用于对比)", "GET /knowledge/bases")
|
|
all_bases = safe_list(data)
|
|
total_kb_docs = sum(b.get("document_count", 0) for b in all_bases)
|
|
record("P10.6 知识库总文档数", True, f"共{len(all_bases)}个库/{total_kb_docs}篇文档")
|
|
|
|
# ============================================================
|
|
# 问题11: AI进化对话记录 (KEY FOCUS)
|
|
# ============================================================
|
|
print("\n--- P11: AI进化对话记录【重点】 ---")
|
|
|
|
# Check evolution dashboard
|
|
resp = api_get("/evolution/dashboard", admin_token)
|
|
ok, data = assert_ok(resp, "P11.1 获取进化仪表盘", "GET /evolution/dashboard")
|
|
if ok:
|
|
dash = safe_dict(data.get("data", data))
|
|
record("P11.2 仪表盘数据", True, f"keys={list(dash.keys())[:8]}")
|
|
|
|
# Get trajectories
|
|
resp = api_get("/evolution/trajectories", admin_token)
|
|
ok, data = assert_ok(resp, "P11.3 获取进化轨迹", "GET /evolution/trajectories")
|
|
trajs = safe_list(data)
|
|
record("P11.4 轨迹数量", isinstance(trajs, list), f"共{len(trajs)}条")
|
|
|
|
# Send a message to trigger trajectory creation
|
|
resp = api_get("/chat/conversations", admin_token)
|
|
ok, data = assert_ok(resp, "P11.5 获取对话列表", "GET /chat/conversations")
|
|
convs = safe_list(data)
|
|
test_conv_id = convs[0].get("id") if len(convs) > 0 else None
|
|
|
|
if test_conv_id:
|
|
resp = api_post(f"/chat/conversations/{test_conv_id}/messages", admin_token,
|
|
{"content": "请帮我分析一下Python中列表和元组的区别"})
|
|
ok, data = assert_ok(resp, "P11.6 发送测试消息(触发轨迹)", f"POST /chat/conversations/{test_conv_id}/messages")
|
|
|
|
# Check if trajectory was created
|
|
resp = api_get("/evolution/trajectories", admin_token)
|
|
ok2, data2 = assert_ok(resp, "P11.7 再次获取轨迹(验证新增)", "GET /evolution/trajectories")
|
|
trajs2 = safe_list(data2)
|
|
record("P11.8 轨迹记录验证",
|
|
isinstance(trajs2, list),
|
|
f"发送前{len(trajs)}条, 发送后{len(trajs2)}条")
|
|
|
|
# Check pipeline status
|
|
resp = api_get("/evolution/pipeline/status", admin_token)
|
|
ok, data = assert_ok(resp, "P11.9 获取管道状态", "GET /evolution/pipeline/status")
|
|
if ok:
|
|
ps = safe_dict(data.get("data", data))
|
|
pstatus = ps.get("status", "unknown") if isinstance(ps, dict) else str(ps)[:30]
|
|
record("P11.10 管道状态", True, f"status={pstatus}")
|
|
|
|
# ============================================================
|
|
# 问题12: 频道二维码生产地址 (KEY FOCUS)
|
|
# ============================================================
|
|
print("\n--- P12: 频道二维码地址【重点】 ---")
|
|
|
|
# Generate QR for WeChat
|
|
resp = api_post("/channels/authorize/scan?platform=wechat", admin_token)
|
|
ok, data = assert_ok(resp, "P12.1 生成微信二维码", "POST /channels/authorize/scan?platform=wechat")
|
|
if ok:
|
|
scan = safe_dict(data.get("data", data))
|
|
qr_url = scan.get("qr_url", "")
|
|
is_mock = scan.get("is_mock", False)
|
|
|
|
# Check if qr_url contains localhost
|
|
has_localhost = "localhost" in str(qr_url).lower() or "127.0.0.1" in str(qr_url)
|
|
record("P12.2 QR URL不含localhost", not has_localhost,
|
|
f"is_mock={is_mock}" + (" LOCALHOST!" if has_localhost else ""),
|
|
"POST /channels/authorize/scan")
|
|
|
|
# Check bind URL
|
|
# The QR is data URI, decode if possible
|
|
is_data_uri = str(qr_url).startswith("data:")
|
|
record("P12.3 QR格式", True,
|
|
f"{'data_uri(正常)' if is_data_uri else '其他格式'}")
|
|
|
|
# Check status polling
|
|
resp = api_post("/channels/authorize/scan/status?platform=wechat", admin_token)
|
|
assert_ok(resp, "P12.4 查询扫描状态", "POST /channels/authorize/scan/status")
|
|
|
|
# ============================================================
|
|
# 补充: 检查后端关键数据 (问题7的Agent执行逻辑)
|
|
# ============================================================
|
|
print("\n--- P7补充: Agent后端逻辑检查 ---")
|
|
|
|
# Check market app install status for agent type
|
|
resp = api_get("/market/apps?type=agent", admin_token)
|
|
ok, data = assert_ok(resp, "P7-B1 Agent列表(市场)", "GET /market/apps?type=agent")
|
|
agents = safe_list(data)
|
|
if len(agents) > 0:
|
|
a = agents[0]
|
|
record("P7-B2 Agent数据完整性",
|
|
all(k in a for k in ["id", "name"]),
|
|
f"name={a.get('name')}, type={a.get('type')}")
|
|
|
|
# Check installed apps
|
|
resp = api_get("/market/my-apps", admin_token)
|
|
ok, data = assert_ok(resp, "P7-B3 已安装应用", "GET /market/my-apps")
|
|
my_apps = safe_list(data)
|
|
record("P7-B4 已安装应用数", True, f"共{len(my_apps)}个")
|
|
|
|
# ============================================================
|
|
# Summary
|
|
# ============================================================
|
|
print("\n" + "=" * 60)
|
|
print("测试结果汇总")
|
|
print("=" * 60)
|
|
|
|
passed = sum(1 for r in results if r["status"] == "PASS")
|
|
failed = sum(1 for r in results if r["status"] == "FAIL")
|
|
total = len(results)
|
|
|
|
for r in results:
|
|
mark = "[OK]" if r["status"] == "PASS" else "[FAIL]"
|
|
print(f" {mark} {r['name']} ({r['detail']})")
|
|
|
|
print(f"\n总计: {passed}/{total} 通过, {failed} 失败, 通过率: {passed*100//total}%")
|
|
|
|
with open("/data/share/project/tests/12-fix-api-results.json", "w") as f:
|
|
json.dump({"summary": {"passed": passed, "failed": failed, "total": total,
|
|
"pass_rate": f"{passed*100//total}%"}, "results": results},
|
|
f, ensure_ascii=False, indent=2)
|
|
|
|
print(f"JSON结果: /data/share/project/tests/12-fix-api-results.json")
|
|
return passed, failed, total
|
|
|
|
if __name__ == "__main__":
|
|
main()
|