run_tests.py 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import sys
  2. sys.path.insert(0, ".")
  3. from datetime import datetime
  4. from fastapi.testclient import TestClient
  5. from app.main import app
  6. client = TestClient(app)
  7. START = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  8. results = {}
  9. def j(r):
  10. return r.json()
  11. def tc(module, name, fn):
  12. try:
  13. ok, detail = fn()
  14. ds = str(detail)[:120]
  15. results.setdefault(module, []).append((name, ok, ds))
  16. print(f" [{'PASS' if ok else 'FAIL'}] {module}/{name}: {ds[:100]}")
  17. except Exception as e:
  18. results.setdefault(module, []).append((name, False, str(e)[:120]))
  19. print(f" [ERROR] {module}/{name}: {str(e)[:100]}")
  20. print("=== Login ===")
  21. login_r = client.post("/api/auth/login", json={"username": "admin", "password": "admin123"})
  22. TOKEN = login_r.json()["data"]["access_token"]
  23. H = {"Authorization": f"Bearer {TOKEN}"}
  24. print(f" Token OK")
  25. tc("健康检查", "获取服务健康状态", lambda: (client.get("/api/health").status_code == 200, "status=ok"))
  26. tc("认证模块", "登录-正确密码", lambda: (login_r.status_code == 200, "token acquired"))
  27. tc("认证模块", "登录-错误密码", lambda: (client.post("/api/auth/login", json={"username":"admin","password":"wrong"}).status_code == 401, "401"))
  28. tc("认证模块", "获取当前用户", lambda: (client.get("/api/auth/me", headers=H).status_code == 200, j(client.get("/api/auth/me", headers=H)).get("data",{}).get("username","")))
  29. tc("用户管理", "用户列表", lambda: (client.get("/api/users/?page=1&pageSize=10", headers=H).status_code == 200, f"total={j(client.get('/api/users/?page=1&pageSize=10',headers=H))['data']['total']}"))
  30. tc("用户管理", "创建用户", lambda: (client.post("/api/users/", json={"username":"test_tc2","password":"test123","realName":"TC用户","status":1}, headers=H).status_code == 200, "created"))
  31. tc("用户管理", "用户分页", lambda: (j(client.get("/api/users/?page=1&pageSize=2",headers=H))["data"]["pageSize"]==2, "pageSize=2"))
  32. tc("角色管理", "角色列表", lambda: (client.get("/api/roles/?page=1&pageSize=10", headers=H).status_code == 200, f"total={j(client.get('/api/roles/?page=1&pageSize=10',headers=H))['data']['total']}"))
  33. tc("角色管理", "角色详情", lambda: (client.get("/api/roles/1", headers=H).status_code == 200, j(client.get("/api/roles/1",headers=H))["data"]["roleName"]))
  34. tc("角色管理", "角色详情-不存在", lambda: (client.get("/api/roles/99999", headers=H).status_code == 404, "404"))
  35. tc("菜单管理", "菜单树", lambda: (client.get("/api/menus/tree", headers=H).status_code == 200, f"roots={len(j(client.get('/api/menus/tree',headers=H))['data'])}"))
  36. tc("菜单管理", "按角色查菜单", lambda: (client.get("/api/menus/role/1", headers=H).status_code == 200, "ok"))
  37. tc("费用项目", "费用项目树", lambda: (client.get("/api/expense-items/tree", headers=H).status_code == 200, f"roots={len(j(client.get('/api/expense-items/tree',headers=H))['data'])}"))
  38. tc("费用项目", "费用项目详情", lambda: (client.get("/api/expense-items/1", headers=H).status_code == 200, j(client.get("/api/expense-items/1",headers=H))["data"]["itemName"]))
  39. tc("工作流配置", "工作流列表", lambda: (client.get("/api/workflows/?page=1&pageSize=10", headers=H).status_code == 200, f"total={j(client.get('/api/workflows/?page=1&pageSize=10',headers=H))['data']['total']}"))
  40. tc("工作流配置", "按biz_type查询-备用金", lambda: (client.get("/api/workflows/biz/advance", headers=H).status_code == 200, j(client.get("/api/workflows/biz/advance",headers=H))["data"]["wfName"]))
  41. tc("工作流配置", "按biz_type查询-报销", lambda: (client.get("/api/workflows/biz/reimburse", headers=H).status_code == 200, j(client.get("/api/workflows/biz/reimburse",headers=H))["data"]["wfName"]))
  42. tc("工作流配置", "工作流详情", lambda: (client.get("/api/workflows/1", headers=H).status_code == 200, f"nodes={len(j(client.get('/api/workflows/1',headers=H))['data']['nodes'])}"))
  43. tc("备用金管理", "备用金列表", lambda: (client.get("/api/advances/?page=1&pageSize=10", headers=H).status_code == 200, "ok"))
  44. tc("备用金管理", "我的备用金", lambda: (client.get("/api/advances/my?page=1&pageSize=10", headers=H).status_code == 200, "ok"))
  45. r_adv = client.post("/api/advances/", json={"reason":"测试备用金","amount":5000}, headers=H)
  46. adv_ok = r_adv.status_code == 200
  47. adv_id = r_adv.json()["data"]["id"] if adv_ok else 0
  48. tc("备用金管理", "创建备用金", lambda: (adv_ok, f"id={adv_id}"))
  49. if adv_id:
  50. tc("备用金管理", "备用金详情", lambda: (client.get(f"/api/advances/{adv_id}", headers=H).status_code == 200, f"id={adv_id}"))
  51. r_sub = client.post(f"/api/advances/{adv_id}/submit", headers=H)
  52. tc("备用金管理", "提交备用金", lambda: (r_sub.status_code == 200, f"status={r_sub.json()['data']['status']}"))
  53. r_appr = client.get(f"/api/advances/{adv_id}/approvals", headers=H)
  54. tc("备用金管理", "获取审批记录", lambda: (r_appr.status_code == 200, f"count={len(r_appr.json()['data'])}"))
  55. tc("报销管理", "报销列表", lambda: (client.get("/api/reimburses/?page=1&pageSize=10", headers=H).status_code == 200, "ok"))
  56. tc("报销管理", "我的报销", lambda: (client.get("/api/reimburses/my?page=1&pageSize=10", headers=H).status_code == 200, "ok"))
  57. r_reim = client.post("/api/reimburses/", json={"title":"测试报销单","expenseItemId":9,"amount":3000}, headers=H)
  58. reim_ok = r_reim.status_code == 200
  59. reim_id = r_reim.json()["data"]["id"] if reim_ok else 0
  60. tc("报销管理", "创建报销", lambda: (reim_ok, f"id={reim_id}"))
  61. if reim_id:
  62. tc("报销管理", "报销详情", lambda: (client.get(f"/api/reimburses/{reim_id}", headers=H).status_code == 200, f"id={reim_id}"))
  63. r_rsub = client.post(f"/api/reimburses/{reim_id}/submit", headers=H)
  64. tc("报销管理", "提交报销", lambda: (r_rsub.status_code == 200, f"status={r_rsub.json()['data']['status']}"))
  65. tc("支付处理", "待支付列表-需FINANCE角色", lambda: (client.get("/api/payments/pending?page=1&pageSize=10", headers=H).status_code in (200, 403), f"status={client.get('/api/payments/pending?page=1&pageSize=10',headers=H).status_code}"))
  66. tc("支付处理", "备用金发放列表-需FINANCE角色", lambda: (client.get("/api/payments/advance-issues?page=1&pageSize=10", headers=H).status_code in (200, 403), f"status={client.get('/api/payments/advance-issues?page=1&pageSize=10',headers=H).status_code}"))
  67. tc("端到端流程", "备用金完整流程-创建并提交", lambda: (adv_ok, f"created={adv_ok}"))
  68. tc("端到端流程", "报销完整流程-创建并提交", lambda: (reim_ok, f"created={reim_ok}"))
  69. tc("认证模块", "未认证访问", lambda: (client.get("/api/users/?page=1&pageSize=10").status_code == 401, "401"))
  70. print("\n=== SUMMARY ===")
  71. total_pass = total_fail = 0
  72. for mod, cases in results.items():
  73. p = sum(1 for _, ok, _ in cases if ok)
  74. f = len(cases) - p
  75. total_pass += p
  76. total_fail += f
  77. print(f" {mod}: {p}/{len(cases)} passed")
  78. rate = total_pass / (total_pass + total_fail) * 100 if (total_pass + total_fail) > 0 else 0
  79. print(f"\n TOTAL: {total_pass}/{total_pass + total_fail} passed ({rate:.1f}%)")
  80. END = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
  81. report = f"# 接口测试执行报告\n\n> 执行时间: {START} ~ {END}\n\n"
  82. report += f"## 汇总\n\n总用例: {total_pass + total_fail}, 通过: {total_pass}, 失败: {total_fail}, 通过率: {rate:.1f}%\n\n"
  83. report += "| 模块 | 通过/总数 | 通过率 |\n|------|----------|-------|\n"
  84. for mod, cases in results.items():
  85. p = sum(1 for _, ok, _ in cases if ok)
  86. r = p / len(cases) * 100 if cases else 0
  87. report += f"| {mod} | {p}/{len(cases)} | {r:.0f}% |\n"
  88. report += "\n## 详细结果\n\n"
  89. for mod, cases in results.items():
  90. report += f"\n### {mod}\n\n| 用例 | 状态 | 结果 |\n|------|------|------|\n"
  91. for name, ok, detail in cases:
  92. s = "✅" if ok else "❌"
  93. report += f"| {name} | {s} | {detail[:100]} |\n"
  94. with open("/Users/wuzilong/work/00code/shanxia/bx/报销业务/接口测试执行报告.md", "w") as f:
  95. f.write(report)
  96. print("\n报告已写入: 报销业务/接口测试执行报告.md")