fix: 更新接口测试脚本

This commit is contained in:
zetaloop
2026-04-23 03:24:24 +08:00
parent cd2c6d4192
commit 9bc86dd904
+197 -95
View File
@@ -10,7 +10,6 @@ import json
import random import random
import string import string
import sys import sys
import time
import urllib.request import urllib.request
import urllib.error import urllib.error
import urllib.parse import urllib.parse
@@ -32,6 +31,29 @@ def rand_str(n=8):
return "".join(random.choices(string.ascii_lowercase + string.digits, k=n)) 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): def read_vcode_from_redis(request_id, scene, account):
if not request_id: if not request_id:
return "" return ""
@@ -117,6 +139,9 @@ class Session:
def report(name, status_code, body, expect_status=200): def report(name, status_code, body, expect_status=200):
global passed, failed global passed, failed
if isinstance(expect_status, (list, tuple, set)):
ok = status_code in expect_status
else:
ok = status_code == expect_status ok = status_code == expect_status
mark = "PASS" if ok else "FAIL" mark = "PASS" if ok else "FAIL"
if not ok: if not ok:
@@ -136,7 +161,7 @@ def report(name, status_code, body, expect_status=200):
# ============================================================ # ============================================================
def phase0_health(s: Session): def phase0_health(s: Session):
print("\n=== Phase 0: Health & CSRF ===") 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) report("GET /healthz", code, body)
xsrf = s.get_cookie("__Host-XSRF-TOKEN") xsrf = s.get_cookie("__Host-XSRF-TOKEN")
xsrf_guard = s.get_cookie("__Host-XSRF-GUARD") xsrf_guard = s.get_cookie("__Host-XSRF-GUARD")
@@ -149,8 +174,8 @@ def phase0_health(s: Session):
# ============================================================ # ============================================================
# Phase 1: Registration # Phase 1: Registration
# ============================================================ # ============================================================
def phase1_register(s: Session, username, email, password): def phase1_register(s: Session, username, email, password, label="user"):
print("\n=== Phase 1: Register ===") print(f"\n=== Phase 1: Register ({label}) ===")
# Step 1: send verification code via gateway # Step 1: send verification code via gateway
code, body, _ = s.post( code, body, _ = s.post(
@@ -158,7 +183,7 @@ def phase1_register(s: Session, username, email, password):
json_body={"email": email, "scene": "register"}, json_body={"email": email, "scene": "register"},
headers=s.csrf_headers(), 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", "") request_id = body.get("requestId", "")
if not request_id: if not request_id:
print(" [ERROR] No requestId returned, cannot register") print(" [ERROR] No requestId returned, cannot register")
@@ -185,19 +210,19 @@ def phase1_register(s: Session, username, email, password):
}, },
headers=csrf, headers=csrf,
) )
report("POST /auth/register (gateway)", code, body) report(f"POST /auth/register ({label})", code, body)
return body return body
def phase1_login(s: Session, username, password): def phase1_login(s: Session, username, password, label="user"):
print("\n=== Phase 1b: Login ===") print(f"\n=== Phase 1b: Login ({label}) ===")
csrf = s.csrf_headers() csrf = s.csrf_headers()
code, body, _ = s.post( code, body, _ = s.post(
f"{GATEWAY}/api/v1/auth/login", f"{GATEWAY}/api/v1/auth/login",
json_body={"username": username, "password": password}, json_body={"username": username, "password": password},
headers=csrf, headers=csrf,
) )
report("POST /auth/login", code, body) report(f"POST /auth/login ({label})", code, body)
jtoken = s.get_cookie("JToken") jtoken = s.get_cookie("JToken")
print(f" JToken: {jtoken[:30]}..." if jtoken else " JToken: None") print(f" JToken: {jtoken[:30]}..." if jtoken else " JToken: None")
return body return body
@@ -244,6 +269,7 @@ def phase2_user(s: Session, user_id):
def phase3_admin_and_verification( def phase3_admin_and_verification(
s_admin: Session, s_admin: Session,
s_user: Session, s_user: Session,
s_reject: Session,
admin_user, admin_user,
admin_pass, admin_pass,
): ):
@@ -259,13 +285,9 @@ def phase3_admin_and_verification(
) )
report("POST /auth/login (admin)", code, body) report("POST /auth/login (admin)", code, body)
admin_id = 0
code, body, _ = s_admin.get(f"{GATEWAY}/api/v1/users/me") code, body, _ = s_admin.get(f"{GATEWAY}/api/v1/users/me")
report("GET /users/me (admin)", code, body) report("GET /users/me (admin)", code, body)
if isinstance(body.get("user"), dict): admin_id = pick_user_id(body)
admin_id = body["user"].get("id", admin_id)
elif body.get("id"):
admin_id = body.get("id", admin_id)
# User applies for player verification # User applies for player verification
csrf_user = s_user.csrf_headers() csrf_user = s_user.csrf_headers()
@@ -300,21 +322,45 @@ def phase3_admin_and_verification(
) )
report("POST /users/me/verification (apply owner)", code, body) 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 # Get my verifications
code, body, _ = s_user.get(f"{GATEWAY}/api/v1/users/me/verification") code, body, _ = s_user.get(f"{GATEWAY}/api/v1/users/me/verification")
report("GET /users/me/verification", code, body) 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 # Admin: list and approve via gateway
code, body, _ = s_admin.get(f"{GATEWAY}/api/v1/admin/verifications") code, body, _ = s_admin.get(f"{GATEWAY}/api/v1/admin/verifications")
report("GET /admin/verifications", code, body) report("GET /admin/verifications", code, body)
verification_ids = [] # Admin: approve only the current test user's records
if isinstance(body.get("list"), list): for role in ("player", "owner"):
for v in body["list"]: vid = user_verification_ids.get(role)
verification_ids.append((v.get("id"), v.get("role"))) if not vid:
continue
# Admin: approve all
for vid, role in verification_ids:
code, body, _ = s_admin.post( code, body, _ = s_admin.post(
f"{GATEWAY}/api/v1/admin/verifications/{vid}/approve", f"{GATEWAY}/api/v1/admin/verifications/{vid}/approve",
json_body={}, json_body={},
@@ -326,6 +372,22 @@ def phase3_admin_and_verification(
body, 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 # User: switch role to player
code, body, _ = s_user.post( code, body, _ = s_user.post(
f"{GATEWAY}/api/v1/users/me/switch-role", 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) 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): 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) report("POST /games (create)", code, body)
game_id = body.get("id", 0) 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") code, body2, _ = s.get(f"{GATEWAY}/api/v1/games")
report("GET /games (after create)", code, body2) report("GET /games (after create)", code, body2)
if not game_id and isinstance(body2.get("list"), list) and body2["list"]: items = pick_items(body2)
game_id = body2["list"][-1].get("id", 0) if not game_id and items:
game_id = as_int(items[-1].get("id"))
if game_id: if game_id:
code, body, _ = s.get(f"{GATEWAY}/api/v1/games/{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 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 ===") print("\n=== Phase 7: Shop ===")
s_owner.post( s_owner.post(
@@ -490,12 +552,23 @@ def phase7_shop(s_owner: Session, player_id):
report(f"GET /shops/{shop_id}", code, body) report(f"GET /shops/{shop_id}", code, body)
code, body, _ = s_owner.put( code, body, _ = s_owner.put(
f"{GATEWAY}/api/v1/shops/{shop_id}/template", f"{GATEWAY}/api/v1/shops/{shop_id}",
json_body={"sections": json.dumps({"layout": "grid", "theme": "dark"})}, 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, headers=csrf,
) )
report(f"PUT /shops/{shop_id}", code, body) 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( code, body, _ = s_owner.post(
f"{GATEWAY}/api/v1/shops/{shop_id}/announcements", f"{GATEWAY}/api/v1/shops/{shop_id}/announcements",
json_body={"content": "Grand opening!"}, json_body={"content": "Grand opening!"},
@@ -535,7 +608,7 @@ def phase7_shop(s_owner: Session, player_id):
return shop_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 ===") print("\n=== Phase 8: Orders ===")
s_consumer.post( 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") code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders?role=consumer")
report("GET /orders?role=consumer", code, body) 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: if order_id:
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders/{order_id}") code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders/{order_id}")
report(f"GET /orders/{order_id}", code, body) 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) 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( code, body, _ = s_consumer.post(
f"{GATEWAY}/api/v1/orders/{order_id}/cancel", f"{GATEWAY}/api/v1/orders/{order_id}/confirm-close",
json_body={}, json_body={},
headers=csrf, 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( code, body, _ = s_consumer.post(
f"{GATEWAY}/api/v1/orders", 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 = body.get("order", {})
order2_id = order2.get("id", 0) if isinstance(order2, dict) else 0 order2_id = order2.get("id", 0) if isinstance(order2, dict) else 0
if order2_id: if order_id:
code, body, _ = s_consumer.post( code, body, _ = s_consumer.post(
f"{GATEWAY}/api/v1/orders/{order2_id}/reorder", f"{GATEWAY}/api/v1/orders/{order_id}/reorder",
json_body={}, json_body={},
headers=csrf, 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( code, body, _ = s_consumer.post(
f"{GATEWAY}/api/v1/orders/paid", f"{GATEWAY}/api/v1/orders/paid",
@@ -725,7 +826,12 @@ def phase11_objectstory(s: Session):
print("\n=== Phase 11: Objectstory (File) ===") print("\n=== Phase 11: Objectstory (File) ===")
code, body, _ = s.get(f"{GATEWAY}/api/v1/files?key=nonexistent") 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 return
@@ -760,35 +866,52 @@ def phase13_logout(s: Session):
report("POST /auth/logout", code, body) 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 ===") 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", f"{GATEWAY}/api/v1/auth/forgot-password/send",
json_body={"email": test_email}, json_body={"email": email},
headers=csrf, headers=csrf,
) )
report("POST /auth/forgot-password/send", code, body) 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", f"{GATEWAY}/api/v1/auth/reset-password",
json_body={ json_body={
"email": test_email, "email": email,
"vcode": "000000", "vcode": vcode,
"newPassword": "newpass123", "newPassword": new_password,
}, },
headers=csrf, headers=csrf,
) )
report( report("POST /auth/reset-password", code, body)
"POST /auth/reset-password (expect fail, wrong vcode)", if code != 200:
code, print(" [SKIP] Reset password failed, skip login verification")
body, return
expect_status=400,
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): def phase15_player_service_delete(s: Session, service_id):
@@ -811,6 +934,10 @@ def main():
user1_name = f"testuser_{suffix}" user1_name = f"testuser_{suffix}"
user1_email = f"testuser_{suffix}@example.com" user1_email = f"testuser_{suffix}@example.com"
user1_pass = "TestPass123!" 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_name = ADMIN_USERNAME
admin_pass = ADMIN_PASSWORD admin_pass = ADMIN_PASSWORD
@@ -818,21 +945,32 @@ def main():
s_user = Session() s_user = Session()
s_admin = Session() s_admin = Session()
s_consumer = Session()
phase0_health(s_user) phase0_health(s_user)
phase1_register(s_user, user1_name, user1_email, user1_pass) phase1_register(s_user, user1_name, user1_email, user1_pass, label="primary")
login_resp = phase1_login(s_user, user1_name, user1_pass) login_resp = phase1_login(s_user, user1_name, user1_pass, label="primary")
user_id = 0 user_id = pick_user_id(login_resp)
if isinstance(login_resp.get("user"), dict):
user_id = login_resp["user"].get("id", 0)
print(f" User ID: {user_id}") print(f" User ID: {user_id}")
phase2_user(s_user, 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_admin,
s_user, s_user,
s_consumer,
admin_name, admin_name,
admin_pass, admin_pass,
) )
@@ -848,50 +986,14 @@ def main():
game_id = phase5_games(s_user, s_admin) game_id = phase5_games(s_user, s_admin)
player_id, service_id = phase6_player(s_user, game_id) 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() phase8_order(s_consumer, s_user, player_id, service_id, shop_id)
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)
phase9_wallet(s_user) phase9_wallet(s_user)
phase10_community(s_user, user_id) phase10_community(s_user, user_id)
phase11_objectstory(s_user) phase11_objectstory(s_user)
phase12_email(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) phase15_player_service_delete(s_user, service_id)
phase13_logout(s_user) phase13_logout(s_user)