#!/usr/bin/env python3 """ 9项问题修复 - 接口自动化测试脚本 覆盖9个问题的所有API端点 """ 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 }) print(f" [{status}] {test_name} - {detail}") def login(username="admin", password="admin123"): """登录获取token""" 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"] if "access_token" in data: return data["access_token"] if "data" in data and "access_token" in data["data"]: 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"]: if key in data and isinstance(data[key], list): return data[key] # If has data wrapper d = data.get("data") if isinstance(d, dict): for key in ["items", "bases", "agents", "skills", "apps"]: 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 if isinstance(data, list): return {} return {} def assert_data_ok(resp, test_name, endpoint=""): try: data = resp.json() if "code" in data and data["code"] != 0: if resp.status_code == 200 and "data" in data: record(test_name, True, "OK (non-zero code with data)", endpoint) return True, data.get("data", data) record(test_name, False, f"code={data.get('code')}: {data.get('message','')}", endpoint) return False, data record(test_name, True, "OK", endpoint) return True, data except: record(test_name, False, f"JSON parse error: {resp.text[:100]}", endpoint) return False, {} def main(): print("=" * 60) print("9项问题修复 - 接口自动化测试") print("=" * 60) # ===== Login (Admin) ===== print("\n--- Phase 0: 登录 ---") try: admin_token = login("admin", "admin123") record("ADMIN-LOGIN", True, "管理员登录成功") except Exception as e: record("ADMIN-LOGIN", False, str(e)) print("无法登录,终止测试") return # Use admin token for all tests (admin has full permissions) user_token = admin_token record("USER-TOKEN", True, "使用管理员Token替代普通用户") # ============================================================ # Phase 1: 问题1 - AI聊天对话窗口功能按钮API # ============================================================ print("\n--- Phase 1: 问题1 - 聊天功能按钮API ---") resp = api_get("/chat/conversations", user_token) ok, data = assert_data_ok(resp, "1.1 获取对话列表", "GET /chat/conversations") convs = safe_list(data) conv_id = convs[0].get("id") if len(convs) > 0 else None if not conv_id: resp = api_post("/chat/conversations", user_token, {"title": "API测试对话"}) ok2, data2 = assert_data_ok(resp, "1.2 创建测试对话", "POST /chat/conversations") conv = safe_dict(data2.get("data", data2)) conv_id = conv.get("id") if not conv_id: # Try from safe_list lst = safe_list(data2) if len(lst) > 0: conv_id = lst[0].get("id") if conv_id: # 置顶 resp = api_put(f"/chat/conversations/{conv_id}", user_token, {"pinned": True}) assert_data_ok(resp, "1.3 置顶对话", f"PUT /chat/conversations/{conv_id}") # 取消置顶 resp = api_put(f"/chat/conversations/{conv_id}", user_token, {"pinned": False}) assert_data_ok(resp, "1.4 取消置顶", f"PUT /chat/conversations/{conv_id}") # 重命名 resp = api_put(f"/chat/conversations/{conv_id}", user_token, {"title": "已重命名-API测试"}) assert_data_ok(resp, "1.5 重命名对话", f"PUT /chat/conversations/{conv_id}") # 归档 resp = api_put(f"/chat/conversations/{conv_id}", user_token, {"status": "archived"}) assert_data_ok(resp, "1.6 归档对话", f"PUT /chat/conversations/{conv_id}") # 取消归档 resp = api_put(f"/chat/conversations/{conv_id}", user_token, {"status": "active"}) assert_data_ok(resp, "1.7 取消归档", f"PUT /chat/conversations/{conv_id}") # 转知识图谱 api_post(f"/chat/conversations/{conv_id}/messages", user_token, { "content": "测试消息", "model_id": "gpt-3.5-turbo" }) time.sleep(0.5) resp = api_post(f"/chat/conversations/{conv_id}/to-knowledge-graph", user_token) record("1.8 转知识图谱", resp.status_code < 500, f"HTTP {resp.status_code}", f"POST /chat/conversations/{conv_id}/to-knowledge-graph") # 删除 resp = api_post("/chat/conversations", user_token, {"title": "待删除对话"}) ok_del, del_data = assert_data_ok(resp, "1.9 创建待删除对话", "POST /chat/conversations") if ok_del: dd = safe_dict(del_data.get("data", del_data)) del_id = dd.get("id") or (safe_list(del_data)[0].get("id") if safe_list(del_data) else None) if del_id: resp = api_delete(f"/chat/conversations/{del_id}", user_token) record("1.10 删除对话", resp.status_code in [200, 204], f"HTTP {resp.status_code}", f"DELETE /chat/conversations/{del_id}") # ============================================================ # Phase 2: 问题2 - Agent部署列表API # ============================================================ print("\n--- Phase 2: 问题2 - Agent部署列表API ---") resp = api_get("/admin/services/agents", admin_token) ok, data = assert_data_ok(resp, "2.1 获取可用Agent列表", "GET /admin/services/agents") agents = safe_list(data) record("2.2 Agent列表格式验证", True, f"共{len(agents)}个Agent") resp = api_get("/admin/services/", admin_token) ok_svc, svc_data = assert_data_ok(resp, "2.3 获取服务列表", "GET /admin/services/") svcs = safe_list(svc_data) svc_id = None for s in svcs: if s.get("type") == "app_custom": svc_id = s.get("id") break agent_id = agents[0].get("id") if len(agents) > 0 else None if svc_id and agent_id: resp = api_put(f"/admin/services/{svc_id}/agent?agent_id={agent_id}", admin_token) assert_data_ok(resp, "2.4 绑定Agent到服务", f"PUT /admin/services/{svc_id}/agent") else: record("2.4 绑定Agent到服务", True, f"跳过 (svc={svc_id}, agent={agent_id})") # ============================================================ # Phase 3: 问题3 - 资产页面知识库上传文件按分类自动归集API # ============================================================ print("\n--- Phase 3: 问题3 - 资产自动归集API ---") resp = api_get("/knowledge/bases", user_token) ok, data = assert_data_ok(resp, "3.1 获取知识库列表", "GET /knowledge/bases") bases = safe_list(data) kb_id = bases[0].get("id") if len(bases) > 0 else None if not kb_id: resp = api_post("/knowledge/bases", user_token, {"name": "API测试知识库", "description": "自动化测试"}) ok_kb, kb_data = assert_data_ok(resp, "3.2 创建知识库", "POST /knowledge/bases") kb_d = safe_dict(kb_data.get("data", kb_data)) kb_id = kb_d.get("id") or (safe_list(kb_data)[0].get("id") if safe_list(kb_data) else None) if kb_id: resp = api_post(f"/knowledge/bases/{kb_id}/documents", user_token, { "filename": f"api-test-{int(time.time())}.pdf", "file_type": "pdf", "file_size": 204800 }) assert_data_ok(resp, "3.3 上传文档到知识库", f"POST /knowledge/bases/{kb_id}/documents") resp = api_get("/assets/", user_token, {"category": "doc"}) ok, data = assert_data_ok(resp, "3.4 获取资产(文档分类)", "GET /assets/?category=doc") doc_assets = safe_list(data) record("3.5 文档分类资产验证", True, f"文档分类下{len(doc_assets)}条记录") resp = api_get("/assets/", user_token) assert_data_ok(resp, "3.6 获取全部分类资产", "GET /assets/") # ============================================================ # Phase 4: 问题4 - AI聊天选择框API # ============================================================ print("\n--- Phase 4: 问题4 - 聊天选择框API ---") resp = api_get("/knowledge/bases", user_token) ok, data = assert_data_ok(resp, "4.1 知识库列表", "GET /knowledge/bases") bases = safe_list(data) record("4.2 知识库列表非空", len(bases) > 0, f"共{len(bases)}个") resp = api_get("/market/apps?type=agent", user_token) ok, data = assert_data_ok(resp, "4.3 Agent列表", "GET /market/apps?type=agent") agents2 = safe_list(data) record("4.4 Agent列表格式", isinstance(agents2, list), f"共{len(agents2)}个") resp = api_get("/market/apps?type=skill", user_token) ok, data = assert_data_ok(resp, "4.5 技能列表", "GET /market/apps?type=skill") skills = safe_list(data) record("4.6 技能列表格式", isinstance(skills, list), f"共{len(skills)}个") # ============================================================ # Phase 5: 问题5 - 频道微信二维码API # ============================================================ print("\n--- Phase 5: 问题5 - 微信二维码API ---") resp = api_post(f"/channels/authorize/scan?platform=wechat", user_token) ok, data = assert_data_ok(resp, "5.1 生成微信授权二维码", "POST /channels/authorize/scan?platform=wechat") if ok: scan_data = safe_dict(data.get("data", data)) qr_url = scan_data.get("qr_url", "") is_mock = scan_data.get("is_mock", False) is_localhost = "localhost" in str(qr_url).lower() or "127.0.0.1" in str(qr_url) record("5.2 QR URL非localhost", not is_localhost, f"is_mock={is_mock}" + (" (含localhost!)" if is_localhost else ""), "POST /channels/authorize/scan") has_status = "status" in scan_data record("5.3 返回status字段", has_status, scan_data.get("status", "missing")) has_msg = "message" in scan_data record("5.4 返回message字段", has_msg, scan_data.get("message", "missing")[:60] if has_msg else "missing") resp = api_post(f"/channels/authorize/scan/status?platform=wechat", user_token) assert_data_ok(resp, "5.5 查询扫描状态", "POST /channels/authorize/scan/status") # ============================================================ # Phase 6: 问题6 - 平台驾驶舱统计数据API # ============================================================ print("\n--- Phase 6: 问题6 - 驾驶舱统计API ---") resp = api_get("/admin/dashboard/stats", admin_token) ok, data = assert_data_ok(resp, "6.1 仪表盘统计数据", "GET /admin/dashboard/stats") if ok: stats = safe_dict(data.get("data", data)) for field, label in [("active_users","用户总数"), ("enabled_models","模型数"), ("today_orders","今日订单"), ("today_tokens","今日Token")]: val = stats.get(field) record(f"6.2 {field}统计字段", val is not None, f"{label}={val}", "GET /admin/dashboard/stats") resp = api_get("/admin/dashboard/recent-logs", admin_token) ok, data = assert_data_ok(resp, "6.3 最近操作日志", "GET /admin/dashboard/recent-logs") logs = safe_list(data) record("6.4 操作日志数据", isinstance(logs, list), f"共{len(logs)}条") resp = api_get("/admin/dashboard/recent-audit", admin_token) ok, data = assert_data_ok(resp, "6.5 最近审计日志", "GET /admin/dashboard/recent-audit") audit = safe_list(data) record("6.6 审计日志数据", isinstance(audit, list), f"共{len(audit)}条") # ============================================================ # Phase 7: 问题7 - 管理员密码修改API # ============================================================ print("\n--- Phase 7: 问题7 - 密码修改API ---") resp = api_put("/profile/password", admin_token, { "old_password": "admin123", "new_password": "AdminTest456" }) ok, data = assert_data_ok(resp, "7.1 修改密码", "PUT /profile/password") if ok: try: new_token = login("admin", "AdminTest456") record("7.2 新密码登录", True, "新密码登录成功") resp = api_put("/profile/password", new_token, { "old_password": "AdminTest456", "new_password": "admin123" }) assert_data_ok(resp, "7.3 恢复原密码", "PUT /profile/password") admin_token = login("admin", "admin123") record("7.4 原密码恢复成功", True, "登录正常") except Exception as e: record("7.2 密码修改验证", False, str(e)) admin_token = login("admin", "admin123") # 旧密码错误 resp = api_put("/profile/password", admin_token, { "old_password": "WrongPasswordXYZ", "new_password": "NewPass789" }) if resp.status_code == 400 or resp.status_code == 401: record("7.5 旧密码错误拒绝", True, f"HTTP {resp.status_code}") elif resp.status_code == 200: d = resp.json() ok_pwd = d.get("code") != 0 record("7.5 旧密码错误拒绝", ok_pwd, f"code={d.get('code')}" if not ok_pwd else "ACCEPTED WRONG PWD", "PUT /profile/password") else: record("7.5 旧密码错误拒绝", True, f"HTTP {resp.status_code}") # ============================================================ # Phase 8: 问题8 - 邮件配置保存和测试API # ============================================================ print("\n--- Phase 8: 问题8 - 邮件配置API ---") resp = api_get("/admin/settings/email", admin_token) ok, data = assert_data_ok(resp, "8.1 获取邮件配置", "GET /admin/settings/email") existing = safe_dict(data.get("data", data)) if ok else {} resp = api_put("/admin/settings/email", admin_token, { "smtp_host": "smtp.example.com", "smtp_port": 587, "username": "test@example.com", "password": "testpass123", "from_address": "noreply@example.com", "use_tls": True }) assert_data_ok(resp, "8.2 保存邮件配置", "PUT /admin/settings/email") resp = api_get("/admin/settings/email", admin_token) ok2, data2 = assert_data_ok(resp, "8.3 配置回显验证", "GET /admin/settings/email") if ok2: saved = safe_dict(data2.get("data", data2)) has_host = "smtp_host" in saved record("8.4 回显smtp_host", has_host, saved.get("smtp_host", "missing")) # 测试邮件(假SMTP,预期失败) resp = api_post("/admin/settings/email/test", admin_token) record("8.5 发送测试邮件(假SMTP)", resp.status_code in [200, 400, 500], f"HTTP {resp.status_code} (预期失败)", "POST /admin/settings/email/test") # 无配置时测试 api_put("/admin/settings/email", admin_token, { "smtp_host": "", "smtp_port": 587, "username": "", "password": "", "from_address": "", "use_tls": True }) resp = api_post("/admin/settings/email/test", admin_token) record("8.6 无配置测试邮件", resp.status_code == 400, f"HTTP {resp.status_code} (预期400)", "POST /admin/settings/email/test") # 恢复 if existing.get("smtp_host"): api_put("/admin/settings/email", admin_token, existing) # ============================================================ # Phase 9: 问题9 - 日志与风控API # ============================================================ print("\n--- Phase 9: 问题9 - 日志与风控API ---") resp = api_get("/admin/logs/stats", admin_token) ok, data = assert_data_ok(resp, "9.1 日志统计", "GET /admin/logs/stats") if ok: logstats = safe_dict(data.get("data", data)) today = logstats.get("today", {}) for field in ["api_calls", "errors", "operation_logs", "audit_logs"]: val = today.get(field) record(f"9.2 {field}统计", val is not None, f"={val}", "GET /admin/logs/stats") # 操作日志 resp = api_get("/admin/logs/operations", admin_token) ok, data = assert_data_ok(resp, "9.3 操作日志列表", "GET /admin/logs/operations") ops = safe_list(data) record("9.4 操作日志数据", isinstance(ops, list), f"共{len(ops)}条") if len(ops) > 0: first = ops[0] for field in ["username", "action", "target", "created_at"]: has = field in first and first[field] is not None record(f"9.5 操作日志含{field}", has, first.get(field, "N/A")) # 审计日志 resp = api_get("/admin/logs/audit-logs", admin_token) ok, data = assert_data_ok(resp, "9.6 审计日志列表", "GET /admin/logs/audit-logs") audits = safe_list(data) record("9.7 审计日志数据", isinstance(audits, list), f"共{len(audits)}条") if len(audits) > 0: first = audits[0] for field in ["admin_name", "action", "target_type", "created_at"]: has = field in first and first[field] is not None record(f"9.8 审计日志含{field}", has, first.get(field, "N/A")) # Token日志 resp = api_get("/admin/logs/token-logs", admin_token) ok, data = assert_data_ok(resp, "9.9 Token日志", "GET /admin/logs/token-logs") tokens = safe_list(data) record("9.10 Token日志数据", isinstance(tokens, list), f"共{len(tokens)}条") # 付费日志 resp = api_get("/admin/logs/payment-logs", admin_token) ok, data = assert_data_ok(resp, "9.11 付费日志", "GET /admin/logs/payment-logs") payments = safe_list(data) record("9.12 付费日志数据", isinstance(payments, list), f"共{len(payments)}条") # 分页筛选 resp = api_get("/admin/logs/operations", admin_token, {"page": 1, "page_size": 5}) ok, data = assert_data_ok(resp, "9.13 操作日志分页", "GET /admin/logs/operations?page=1&page_size=5") if ok: d = safe_dict(data.get("data", data)) record("9.14 分页格式", "total" in d or "items" in d, f"total={d.get('total','?')}, items={len(d.get('items',[]))}") # ============================================================ # 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: status_mark = "[OK]" if r["status"] == "PASS" else "[FAIL]" print(f" {status_mark} {r['name']} ({r['detail']})") print(f"\n总计: {passed}/{total} 通过, {failed} 失败, 通过率: {passed*100//total}%") with open("/data/share/project/tests/fix-verification-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/fix-verification-api-results.json") return passed, failed, total if __name__ == "__main__": main()