fix: 更新 dev 接口测试脚本
This commit is contained in:
+191
-8
@@ -74,6 +74,41 @@ def read_vcode_from_redis(request_id, scene, account):
|
||||
return result.stdout.strip()
|
||||
|
||||
|
||||
def build_multipart_form(fields, files):
|
||||
boundary = f"----juwan{rand_str(12)}"
|
||||
chunks = []
|
||||
|
||||
for name, value in fields.items():
|
||||
chunks.extend(
|
||||
[
|
||||
f"--{boundary}\r\n".encode(),
|
||||
f'Content-Disposition: form-data; name="{name}"\r\n\r\n'.encode(),
|
||||
str(value).encode(),
|
||||
b"\r\n",
|
||||
]
|
||||
)
|
||||
|
||||
for name, (filename, content, content_type) in files.items():
|
||||
if isinstance(content, str):
|
||||
content = content.encode()
|
||||
chunks.extend(
|
||||
[
|
||||
f"--{boundary}\r\n".encode(),
|
||||
(
|
||||
f'Content-Disposition: form-data; name="{name}"; '
|
||||
f'filename="{filename}"\r\n'
|
||||
).encode(),
|
||||
f"Content-Type: {content_type}\r\n\r\n".encode(),
|
||||
content,
|
||||
b"\r\n",
|
||||
]
|
||||
)
|
||||
|
||||
chunks.append(f"--{boundary}--\r\n".encode())
|
||||
body = b"".join(chunks)
|
||||
return f"multipart/form-data; boundary={boundary}", body
|
||||
|
||||
|
||||
class Session:
|
||||
"""Minimal cookie-aware HTTP session using stdlib only."""
|
||||
|
||||
@@ -93,7 +128,15 @@ class Session:
|
||||
return c.value
|
||||
return None
|
||||
|
||||
def request(self, method, url, json_body=None, headers=None, form_data=None):
|
||||
def request(
|
||||
self,
|
||||
method,
|
||||
url,
|
||||
json_body=None,
|
||||
headers=None,
|
||||
form_data=None,
|
||||
raw_body=None,
|
||||
):
|
||||
hdrs = headers or {}
|
||||
body = None
|
||||
if json_body is not None:
|
||||
@@ -102,6 +145,8 @@ class Session:
|
||||
elif form_data is not None:
|
||||
body = urllib.parse.urlencode(form_data).encode()
|
||||
hdrs.setdefault("Content-Type", "application/x-www-form-urlencoded")
|
||||
elif raw_body is not None:
|
||||
body = raw_body
|
||||
|
||||
req = urllib.request.Request(url, data=body, headers=hdrs, method=method)
|
||||
try:
|
||||
@@ -126,6 +171,12 @@ class Session:
|
||||
def post(self, url, **kw):
|
||||
return self.request("POST", url, **kw)
|
||||
|
||||
def post_multipart(self, url, fields, files, headers=None):
|
||||
hdrs = dict(headers or {})
|
||||
content_type, body = build_multipart_form(fields, files)
|
||||
hdrs["Content-Type"] = content_type
|
||||
return self.post(url, headers=hdrs, raw_body=body)
|
||||
|
||||
def put(self, url, **kw):
|
||||
return self.request("PUT", url, **kw)
|
||||
|
||||
@@ -156,6 +207,25 @@ def report(name, status_code, body, expect_status=200):
|
||||
return ok
|
||||
|
||||
|
||||
def report_check(name, ok, body=None):
|
||||
global passed, failed
|
||||
mark = "PASS" if ok else "FAIL"
|
||||
if not ok:
|
||||
failed += 1
|
||||
errors_list.append((name, "CHECK", body))
|
||||
else:
|
||||
passed += 1
|
||||
body_preview = json.dumps(body, ensure_ascii=False)
|
||||
if len(body_preview) > 200:
|
||||
body_preview = body_preview[:200] + "..."
|
||||
print(f" [{mark}] {name}: {body_preview}")
|
||||
return ok
|
||||
|
||||
|
||||
def skip(name, reason):
|
||||
print(f" [SKIP] {name}: {reason}")
|
||||
|
||||
|
||||
# ============================================================
|
||||
# Phase 0: Health check & CSRF
|
||||
# ============================================================
|
||||
@@ -399,6 +469,66 @@ def phase3_admin_and_verification(
|
||||
return admin_id
|
||||
|
||||
|
||||
def phase3b_secondary_player(s_admin: Session, s_player: Session):
|
||||
print("\n=== Phase 3b: Secondary Player Setup ===")
|
||||
|
||||
csrf_player = s_player.csrf_headers()
|
||||
code, body, _ = s_player.post(
|
||||
f"{GATEWAY}/api/v1/users/me/verification",
|
||||
json_body={
|
||||
"role": "player",
|
||||
"materials": {
|
||||
"idCardFront": "http://example.com/player-front.jpg",
|
||||
"idCardBack": "http://example.com/player-back.jpg",
|
||||
"gameScreenshots": ["http://example.com/player-ss1.jpg"],
|
||||
"voiceDemo": "http://example.com/player-voice.mp3",
|
||||
},
|
||||
},
|
||||
headers=csrf_player,
|
||||
)
|
||||
report("POST /users/me/verification (apply player, invited user)", code, body)
|
||||
|
||||
code, body, _ = s_player.get(f"{GATEWAY}/api/v1/users/me/verification")
|
||||
report("GET /users/me/verification (invited user)", code, body)
|
||||
verification_id = 0
|
||||
for item in pick_items(body):
|
||||
if item.get("role") == "player":
|
||||
verification_id = as_int(item.get("id"))
|
||||
break
|
||||
report_check(
|
||||
"lookup player verification id (invited user)",
|
||||
bool(verification_id),
|
||||
{"id": verification_id},
|
||||
)
|
||||
|
||||
if verification_id:
|
||||
code, body, _ = s_admin.post(
|
||||
f"{GATEWAY}/api/v1/admin/verifications/{verification_id}/approve",
|
||||
json_body={},
|
||||
headers=s_admin.csrf_headers(),
|
||||
)
|
||||
report(
|
||||
f"POST /admin/verifications/{verification_id}/approve (invited user player)",
|
||||
code,
|
||||
body,
|
||||
)
|
||||
|
||||
code, body, _ = s_player.post(
|
||||
f"{GATEWAY}/api/v1/users/me/switch-role",
|
||||
json_body={"role": "player"},
|
||||
headers=s_player.csrf_headers(),
|
||||
)
|
||||
report("POST /users/me/switch-role (invited user player)", code, body)
|
||||
|
||||
code, body, _ = s_player.post(
|
||||
f"{GATEWAY}/api/v1/players/me",
|
||||
json_body={},
|
||||
headers=s_player.csrf_headers(),
|
||||
)
|
||||
report("POST /players/me (invited user init)", code, body)
|
||||
return as_int(body.get("id"))
|
||||
|
||||
|
||||
def phase4_follow(s: Session, target_user_id):
|
||||
print("\n=== Phase 4: Follow/Unfollow ===")
|
||||
csrf = s.csrf_headers()
|
||||
@@ -517,7 +647,7 @@ def phase6_player(s: Session, game_id):
|
||||
return player_id, service_id
|
||||
|
||||
|
||||
def phase7_shop(s_owner: Session, owner_user_id, player_id):
|
||||
def phase7_shop(s_owner: Session, owner_user_id, invited_player_id):
|
||||
print("\n=== Phase 7: Shop ===")
|
||||
|
||||
s_owner.post(
|
||||
@@ -569,18 +699,38 @@ def phase7_shop(s_owner: Session, owner_user_id, player_id):
|
||||
code, body, _ = s_owner.get(f"{GATEWAY}/api/v1/users/{owner_user_id}/shop")
|
||||
report(f"GET /users/{owner_user_id}/shop", code, body)
|
||||
|
||||
announcement = f"Grand opening {rand_str(4)}!"
|
||||
code, body, _ = s_owner.post(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/announcements",
|
||||
json_body={"content": "Grand opening!"},
|
||||
json_body={"content": announcement},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /shops/{shop_id}/announcements", code, body)
|
||||
|
||||
code, body, _ = s_owner.get(f"{GATEWAY}/api/v1/shops/{shop_id}")
|
||||
report(f"GET /shops/{shop_id} (after announcement)", code, body)
|
||||
announcement_index = -1
|
||||
announcements = body.get("announcements") if isinstance(body, dict) else None
|
||||
if isinstance(announcements, list):
|
||||
for idx in range(len(announcements) - 1, -1, -1):
|
||||
if announcement in str(announcements[idx]):
|
||||
announcement_index = idx
|
||||
break
|
||||
report_check(
|
||||
f"locate announcement index ({shop_id})",
|
||||
announcement_index >= 0,
|
||||
{"index": announcement_index},
|
||||
)
|
||||
if announcement_index >= 0:
|
||||
code, body, _ = s_owner.delete(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/announcements/0",
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/announcements/{announcement_index}",
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"DELETE /shops/{shop_id}/announcements/0", code, body)
|
||||
report(
|
||||
f"DELETE /shops/{shop_id}/announcements/{announcement_index}",
|
||||
code,
|
||||
body,
|
||||
)
|
||||
|
||||
code, body, _ = s_owner.put(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/template",
|
||||
@@ -594,13 +744,25 @@ def phase7_shop(s_owner: Session, owner_user_id, player_id):
|
||||
)
|
||||
report(f"GET /shops/{shop_id}/income-stats", code, body)
|
||||
|
||||
if player_id:
|
||||
if invited_player_id:
|
||||
code, body, _ = s_owner.post(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/invitations",
|
||||
json_body={"playerId": player_id},
|
||||
json_body={"playerId": invited_player_id},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /shops/{shop_id}/invitations", code, body)
|
||||
skip(
|
||||
"POST /shops/invitations/:id/accept",
|
||||
"create invitation does not return invitation id",
|
||||
)
|
||||
skip(
|
||||
"DELETE /shops/invitations/:id",
|
||||
"create invitation does not return invitation id",
|
||||
)
|
||||
skip(
|
||||
f"DELETE /shops/{shop_id}/players/{invited_player_id}",
|
||||
"player removal depends on accepted invitation flow",
|
||||
)
|
||||
|
||||
code, body, _ = s_owner.get(f"{GATEWAY}/api/v1/shops/mine")
|
||||
report("GET /shops/mine", code, body)
|
||||
@@ -825,6 +987,26 @@ def phase10_community(s: Session, user_id):
|
||||
def phase11_objectstory(s: Session):
|
||||
print("\n=== Phase 11: Objectstory (File) ===")
|
||||
|
||||
code, body, _ = s.post_multipart(
|
||||
f"{GATEWAY}/api/v1/upload",
|
||||
fields={"type": "post"},
|
||||
files={
|
||||
"file": (
|
||||
f"test-{rand_str(4)}.txt",
|
||||
f"juwan-objectstory-{rand_str(8)}",
|
||||
"text/plain",
|
||||
)
|
||||
},
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report("POST /upload", code, body)
|
||||
if code == 200:
|
||||
report_check(
|
||||
"POST /upload returned url",
|
||||
bool(body.get("url")),
|
||||
{"url": body.get("url", "")},
|
||||
)
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/files?key=nonexistent")
|
||||
report(
|
||||
"GET /files?key=nonexistent (expect error)",
|
||||
@@ -974,6 +1156,7 @@ def main():
|
||||
admin_name,
|
||||
admin_pass,
|
||||
)
|
||||
invited_player_id = phase3b_secondary_player(s_admin, s_consumer)
|
||||
|
||||
if admin_id and user_id:
|
||||
phase4_follow(s_user, admin_id)
|
||||
@@ -986,7 +1169,7 @@ 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, user_id, player_id)
|
||||
shop_id = phase7_shop(s_user, user_id, invited_player_id)
|
||||
|
||||
phase8_order(s_consumer, s_user, player_id, service_id, shop_id)
|
||||
phase9_wallet(s_user)
|
||||
|
||||
Reference in New Issue
Block a user