fix: 更新接口测试脚本
This commit is contained in:
+198
-96
@@ -10,7 +10,6 @@ import json
|
||||
import random
|
||||
import string
|
||||
import sys
|
||||
import time
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
@@ -32,6 +31,29 @@ def rand_str(n=8):
|
||||
return "".join(random.choices(string.ascii_lowercase + string.digits, k=n))
|
||||
|
||||
|
||||
def as_int(value, default=0):
|
||||
try:
|
||||
return int(value)
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def pick_items(body):
|
||||
if isinstance(body.get("items"), list):
|
||||
return body["items"]
|
||||
if isinstance(body.get("list"), list):
|
||||
return body["list"]
|
||||
return []
|
||||
|
||||
|
||||
def pick_user_id(body):
|
||||
if not isinstance(body, dict):
|
||||
return 0
|
||||
if isinstance(body.get("user"), dict):
|
||||
return as_int(body["user"].get("id"))
|
||||
return as_int(body.get("id"))
|
||||
|
||||
|
||||
def read_vcode_from_redis(request_id, scene, account):
|
||||
if not request_id:
|
||||
return ""
|
||||
@@ -117,7 +139,10 @@ class Session:
|
||||
|
||||
def report(name, status_code, body, expect_status=200):
|
||||
global passed, failed
|
||||
ok = status_code == expect_status
|
||||
if isinstance(expect_status, (list, tuple, set)):
|
||||
ok = status_code in expect_status
|
||||
else:
|
||||
ok = status_code == expect_status
|
||||
mark = "PASS" if ok else "FAIL"
|
||||
if not ok:
|
||||
failed += 1
|
||||
@@ -136,7 +161,7 @@ def report(name, status_code, body, expect_status=200):
|
||||
# ============================================================
|
||||
def phase0_health(s: Session):
|
||||
print("\n=== Phase 0: Health & CSRF ===")
|
||||
code, body, hdrs = s.get(f"{GATEWAY}/healthz")
|
||||
code, body, _ = s.get(f"{GATEWAY}/healthz")
|
||||
report("GET /healthz", code, body)
|
||||
xsrf = s.get_cookie("__Host-XSRF-TOKEN")
|
||||
xsrf_guard = s.get_cookie("__Host-XSRF-GUARD")
|
||||
@@ -149,8 +174,8 @@ def phase0_health(s: Session):
|
||||
# ============================================================
|
||||
# Phase 1: Registration
|
||||
# ============================================================
|
||||
def phase1_register(s: Session, username, email, password):
|
||||
print("\n=== Phase 1: Register ===")
|
||||
def phase1_register(s: Session, username, email, password, label="user"):
|
||||
print(f"\n=== Phase 1: Register ({label}) ===")
|
||||
|
||||
# Step 1: send verification code via gateway
|
||||
code, body, _ = s.post(
|
||||
@@ -158,7 +183,7 @@ def phase1_register(s: Session, username, email, password):
|
||||
json_body={"email": email, "scene": "register"},
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report("POST /email/verification-code/send (gateway)", code, body)
|
||||
report(f"POST /email/verification-code/send ({label})", code, body)
|
||||
request_id = body.get("requestId", "")
|
||||
if not request_id:
|
||||
print(" [ERROR] No requestId returned, cannot register")
|
||||
@@ -185,19 +210,19 @@ def phase1_register(s: Session, username, email, password):
|
||||
},
|
||||
headers=csrf,
|
||||
)
|
||||
report("POST /auth/register (gateway)", code, body)
|
||||
report(f"POST /auth/register ({label})", code, body)
|
||||
return body
|
||||
|
||||
|
||||
def phase1_login(s: Session, username, password):
|
||||
print("\n=== Phase 1b: Login ===")
|
||||
def phase1_login(s: Session, username, password, label="user"):
|
||||
print(f"\n=== Phase 1b: Login ({label}) ===")
|
||||
csrf = s.csrf_headers()
|
||||
code, body, _ = s.post(
|
||||
f"{GATEWAY}/api/v1/auth/login",
|
||||
json_body={"username": username, "password": password},
|
||||
headers=csrf,
|
||||
)
|
||||
report("POST /auth/login", code, body)
|
||||
report(f"POST /auth/login ({label})", code, body)
|
||||
jtoken = s.get_cookie("JToken")
|
||||
print(f" JToken: {jtoken[:30]}..." if jtoken else " JToken: None")
|
||||
return body
|
||||
@@ -244,6 +269,7 @@ def phase2_user(s: Session, user_id):
|
||||
def phase3_admin_and_verification(
|
||||
s_admin: Session,
|
||||
s_user: Session,
|
||||
s_reject: Session,
|
||||
admin_user,
|
||||
admin_pass,
|
||||
):
|
||||
@@ -259,13 +285,9 @@ def phase3_admin_and_verification(
|
||||
)
|
||||
report("POST /auth/login (admin)", code, body)
|
||||
|
||||
admin_id = 0
|
||||
code, body, _ = s_admin.get(f"{GATEWAY}/api/v1/users/me")
|
||||
report("GET /users/me (admin)", code, body)
|
||||
if isinstance(body.get("user"), dict):
|
||||
admin_id = body["user"].get("id", admin_id)
|
||||
elif body.get("id"):
|
||||
admin_id = body.get("id", admin_id)
|
||||
admin_id = pick_user_id(body)
|
||||
|
||||
# User applies for player verification
|
||||
csrf_user = s_user.csrf_headers()
|
||||
@@ -300,21 +322,45 @@ def phase3_admin_and_verification(
|
||||
)
|
||||
report("POST /users/me/verification (apply owner)", code, body)
|
||||
|
||||
# Another user applies so admin reject flow is covered too
|
||||
csrf_reject = s_reject.csrf_headers()
|
||||
code, body, _ = s_reject.post(
|
||||
f"{GATEWAY}/api/v1/users/me/verification",
|
||||
json_body={
|
||||
"role": "owner",
|
||||
"materials": {
|
||||
"idCardFront": "http://example.com/reject-front.jpg",
|
||||
"idCardBack": "http://example.com/reject-back.jpg",
|
||||
"gameScreenshots": [],
|
||||
"voiceDemo": "",
|
||||
},
|
||||
},
|
||||
headers=csrf_reject,
|
||||
)
|
||||
report("POST /users/me/verification (apply owner, reject user)", code, body)
|
||||
|
||||
# Get my verifications
|
||||
code, body, _ = s_user.get(f"{GATEWAY}/api/v1/users/me/verification")
|
||||
report("GET /users/me/verification", code, body)
|
||||
user_verification_ids = {}
|
||||
for v in pick_items(body):
|
||||
user_verification_ids[v.get("role")] = as_int(v.get("id"))
|
||||
|
||||
code, body, _ = s_reject.get(f"{GATEWAY}/api/v1/users/me/verification")
|
||||
report("GET /users/me/verification (reject user)", code, body)
|
||||
reject_verification_ids = {}
|
||||
for v in pick_items(body):
|
||||
reject_verification_ids[v.get("role")] = as_int(v.get("id"))
|
||||
|
||||
# Admin: list and approve via gateway
|
||||
code, body, _ = s_admin.get(f"{GATEWAY}/api/v1/admin/verifications")
|
||||
report("GET /admin/verifications", code, body)
|
||||
|
||||
verification_ids = []
|
||||
if isinstance(body.get("list"), list):
|
||||
for v in body["list"]:
|
||||
verification_ids.append((v.get("id"), v.get("role")))
|
||||
|
||||
# Admin: approve all
|
||||
for vid, role in verification_ids:
|
||||
# Admin: approve only the current test user's records
|
||||
for role in ("player", "owner"):
|
||||
vid = user_verification_ids.get(role)
|
||||
if not vid:
|
||||
continue
|
||||
code, body, _ = s_admin.post(
|
||||
f"{GATEWAY}/api/v1/admin/verifications/{vid}/approve",
|
||||
json_body={},
|
||||
@@ -326,6 +372,22 @@ def phase3_admin_and_verification(
|
||||
body,
|
||||
)
|
||||
|
||||
reject_vid = reject_verification_ids.get("owner")
|
||||
if reject_vid:
|
||||
code, body, _ = s_admin.post(
|
||||
f"{GATEWAY}/api/v1/admin/verifications/{reject_vid}/reject",
|
||||
json_body={"reason": "test reject flow"},
|
||||
headers=s_admin.csrf_headers(),
|
||||
)
|
||||
report(
|
||||
f"POST /admin/verifications/{reject_vid}/reject (owner)",
|
||||
code,
|
||||
body,
|
||||
)
|
||||
|
||||
code, body, _ = s_reject.get(f"{GATEWAY}/api/v1/users/me/verification")
|
||||
report("GET /users/me/verification (after reject)", code, body)
|
||||
|
||||
# User: switch role to player
|
||||
code, body, _ = s_user.post(
|
||||
f"{GATEWAY}/api/v1/users/me/switch-role",
|
||||
@@ -334,7 +396,7 @@ def phase3_admin_and_verification(
|
||||
)
|
||||
report("POST /users/me/switch-role (player)", code, body)
|
||||
|
||||
return admin_id, verification_ids
|
||||
return admin_id
|
||||
|
||||
|
||||
def phase4_follow(s: Session, target_user_id):
|
||||
@@ -374,11 +436,11 @@ def phase5_games(s: Session, s_admin: Session):
|
||||
report("POST /games (create)", code, body)
|
||||
game_id = body.get("id", 0)
|
||||
|
||||
# createGameLogic returns empty Game{} (bug), so get game_id from list
|
||||
code, body2, _ = s.get(f"{GATEWAY}/api/v1/games")
|
||||
report("GET /games (after create)", code, body2)
|
||||
if not game_id and isinstance(body2.get("list"), list) and body2["list"]:
|
||||
game_id = body2["list"][-1].get("id", 0)
|
||||
items = pick_items(body2)
|
||||
if not game_id and items:
|
||||
game_id = as_int(items[-1].get("id"))
|
||||
|
||||
if game_id:
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/games/{game_id}")
|
||||
@@ -455,7 +517,7 @@ def phase6_player(s: Session, game_id):
|
||||
return player_id, service_id
|
||||
|
||||
|
||||
def phase7_shop(s_owner: Session, player_id):
|
||||
def phase7_shop(s_owner: Session, owner_user_id, player_id):
|
||||
print("\n=== Phase 7: Shop ===")
|
||||
|
||||
s_owner.post(
|
||||
@@ -490,12 +552,23 @@ def phase7_shop(s_owner: Session, player_id):
|
||||
report(f"GET /shops/{shop_id}", code, body)
|
||||
|
||||
code, body, _ = s_owner.put(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/template",
|
||||
json_body={"sections": json.dumps({"layout": "grid", "theme": "dark"})},
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}",
|
||||
json_body={
|
||||
"name": f"UpdatedShop_{rand_str(4)}",
|
||||
"description": "An updated test shop",
|
||||
"commissionType": "percentage",
|
||||
"commissionValue": "12",
|
||||
"allowMultiShop": True,
|
||||
"allowIndependentOrders": False,
|
||||
"dispatchMode": "manual",
|
||||
},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"PUT /shops/{shop_id}", code, body)
|
||||
|
||||
code, body, _ = s_owner.get(f"{GATEWAY}/api/v1/users/{owner_user_id}/shop")
|
||||
report(f"GET /users/{owner_user_id}/shop", code, body)
|
||||
|
||||
code, body, _ = s_owner.post(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/announcements",
|
||||
json_body={"content": "Grand opening!"},
|
||||
@@ -535,7 +608,7 @@ def phase7_shop(s_owner: Session, player_id):
|
||||
return shop_id
|
||||
|
||||
|
||||
def phase8_order(s_consumer: Session, player_id, service_id, shop_id):
|
||||
def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, shop_id):
|
||||
print("\n=== Phase 8: Orders ===")
|
||||
|
||||
s_consumer.post(
|
||||
@@ -563,6 +636,12 @@ def phase8_order(s_consumer: Session, player_id, service_id, shop_id):
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders?role=consumer")
|
||||
report("GET /orders?role=consumer", code, body)
|
||||
|
||||
code, body, _ = s_actor.get(f"{GATEWAY}/api/v1/orders?role=player")
|
||||
report("GET /orders?role=player", code, body)
|
||||
|
||||
code, body, _ = s_actor.get(f"{GATEWAY}/api/v1/orders?role=owner")
|
||||
report("GET /orders?role=owner", code, body)
|
||||
|
||||
if order_id:
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders/{order_id}")
|
||||
report(f"GET /orders/{order_id}", code, body)
|
||||
@@ -574,12 +653,26 @@ def phase8_order(s_consumer: Session, player_id, service_id, shop_id):
|
||||
)
|
||||
report(f"POST /orders/{order_id}/pay", code, body)
|
||||
|
||||
code, body, _ = s_actor.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/accept",
|
||||
json_body={},
|
||||
headers=s_actor.csrf_headers(),
|
||||
)
|
||||
report(f"POST /orders/{order_id}/accept", code, body)
|
||||
|
||||
code, body, _ = s_actor.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/request-close",
|
||||
json_body={},
|
||||
headers=s_actor.csrf_headers(),
|
||||
)
|
||||
report(f"POST /orders/{order_id}/request-close", code, body)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/cancel",
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/confirm-close",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order_id}/cancel", code, body)
|
||||
report(f"POST /orders/{order_id}/confirm-close", code, body)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders",
|
||||
@@ -593,13 +686,21 @@ def phase8_order(s_consumer: Session, player_id, service_id, shop_id):
|
||||
order2 = body.get("order", {})
|
||||
order2_id = order2.get("id", 0) if isinstance(order2, dict) else 0
|
||||
|
||||
if order2_id:
|
||||
if order_id:
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order2_id}/reorder",
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/reorder",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order2_id}/reorder", code, body)
|
||||
report(f"POST /orders/{order_id}/reorder", code, body)
|
||||
|
||||
if order2_id:
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order2_id}/cancel",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order2_id}/cancel", code, body)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/paid",
|
||||
@@ -725,7 +826,12 @@ def phase11_objectstory(s: Session):
|
||||
print("\n=== Phase 11: Objectstory (File) ===")
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/files?key=nonexistent")
|
||||
report("GET /files?key=nonexistent (expect error)", code, body, expect_status=400)
|
||||
report(
|
||||
"GET /files?key=nonexistent (expect error)",
|
||||
code,
|
||||
body,
|
||||
expect_status=(400, 500),
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
@@ -760,35 +866,52 @@ def phase13_logout(s: Session):
|
||||
report("POST /auth/logout", code, body)
|
||||
|
||||
|
||||
def phase14_misc_auth(s: Session):
|
||||
def phase14_reset_password(username, email, new_password):
|
||||
print("\n=== Phase 14: Forgot/Reset Password ===")
|
||||
s.get(f"{GATEWAY}/healthz")
|
||||
csrf = s.csrf_headers()
|
||||
|
||||
test_email = f"reset_{rand_str(4)}@example.com"
|
||||
s_reset = Session()
|
||||
s_reset.get(f"{GATEWAY}/healthz")
|
||||
csrf = s_reset.csrf_headers()
|
||||
|
||||
code, body, _ = s.post(
|
||||
code, body, _ = s_reset.post(
|
||||
f"{GATEWAY}/api/v1/auth/forgot-password/send",
|
||||
json_body={"email": test_email},
|
||||
json_body={"email": email},
|
||||
headers=csrf,
|
||||
)
|
||||
report("POST /auth/forgot-password/send", code, body)
|
||||
request_id = body.get("requestId", "")
|
||||
if not request_id:
|
||||
print(" [WARN] No requestId returned, skip reset-password")
|
||||
return
|
||||
|
||||
code, body, _ = s.post(
|
||||
vcode = read_vcode_from_redis(request_id, "reset_password", email)
|
||||
if not vcode:
|
||||
print(" [WARN] No reset password verification code found in Redis")
|
||||
return
|
||||
|
||||
csrf["X-Request-Id"] = request_id
|
||||
code, body, _ = s_reset.post(
|
||||
f"{GATEWAY}/api/v1/auth/reset-password",
|
||||
json_body={
|
||||
"email": test_email,
|
||||
"vcode": "000000",
|
||||
"newPassword": "newpass123",
|
||||
"email": email,
|
||||
"vcode": vcode,
|
||||
"newPassword": new_password,
|
||||
},
|
||||
headers=csrf,
|
||||
)
|
||||
report(
|
||||
"POST /auth/reset-password (expect fail, wrong vcode)",
|
||||
code,
|
||||
body,
|
||||
expect_status=400,
|
||||
report("POST /auth/reset-password", code, body)
|
||||
if code != 200:
|
||||
print(" [SKIP] Reset password failed, skip login verification")
|
||||
return
|
||||
|
||||
s_login = Session()
|
||||
s_login.get(f"{GATEWAY}/healthz")
|
||||
code, body, _ = s_login.post(
|
||||
f"{GATEWAY}/api/v1/auth/login",
|
||||
json_body={"username": username, "password": new_password},
|
||||
headers=s_login.csrf_headers(),
|
||||
)
|
||||
report("POST /auth/login (after reset)", code, body)
|
||||
|
||||
|
||||
def phase15_player_service_delete(s: Session, service_id):
|
||||
@@ -811,6 +934,10 @@ def main():
|
||||
user1_name = f"testuser_{suffix}"
|
||||
user1_email = f"testuser_{suffix}@example.com"
|
||||
user1_pass = "TestPass123!"
|
||||
consumer_name = f"consumer_{suffix}"
|
||||
consumer_email = f"consumer_{suffix}@example.com"
|
||||
consumer_pass = "ConsumerPass123!"
|
||||
consumer_new_pass = "ConsumerPass456!"
|
||||
admin_name = ADMIN_USERNAME
|
||||
admin_pass = ADMIN_PASSWORD
|
||||
|
||||
@@ -818,21 +945,32 @@ def main():
|
||||
|
||||
s_user = Session()
|
||||
s_admin = Session()
|
||||
s_consumer = Session()
|
||||
|
||||
phase0_health(s_user)
|
||||
phase1_register(s_user, user1_name, user1_email, user1_pass)
|
||||
login_resp = phase1_login(s_user, user1_name, user1_pass)
|
||||
phase1_register(s_user, user1_name, user1_email, user1_pass, label="primary")
|
||||
login_resp = phase1_login(s_user, user1_name, user1_pass, label="primary")
|
||||
|
||||
user_id = 0
|
||||
if isinstance(login_resp.get("user"), dict):
|
||||
user_id = login_resp["user"].get("id", 0)
|
||||
user_id = pick_user_id(login_resp)
|
||||
print(f" User ID: {user_id}")
|
||||
|
||||
phase2_user(s_user, user_id)
|
||||
|
||||
admin_id, _ = phase3_admin_and_verification(
|
||||
s_consumer.get(f"{GATEWAY}/healthz")
|
||||
phase1_register(s_consumer, consumer_name, consumer_email, consumer_pass, label="consumer")
|
||||
consumer_login_resp = phase1_login(
|
||||
s_consumer,
|
||||
consumer_name,
|
||||
consumer_pass,
|
||||
label="consumer",
|
||||
)
|
||||
consumer_user_id = pick_user_id(consumer_login_resp)
|
||||
print(f" Consumer User ID: {consumer_user_id}")
|
||||
|
||||
admin_id = phase3_admin_and_verification(
|
||||
s_admin,
|
||||
s_user,
|
||||
s_consumer,
|
||||
admin_name,
|
||||
admin_pass,
|
||||
)
|
||||
@@ -848,50 +986,14 @@ def main():
|
||||
|
||||
game_id = phase5_games(s_user, s_admin)
|
||||
player_id, service_id = phase6_player(s_user, game_id)
|
||||
shop_id = phase7_shop(s_user, player_id)
|
||||
shop_id = phase7_shop(s_user, user_id, player_id)
|
||||
|
||||
s_consumer = Session()
|
||||
consumer_name = f"consumer_{suffix}"
|
||||
consumer_email = f"consumer_{suffix}@example.com"
|
||||
consumer_pass = "ConsumerPass123!"
|
||||
s_consumer.get(f"{GATEWAY}/healthz")
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/email/verification-code/send",
|
||||
json_body={"email": consumer_email, "scene": "register"},
|
||||
headers=s_consumer.csrf_headers(),
|
||||
)
|
||||
report("POST /email/verification-code/send (consumer)", code, body)
|
||||
c_request_id = body.get("requestId", "")
|
||||
|
||||
c_vcode = read_vcode_from_redis(c_request_id, "register", consumer_email)
|
||||
if not c_vcode:
|
||||
print(" [WARN] No consumer verification code found in Redis")
|
||||
|
||||
csrf_c = s_consumer.csrf_headers()
|
||||
csrf_c["X-Request-Id"] = c_request_id
|
||||
s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/auth/register",
|
||||
json_body={
|
||||
"username": consumer_name,
|
||||
"email": consumer_email,
|
||||
"password": consumer_pass,
|
||||
"vcode": c_vcode,
|
||||
},
|
||||
headers=csrf_c,
|
||||
)
|
||||
s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/auth/login",
|
||||
json_body={"username": consumer_name, "password": consumer_pass},
|
||||
headers=s_consumer.csrf_headers(),
|
||||
)
|
||||
|
||||
phase8_order(s_consumer, player_id, service_id, shop_id)
|
||||
phase8_order(s_consumer, s_user, player_id, service_id, shop_id)
|
||||
phase9_wallet(s_user)
|
||||
phase10_community(s_user, user_id)
|
||||
phase11_objectstory(s_user)
|
||||
phase12_email(s_user)
|
||||
phase14_misc_auth(Session())
|
||||
phase14_reset_password(consumer_name, consumer_email, consumer_new_pass)
|
||||
phase15_player_service_delete(s_user, service_id)
|
||||
phase13_logout(s_user)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user