test_market_writes.py 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. # -*- coding: utf-8 -*-
  2. """营销与会员写接口端到端测试(优惠券 / 充值套餐 / 会员等级 / 会员充值 / 改等级 / 删除会员 / 领取记录)。
  3. 自带造数与清理:测试记录以 TESTJ 命名,跑完物理删除;用户余额、积分、等级恢复原值。
  4. 用法:python test_market_writes.py
  5. """
  6. import json
  7. import re
  8. from pathlib import Path
  9. import pymysql
  10. import requests
  11. from _root import ROOT
  12. JAVA = "http://127.0.0.1:18080"
  13. TOKEN = "testsession123"
  14. STORE_ID = 10001
  15. ROOT = Path(str(ROOT))
  16. YML = ROOT / "yoshop-java" / "src" / "main" / "resources" / "application.yml"
  17. SESSION = requests.Session()
  18. SESSION.trust_env = False
  19. PASS = FAIL = 0
  20. FAILS = []
  21. def db():
  22. pwd = re.findall(r"password: *(.+)", YML.read_text(encoding="utf-8"))[0].strip().strip('"')
  23. return pymysql.connect(host="192.168.1.177", port=3306, user="root", password=pwd,
  24. database="shop", autocommit=True)
  25. CONN = db()
  26. def query(sql, args=None):
  27. cur = CONN.cursor()
  28. cur.execute(sql, args or ())
  29. cols = [d[0] for d in cur.description]
  30. return [dict(zip(cols, row)) for row in cur.fetchall()]
  31. def execute(sql, args=None):
  32. cur = CONN.cursor()
  33. cur.execute(sql, args or ())
  34. return cur.rowcount
  35. def get(path, params=None):
  36. r = SESSION.get(JAVA + path, headers={"Access-Token": TOKEN}, params=params, timeout=60)
  37. return r.json()
  38. def post(path, body):
  39. r = SESSION.post(JAVA + path, headers={"Access-Token": TOKEN}, json=body, timeout=60)
  40. return r.json()
  41. def check(name, ok, detail=""):
  42. global PASS, FAIL
  43. if ok:
  44. PASS += 1
  45. print(f" [ok] {name}")
  46. else:
  47. FAIL += 1
  48. FAILS.append(name)
  49. print(f" [FAIL] {name} {detail}")
  50. def eq(name, actual, expect):
  51. check(name, actual == expect, f"期望 {expect!r} 实际 {actual!r}")
  52. def main():
  53. # ---------------------------------------------------------------- 基线
  54. users = query("select user_id, balance, points, grade_id from yoshop_user where user_id=10001")
  55. if not users:
  56. print("缺少测试会员 10001,终止")
  57. return
  58. base = users[0]
  59. base_balance = base["balance"]
  60. max_balance_log = (query("select coalesce(max(log_id),0) m from yoshop_user_balance_log")[0]["m"])
  61. max_points_log = (query("select coalesce(max(log_id),0) m from yoshop_user_points_log")[0]["m"])
  62. max_grade_log = (query("select coalesce(max(log_id),0) m from yoshop_user_grade_log")[0]["m"])
  63. print(f"基线:余额 {base_balance} 积分 {base['points']} 等级 {base['grade_id']}")
  64. coupon_ids = []
  65. plan_ids = []
  66. grade_ids = []
  67. test_user = 10999
  68. try:
  69. # ------------------------------------------------------------ 优惠券
  70. print("\n[优惠券] 新增(满减 · 领取后生效 · 全部商品)")
  71. res = post("/store/market.coupon/add", {"form": {
  72. "name": "TESTJ券A", "coupon_type": 10, "reduce_price": 10, "min_price": 100,
  73. "expire_type": 10, "expire_day": 7, "apply_range": 10, "total_num": -1,
  74. "status": 1, "describe": "自动化用例", "sort": 100}})
  75. eq("add 返回成功", res.get("status"), 200)
  76. eq("add 文案", res.get("message"), "添加成功")
  77. rows = query("select * from yoshop_coupon where name='TESTJ券A'")
  78. check("库内已写入", len(rows) == 1)
  79. a = rows[0]
  80. coupon_ids.append(a["coupon_id"])
  81. eq("start_time 清零", a["start_time"], 0)
  82. eq("end_time 清零", a["end_time"], 0)
  83. eq("expire_day 保留", a["expire_day"], 7)
  84. eq("apply_range_config 空数组", a["apply_range_config"], "[]")
  85. eq("store_id", a["store_id"], STORE_ID)
  86. eq("reduce_price", str(a["reduce_price"]), "10.00")
  87. check("create_time 已写", a["create_time"] > 0)
  88. detail = get("/store/market.coupon/detail", {"couponId": a["coupon_id"]})["data"]["detail"]
  89. eq("detail 折扣为 0", detail["discount"], 0)
  90. eq("detail 开始时间为 null", detail["start_time"], None)
  91. eq("detail 适用范围空数组", detail["apply_range_config"], [])
  92. eq("detail state 正常", detail["state"], {"text": "正常", "value": 1})
  93. print("\n[优惠券] 新增(折扣 · 固定时间 · 指定商品)")
  94. res = post("/store/market.coupon/add", {"form": {
  95. "name": "TESTJ券B", "coupon_type": 20, "discount": 9.9, "min_price": 50,
  96. "expire_type": 20, "betweenTime": ["2026-10-01", "2026-10-31"],
  97. "apply_range": 20, "apply_range_config": {"goodsIds": ["10004"]},
  98. "total_num": 5, "status": 1, "describe": "", "sort": 90}})
  99. eq("add 返回成功", res.get("status"), 200)
  100. rows = query("select * from yoshop_coupon where name='TESTJ券B'")
  101. check("库内已写入", len(rows) == 1)
  102. b = rows[0]
  103. coupon_ids.append(b["coupon_id"])
  104. eq("discount 乘 10 入库", b["discount"], 99)
  105. eq("expire_day 置 1", b["expire_day"], 1)
  106. eq("apply_range_config 存对象", json.loads(b["apply_range_config"]), {"goodsIds": ["10004"]})
  107. check("end_time 补足当天 23:59:59", (b["end_time"] - b["start_time"]) % 86400 == 86399)
  108. detail = get("/store/market.coupon/detail", {"couponId": b["coupon_id"]})["data"]["detail"]
  109. eq("detail 折扣率 9.9", detail["discount"], 9.9)
  110. eq("detail 开始时间 Y/m/d", detail["start_time"], "2026/10/01")
  111. eq("detail 结束时间 Y/m/d", detail["end_time"], "2026/10/31")
  112. print("\n[优惠券] 固定时间缺少区间")
  113. res = post("/store/market.coupon/add", {"form": {
  114. "name": "TESTJ券C", "coupon_type": 10, "reduce_price": 5, "min_price": 10,
  115. "expire_type": 20, "apply_range": 10, "total_num": -1, "status": 1, "sort": 100}})
  116. eq("返回失败", res.get("status"), 500)
  117. eq("提示有效期", res.get("message"), "请选择有效期范围")
  118. eq("未写入脏数据", len(query("select coupon_id from yoshop_coupon where name='TESTJ券C'")), 0)
  119. print("\n[优惠券] 编辑")
  120. res = post("/store/market.coupon/edit", {"couponId": a["coupon_id"], "form": {
  121. "name": "TESTJ券A改", "coupon_type": 10, "reduce_price": 20, "min_price": 200,
  122. "expire_type": 10, "expire_day": 3, "apply_range": 20,
  123. "apply_range_config": {"goodsIds": ["10004", "10005"]},
  124. "total_num": 100, "status": 1, "describe": "改过", "sort": 88}})
  125. eq("edit 返回成功", res.get("status"), 200)
  126. eq("edit 文案", res.get("message"), "更新成功")
  127. a2 = query("select * from yoshop_coupon where coupon_id=%s", (a["coupon_id"],))[0]
  128. eq("名称已改", a2["name"], "TESTJ券A改")
  129. eq("减免金额已改", str(a2["reduce_price"]), "20.00")
  130. eq("适用范围已改", a2["apply_range"], 20)
  131. eq("适用范围配置已改", json.loads(a2["apply_range_config"]), {"goodsIds": ["10004", "10005"]})
  132. eq("排序已改", a2["sort"], 88)
  133. print("\n[优惠券] 编辑为全部商品 → 配置清空")
  134. post("/store/market.coupon/edit", {"couponId": a["coupon_id"], "form": {
  135. "name": "TESTJ券A改", "coupon_type": 10, "reduce_price": 20, "min_price": 200,
  136. "expire_type": 10, "expire_day": 3, "apply_range": 10,
  137. "apply_range_config": {"goodsIds": ["10004"]},
  138. "total_num": 100, "status": 1, "describe": "改过", "sort": 88}})
  139. a3 = query("select apply_range_config from yoshop_coupon where coupon_id=%s", (a["coupon_id"],))[0]
  140. eq("配置被清空", a3["apply_range_config"], "[]")
  141. print("\n[优惠券] 编辑不存在的券")
  142. res = post("/store/market.coupon/edit", {"couponId": 999999, "form": {"name": "x"}})
  143. eq("返回失败", res.get("status"), 500)
  144. print("\n[优惠券] 删除")
  145. res = post("/store/market.coupon/delete", {"couponId": a["coupon_id"]})
  146. eq("delete 返回成功", res.get("status"), 200)
  147. eq("软删除标记", query("select is_delete from yoshop_coupon where coupon_id=%s",
  148. (a["coupon_id"],))[0]["is_delete"], 1)
  149. listed = get("/store/market.coupon/list", {"page": 1, "search": "TESTJ券A"})["data"]["list"]["data"]
  150. eq("列表不再出现", len(listed), 0)
  151. print("\n[优惠券] 删除不存在的券")
  152. eq("返回失败", post("/store/market.coupon/delete", {"couponId": 999999}).get("status"), 500)
  153. # ---------------------------------------------------------- 充值套餐
  154. print("\n[充值套餐] 新增 / 编辑 / 删除")
  155. res = post("/store/market.recharge.plan/add", {"form": {
  156. "plan_name": "TESTJ套餐A", "money": 100, "gift_money": 10, "sort": 100}})
  157. eq("add 返回成功", res.get("status"), 200)
  158. rows = query("select * from yoshop_recharge_plan where plan_name='TESTJ套餐A'")
  159. check("库内已写入", len(rows) == 1)
  160. plan = rows[0]
  161. plan_ids.append(plan["plan_id"])
  162. eq("充值金额", str(plan["money"]), "100.00")
  163. eq("赠送金额", str(plan["gift_money"]), "10.00")
  164. eq("store_id", plan["store_id"], STORE_ID)
  165. res = post("/store/market.recharge.plan/edit", {"planId": plan["plan_id"], "form": {
  166. "plan_name": "TESTJ套餐A改", "money": 200, "gift_money": 30, "sort": 66}})
  167. eq("edit 返回成功", res.get("status"), 200)
  168. plan2 = query("select * from yoshop_recharge_plan where plan_id=%s", (plan["plan_id"],))[0]
  169. eq("名称已改", plan2["plan_name"], "TESTJ套餐A改")
  170. eq("金额已改", str(plan2["money"]), "200.00")
  171. eq("排序已改", plan2["sort"], 66)
  172. res = post("/store/market.recharge.plan/delete", {"planId": plan["plan_id"]})
  173. eq("delete 返回成功", res.get("status"), 200)
  174. eq("软删除标记", query("select is_delete from yoshop_recharge_plan where plan_id=%s",
  175. (plan["plan_id"],))[0]["is_delete"], 1)
  176. eq("删除不存在的套餐", post("/store/market.recharge.plan/delete", {"planId": 999999}).get("status"), 500)
  177. # ---------------------------------------------------------- 会员等级
  178. print("\n[会员等级] 新增 / 权重重复 / 编辑 / 删除")
  179. res = post("/store/user.grade/add", {"form": {
  180. "name": "TESTJ等级", "weight": 9,
  181. "upgrade": {"expend_money": 50000}, "equity": {"discount": "9.5"}, "status": 1}})
  182. eq("add 返回成功", res.get("status"), 200)
  183. rows = query("select * from yoshop_user_grade where name='TESTJ等级'")
  184. check("库内已写入", len(rows) == 1)
  185. grade = rows[0]
  186. grade_ids.append(grade["grade_id"])
  187. eq("权重", grade["weight"], 9)
  188. eq("升级条件 JSON", json.loads(grade["upgrade"]), {"expend_money": 50000})
  189. eq("等级权益 JSON", json.loads(grade["equity"]), {"discount": "9.5"})
  190. eq("store_id", grade["store_id"], STORE_ID)
  191. res = post("/store/user.grade/add", {"form": {"name": "TESTJ等级2", "weight": 9,
  192. "upgrade": {}, "equity": {}, "status": 1}})
  193. eq("权重重复被拦", res.get("status"), 500)
  194. eq("权重重复提示", res.get("message"), "等级权重已存在")
  195. res = post("/store/user.grade/edit", {"gradeId": grade["grade_id"], "form": {
  196. "name": "TESTJ等级改", "weight": 8,
  197. "upgrade": {"expend_money": 60000}, "equity": {"discount": "9.4"}, "status": 1}})
  198. eq("edit 返回成功", res.get("status"), 200)
  199. g2 = query("select * from yoshop_user_grade where grade_id=%s", (grade["grade_id"],))[0]
  200. eq("名称已改", g2["name"], "TESTJ等级改")
  201. eq("权重已改", g2["weight"], 8)
  202. res = post("/store/user.grade/edit", {"gradeId": grade["grade_id"], "form": {
  203. "name": "TESTJ等级改", "weight": 1,
  204. "upgrade": {"expend_money": 60000}, "equity": {"discount": "9.4"}, "status": 1}})
  205. eq("编辑撞已有权重被拦", res.get("status"), 500)
  206. # 有会员占用时不允许删除:先造一个挂在该等级下的会员
  207. execute("delete from yoshop_user where user_id=%s", (test_user,))
  208. execute("""insert into yoshop_user
  209. (user_id,mobile,nick_name,avatar_id,gender,country,province,city,address_id,
  210. balance,points,pay_money,expend_money,grade_id,platform,last_login_time,
  211. is_delete,store_id,create_time,update_time)
  212. values (%s,'19900000000','TESTJ会员',0,0,'','','',0,0,0,0,0,%s,'H5',0,0,%s,0,0)""",
  213. (test_user, grade["grade_id"], STORE_ID))
  214. res = post("/store/user.grade/delete", {"gradeId": grade["grade_id"]})
  215. eq("等级下有会员时禁止删除", res.get("status"), 500)
  216. eq("提示文案", res.get("message"), "该会员等级下存在用户,不允许删除")
  217. eq("未被删除", query("select is_delete from yoshop_user_grade where grade_id=%s",
  218. (grade["grade_id"],))[0]["is_delete"], 0)
  219. execute("update yoshop_user set grade_id=0 where user_id=%s", (test_user,))
  220. res = post("/store/user.grade/delete", {"gradeId": grade["grade_id"]})
  221. eq("无会员时可删除", res.get("status"), 200)
  222. eq("软删除标记", query("select is_delete from yoshop_user_grade where grade_id=%s",
  223. (grade["grade_id"],))[0]["is_delete"], 1)
  224. # ------------------------------------------------------------ 会员充值
  225. print("\n[会员充值] 余额 · 增加")
  226. before = query("select balance from yoshop_user where user_id=10001")[0]["balance"]
  227. res = post("/store/user/recharge", {"userId": 10001, "target": "balance",
  228. "form": {"balance": {"mode": "inc", "money": 50, "remark": "TESTJ充值"}}})
  229. eq("返回成功", res.get("status"), 200)
  230. eq("文案", res.get("message"), "操作成功")
  231. after = query("select balance from yoshop_user where user_id=10001")[0]["balance"]
  232. eq("余额增加 50", str(after - before), "50.00")
  233. logs = query("""select * from yoshop_user_balance_log where user_id=10001
  234. and log_id>%s order by log_id desc limit 1""", (max_balance_log,))
  235. eq("写入余额流水", len(logs), 1)
  236. eq("流水场景=管理员操作", logs[0]["scene"], 30)
  237. eq("流水金额", str(logs[0]["money"]), "50.00")
  238. check("流水描述含管理员", logs[0]["describe"].startswith("后台管理员 [")
  239. and logs[0]["describe"].endswith("] 操作"), logs[0]["describe"])
  240. eq("流水备注", logs[0]["remark"], "TESTJ充值")
  241. print("\n[会员充值] 余额 · 减少")
  242. before = query("select balance from yoshop_user where user_id=10001")[0]["balance"]
  243. res = post("/store/user/recharge", {"userId": 10001, "target": "balance",
  244. "form": {"balance": {"mode": "dec", "money": 20, "remark": ""}}})
  245. eq("返回成功", res.get("status"), 200)
  246. after = query("select balance from yoshop_user where user_id=10001")[0]["balance"]
  247. eq("余额减少 20", str(before - after), "20.00")
  248. logs = query("""select money from yoshop_user_balance_log where user_id=10001
  249. and log_id>%s order by log_id desc limit 1""", (max_balance_log,))
  250. eq("流水记录负数", str(logs[0]["money"]), "-20.00")
  251. print("\n[会员充值] 余额 · 最终金额")
  252. res = post("/store/user/recharge", {"userId": 10001, "target": "balance",
  253. "form": {"balance": {"mode": "final", "money": 888, "remark": ""}}})
  254. eq("返回成功", res.get("status"), 200)
  255. eq("余额变为 888", str(query("select balance from yoshop_user where user_id=10001")[0]["balance"]), "888.00")
  256. print("\n[会员充值] 金额非法")
  257. res = post("/store/user/recharge", {"userId": 10001, "target": "balance",
  258. "form": {"balance": {"mode": "inc", "money": "", "remark": ""}}})
  259. eq("空金额被拦", res.get("status"), 500)
  260. eq("提示文案", res.get("message"), "请输入正确的金额")
  261. res = post("/store/user/recharge", {"userId": 10001, "target": "unknown", "form": {}})
  262. eq("非法 target 被拦", res.get("status"), 500)
  263. print("\n[会员充值] 积分 · 增加 / 最终值")
  264. res = post("/store/user/recharge", {"userId": 10001, "target": "points",
  265. "form": {"points": {"mode": "inc", "value": 30, "remark": "TESTJ积分"}}})
  266. eq("返回成功", res.get("status"), 200)
  267. eq("积分 +30", query("select points from yoshop_user where user_id=10001")[0]["points"], 30)
  268. logs = query("""select * from yoshop_user_points_log where user_id=10001
  269. and log_id>%s order by log_id desc limit 1""", (max_points_log,))
  270. eq("写入积分流水", len(logs), 1)
  271. eq("流水积分值", logs[0]["value"], 30)
  272. eq("流水描述", logs[0]["describe"].startswith("后台管理员 ["), True)
  273. res = post("/store/user/recharge", {"userId": 10001, "target": "points",
  274. "form": {"points": {"mode": "final", "value": 0, "remark": ""}}})
  275. eq("返回成功", res.get("status"), 200)
  276. eq("积分归零", query("select points from yoshop_user where user_id=10001")[0]["points"], 0)
  277. res = post("/store/user/recharge", {"userId": 10001, "target": "points",
  278. "form": {"points": {"mode": "inc", "value": "", "remark": ""}}})
  279. eq("空积分被拦", res.get("status"), 500)
  280. eq("提示文案", res.get("message"), "请输入正确的积分数量")
  281. # ------------------------------------------------------------ 改等级
  282. print("\n[会员等级变更]")
  283. res = post("/store/user/grade", {"userId": 10001, "form": {"grade_id": 10002, "remark": "TESTJ调级"}})
  284. eq("返回成功", res.get("status"), 200)
  285. eq("等级已改", query("select grade_id from yoshop_user where user_id=10001")[0]["grade_id"], 10002)
  286. glog = query("""select * from yoshop_user_grade_log where user_id=10001
  287. and log_id>%s order by log_id desc limit 1""", (max_grade_log,))
  288. eq("写入等级变更记录", len(glog), 1)
  289. eq("变更前等级", glog[0]["old_grade_id"], 0)
  290. eq("变更后等级", glog[0]["new_grade_id"], 10002)
  291. eq("变更类型=管理员", glog[0]["change_type"], 10)
  292. eq("变更备注", glog[0]["remark"], "TESTJ调级")
  293. post("/store/user/grade", {"userId": 10001, "form": {"grade_id": 0, "remark": ""}})
  294. eq("已恢复原等级", query("select grade_id from yoshop_user where user_id=10001")[0]["grade_id"], 0)
  295. # ------------------------------------------------------------ 删除会员
  296. print("\n[删除会员]")
  297. execute("""insert into yoshop_user_oauth (user_id,oauth_type,oauth_id,unionid,store_id,is_delete,
  298. create_time,update_time) values (%s,'TESTJ','oauth-test','',%s,0,0,0)""",
  299. (test_user, STORE_ID))
  300. res = post("/store/user/delete", {"userId": test_user})
  301. eq("返回成功", res.get("status"), 200)
  302. eq("文案", res.get("message"), "删除成功")
  303. eq("会员标记删除", query("select is_delete from yoshop_user where user_id=%s",
  304. (test_user,))[0]["is_delete"], 1)
  305. eq("三方绑定同步标记", query("select is_delete from yoshop_user_oauth where user_id=%s",
  306. (test_user,))[0]["is_delete"], 1)
  307. eq("重复删除被拦", post("/store/user/delete", {"userId": test_user}).get("status"), 500)
  308. # -------------------------------------------------------- 领取记录列表
  309. print("\n[领取记录] 造一条领取记录后读取")
  310. execute("""insert into yoshop_user_coupon
  311. (coupon_id,name,coupon_type,reduce_price,discount,min_price,expire_type,expire_day,
  312. start_time,end_time,apply_range,apply_range_config,`describe`,is_expire,is_use,
  313. user_id,store_id,create_time,update_time)
  314. values (10002,'TESTJ领取券',20,0.00,99,50.00,20,1,1790784000,1793462399,10,'[]','',
  315. 0,0,10001,%s,1792000000,1792000000)""", (STORE_ID,))
  316. uc = query("select user_coupon_id from yoshop_user_coupon where name='TESTJ领取券'")[0]["user_coupon_id"]
  317. res = get("/store/market.coupon/receive", {"page": 1, "couponName": "TESTJ领取券"})
  318. data = res["data"]["list"]
  319. eq("总数", data["total"], 1)
  320. row = data["data"][0]
  321. eq("折扣率 9.9", row["discount"], 9.9)
  322. eq("开始时间 Y/m/d", row["start_time"], "2026/10/01")
  323. eq("state 正常", row["state"], {"text": "正常", "value": 1})
  324. check("带会员信息", isinstance(row.get("user"), dict) and row["user"]["user_id"] == 10001)
  325. check("会员含头像键", "avatar" in row["user"] and "avatar_url" in row["user"])
  326. check("会员无 grade 键", "grade" not in row["user"])
  327. res = get("/store/market.coupon/receive", {"page": 1, "nickName": "185****1340"})
  328. check("按昵称可检索", res["data"]["list"]["total"] >= 1)
  329. res = get("/store/market.coupon/receive", {"page": 1, "nickName": "不存在的昵称XYZ"})
  330. eq("昵称无匹配为空", res["data"]["list"]["total"], 0)
  331. finally:
  332. # ------------------------------------------------------------ 清理
  333. # 只删本次新建的记录(本地存在与线上对照的种子数据,不能按名称前缀删)
  334. print("\n[清理]")
  335. if coupon_ids:
  336. execute("delete from yoshop_coupon where coupon_id in (%s)"
  337. % ",".join(str(int(i)) for i in coupon_ids))
  338. if plan_ids:
  339. execute("delete from yoshop_recharge_plan where plan_id in (%s)"
  340. % ",".join(str(int(i)) for i in plan_ids))
  341. if grade_ids:
  342. execute("delete from yoshop_user_grade where grade_id in (%s)"
  343. % ",".join(str(int(i)) for i in grade_ids))
  344. execute("delete from yoshop_user_coupon where name='TESTJ领取券'")
  345. execute("delete from yoshop_user_balance_log where log_id>%s", (max_balance_log,))
  346. execute("delete from yoshop_user_points_log where log_id>%s", (max_points_log,))
  347. execute("delete from yoshop_user_grade_log where log_id>%s", (max_grade_log,))
  348. execute("delete from yoshop_user_oauth where user_id=%s", (test_user,))
  349. execute("delete from yoshop_user where user_id=%s", (test_user,))
  350. execute("update yoshop_user set balance=%s, points=%s, grade_id=%s where user_id=10001",
  351. (base_balance, base["points"], base["grade_id"]))
  352. restored = query("select balance, points, grade_id from yoshop_user where user_id=10001")[0]
  353. eq("余额已恢复", str(restored["balance"]), str(base_balance))
  354. eq("积分已恢复", restored["points"], base["points"])
  355. eq("等级已恢复", restored["grade_id"], base["grade_id"])
  356. print(f"\n通过 {PASS} / 失败 {FAIL}")
  357. if FAILS:
  358. print("失败项:" + "、".join(FAILS))
  359. if __name__ == "__main__":
  360. main()