test_batch3_writes.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. # -*- coding: utf-8 -*-
  2. """第 3 批写接口端到端自测:新增 -> 编辑 -> 删除,逐项校验文案与状态。
  3. 覆盖 /store/store.role、/store/store.user、/store/store.address、/store/page。
  4. 所有中间数据都会在脚本末尾清理,避免污染库。
  5. """
  6. import json
  7. import requests
  8. BASE = "http://127.0.0.1:18080"
  9. TOKEN = "testsession123"
  10. S = requests.Session()
  11. S.trust_env = False
  12. H = {"Access-Token": TOKEN, "Content-Type": "application/json"}
  13. ok_count = 0
  14. fail_count = 0
  15. def call(path, payload=None, method="post", params=None):
  16. url = BASE + path
  17. if method == "get":
  18. r = S.get(url, headers=H, params=params or {}, timeout=60)
  19. else:
  20. r = S.post(url, headers=H, data=json.dumps(payload or {}), timeout=60)
  21. return r.json()
  22. def check(name, cond, detail=""):
  23. global ok_count, fail_count
  24. if cond:
  25. ok_count += 1
  26. print(f" [OK] {name}")
  27. else:
  28. fail_count += 1
  29. print(f" [FAIL] {name} {detail}")
  30. def expect_ok(name, resp, message=None):
  31. good = resp.get("status") == 200 and (message is None or resp.get("message") == message)
  32. check(name, good, f"-> {json.dumps(resp, ensure_ascii=False)[:160]}")
  33. def expect_fail(name, resp, message):
  34. good = resp.get("status") == 500 and resp.get("message") == message
  35. check(name, good, f"-> {json.dumps(resp, ensure_ascii=False)[:160]}")
  36. def main():
  37. print("== 角色 CRUD")
  38. roles_before = call("/store/store.role/list", method="get")["data"]["list"]
  39. menus = []
  40. def collect(nodes):
  41. for n in nodes:
  42. menus.append(n["menu_id"])
  43. collect(n.get("children") or [])
  44. collect(call("/store/store.menu/list", method="get")["data"]["list"])
  45. pick = menus[:5]
  46. expect_fail("新增角色-未选菜单", call("/store/store.role/add", {"form": {"role_name": "自测角色", "parent_id": 0, "sort": 99, "menus": []}}),
  47. "菜单权限不能为空,请重新选择")
  48. expect_ok("新增角色", call("/store/store.role/add",
  49. {"form": {"role_name": "自测角色", "parent_id": 0, "sort": 99, "menus": pick}}), "添加成功")
  50. roles_after = call("/store/store.role/list", method="get")["data"]["list"]
  51. created = [r for r in roles_after if r.get("role_name") == "自测角色"]
  52. check("角色已落库", len(created) == 1, f"got {len(created)}")
  53. role_id = created[0]["role_id"] if created else None
  54. check("角色菜单关联正确", created and sorted(created[0].get("menuIds") or []) == sorted(pick),
  55. f"{created[0].get('menuIds') if created else None}")
  56. expect_ok("编辑角色", call("/store/store.role/edit",
  57. {"roleId": role_id, "form": {"role_name": "自测角色改", "parent_id": 0, "sort": 98, "menus": pick[:3]}}),
  58. "更新成功")
  59. role_now = [r for r in call("/store/store.role/list", method="get")["data"]["list"] if r["role_id"] == role_id]
  60. check("角色编辑生效", role_now and role_now[0]["role_name"] == "自测角色改"
  61. and sorted(role_now[0].get("menuIds") or []) == sorted(pick[:3]),
  62. json.dumps(role_now, ensure_ascii=False)[:200] if role_now else "missing")
  63. print("== 管理员 CRUD")
  64. expect_fail("新增管理员-用户名重复", call("/store/store.user/add", {"form": {
  65. "user_name": "admin", "real_name": "x", "password": "123456", "password_confirm": "123456", "roles": [role_id]}}),
  66. "用户名已存在") if any(u.get("user_name") == "admin" for u in
  67. call("/store/store.user/list", method="get", params={"page": 1})["data"]["list"]["data"]) else None
  68. expect_fail("新增管理员-两次密码不一致", call("/store/store.user/add", {"form": {
  69. "user_name": "autotest_admin", "real_name": "自测", "password": "123456", "password_confirm": "654321",
  70. "roles": [role_id]}}), "确认密码不正确")
  71. expect_fail("新增管理员-未选角色", call("/store/store.user/add", {"form": {
  72. "user_name": "autotest_admin", "real_name": "自测", "password": "123456", "password_confirm": "123456",
  73. "roles": []}}), "请选择所属角色")
  74. expect_ok("新增管理员", call("/store/store.user/add", {"form": {
  75. "user_name": "AutoTest_Admin", "real_name": "自测账号", "password": "123456", "password_confirm": "123456",
  76. "roles": [role_id], "sort": 99}}), "添加成功")
  77. users = call("/store/store.user/list", method="get", params={"page": 1})["data"]["list"]["data"]
  78. created_user = [u for u in users if u["user_name"] == "autotest_admin"]
  79. check("用户名已转小写落库", len(created_user) == 1, str([u["user_name"] for u in users]))
  80. user_id = created_user[0]["store_user_id"] if created_user else None
  81. check("角色关联正确", created_user and created_user[0].get("roleIds") == [role_id],
  82. str(created_user[0].get("roleIds")) if created_user else "missing")
  83. # 新账号能登录,说明 Java 生成的 $2y$ 哈希与 PHP password_verify 同构
  84. login = S.post(BASE + "/store/passport/login", headers={"Content-Type": "application/json"},
  85. data=json.dumps({"username": "autotest_admin", "password": "123456"}), timeout=60).json()
  86. expect_ok("新管理员可登录", login)
  87. new_token = (login.get("data") or {}).get("token")
  88. expect_ok("编辑管理员", call("/store/store.user/edit", {"userId": user_id, "form": {
  89. "user_name": "autotest_admin", "real_name": "自测账号改", "password": "", "password_confirm": "",
  90. "roles": [role_id], "sort": 97}}), "更新成功")
  91. users = call("/store/store.user/list", method="get", params={"page": 1})["data"]["list"]["data"]
  92. u2 = [u for u in users if u["store_user_id"] == user_id]
  93. check("管理员编辑生效", u2 and u2[0]["real_name"] == "自测账号改" and u2[0]["sort"] == 97,
  94. json.dumps(u2, ensure_ascii=False)[:200])
  95. if new_token:
  96. renew = S.post(BASE + "/store/store.user/renew",
  97. headers={"Access-Token": new_token, "Content-Type": "application/json"},
  98. data=json.dumps({"form": {"user_name": "autotest_admin", "real_name": "自测账号二改",
  99. "password": "", "password_confirm": ""}}), timeout=60).json()
  100. expect_ok("当前管理员改资料", renew, "更新成功")
  101. print("== 地址 CRUD")
  102. expect_ok("新增地址", call("/store/store.address/add", {"form": {
  103. "type": 10, "name": "自测联系人", "phone": "13800000000",
  104. "cascader": [110000, 110100, 110101], "detail": "自测地址1号", "sort": 99}}), "添加成功")
  105. all_addr = call("/store/store.address/all", method="get")["data"]["list"]
  106. addr = [a for a in all_addr if a["name"] == "自测联系人"]
  107. check("地址已落库且省市区拆分正确",
  108. len(addr) == 1 and addr[0]["province_id"] == 110000 and addr[0]["city_id"] == 110100 and addr[0]["region_id"] == 110101,
  109. json.dumps(addr, ensure_ascii=False)[:240])
  110. addr_id = addr[0]["address_id"] if addr else None
  111. check("地址级联/全称已组装", addr and addr[0].get("cascader") == [110000, 110100, 110101]
  112. and str(addr[0].get("full_address", "")).endswith("自测地址1号"),
  113. str(addr[0].get("full_address")) if addr else "missing")
  114. expect_ok("编辑地址", call("/store/store.address/edit", {"addressId": addr_id, "form": {
  115. "type": 20, "name": "自测联系人改", "phone": "13900000000",
  116. "cascader": [310000, 310100, 310101], "detail": "自测地址2号", "sort": 98}}), "更新成功")
  117. addr2 = [a for a in call("/store/store.address/all", method="get")["data"]["list"] if a["address_id"] == addr_id]
  118. check("地址编辑生效", addr2 and addr2[0]["name"] == "自测联系人改" and addr2[0]["type"] == 20
  119. and addr2[0]["province_id"] == 310000, json.dumps(addr2, ensure_ascii=False)[:240])
  120. filtered = call("/store/store.address/all", method="get", params={"type": 20})["data"]["list"]
  121. check("地址 type 过滤生效", all(a["type"] == 20 for a in filtered) and len(filtered) >= 1, f"{len(filtered)} 条")
  122. searched = call("/store/store.address/all", method="get", params={"search": "13900000000"})["data"]["list"]
  123. check("地址 search 过滤生效", any(a["address_id"] == addr_id for a in searched), f"{len(searched)} 条")
  124. print("== 页面 CRUD")
  125. page_id = None
  126. page_payload = {"form": {"page": {"params": {"name": "自测页面", "title": "自测标题", "shareTitle": "自测分享"},
  127. "style": {"titleTextColor": "black", "titleBackgroundColor": "#ffffff"},
  128. "name": "页面设置", "type": "page"},
  129. "items": [{"name": "富文本", "type": "richText",
  130. "params": {"content": "<p>你好 &amp; 世界</p>"},
  131. "style": {"paddingTop": 0, "paddingLeft": 0, "background": "#ffffff"}}]}}
  132. expect_ok("新增页面", call("/store/page/add", page_payload), "添加成功")
  133. pages = call("/store/page/list", method="get", params={"page": 1})["data"]["list"]["data"]
  134. created_page = [p for p in pages if p["page_name"] == "自测页面"]
  135. check("页面已落库且类型为自定义页", len(created_page) == 1 and created_page[0]["page_type"] == 20,
  136. json.dumps(created_page, ensure_ascii=False)[:200])
  137. page_id = created_page[0]["page_id"] if created_page else None
  138. detail = call("/store/page/detail", method="get", params={"pageId": page_id})["data"]["detail"]
  139. items = detail["page_data"]["items"]
  140. check("页面详情合并默认组件参数", items and items[0]["type"] == "richText"
  141. and items[0]["style"]["paddingTop"] == 0 and "name" in items[0],
  142. json.dumps(items, ensure_ascii=False)[:200])
  143. check("页面详情 page.params 合并正常", detail["page_data"]["page"]["params"]["name"] == "自测页面",
  144. json.dumps(detail["page_data"]["page"], ensure_ascii=False)[:200])
  145. expect_fail("删除首页被拒绝", call("/store/page/delete", {"pageId": 10001}), "默认首页不可以删除")
  146. page_payload["form"]["page"]["params"]["name"] = "自测页面改"
  147. expect_ok("编辑页面", call("/store/page/edit", {"pageId": page_id, "form": page_payload["form"]}), "更新成功")
  148. pages = call("/store/page/list", method="get", params={"page": 1})["data"]["list"]["data"]
  149. check("页面编辑生效", any(p["page_id"] == page_id and p["page_name"] == "自测页面改" for p in pages))
  150. print("== 清理")
  151. if page_id:
  152. expect_ok("删除页面", call("/store/page/delete", {"pageId": page_id}), "删除成功")
  153. if addr_id:
  154. expect_ok("删除地址", call("/store/store.address/delete", {"addressId": addr_id}), "删除成功")
  155. if user_id:
  156. expect_ok("删除管理员", call("/store/store.user/delete", {"userId": user_id}), "删除成功")
  157. if new_token:
  158. S.post(BASE + "/store/passport/logout", headers={"Access-Token": new_token}, timeout=60)
  159. if role_id:
  160. expect_ok("删除角色", call("/store/store.role/delete", {"roleId": role_id}), "删除成功")
  161. print("== 删除后复核")
  162. pages = call("/store/page/list", method="get", params={"page": 1})["data"]["list"]["data"]
  163. check("页面已软删不可见", all(p["page_id"] != page_id for p in pages))
  164. addr = call("/store/store.address/all", method="get")["data"]["list"]
  165. check("地址已软删不可见", all(a["address_id"] != addr_id for a in addr))
  166. users = call("/store/store.user/list", method="get", params={"page": 1})["data"]["list"]["data"]
  167. check("管理员已软删不可见", all(u["store_user_id"] != user_id for u in users))
  168. roles = call("/store/store.role/list", method="get")["data"]["list"]
  169. check("角色已删除", all(r["role_id"] != role_id for r in roles))
  170. print(f"\n汇总:通过 {ok_count},失败 {fail_count}")
  171. if __name__ == "__main__":
  172. main()