fix: 修复 player 详情 completionRate 和 k8s 公开路由
同步 k8s Envoy 中已放行公开接口的实际路由,避免落到泛用 user API;同时规范 dev API 测试脚本的响应体解析,使新增负向断言通过静态检查。
This commit is contained in:
@@ -45,11 +45,16 @@ func (l *GetPlayerLogic) GetPlayer(req *types.GetPlayerReq) (resp *types.PlayerP
|
||||
if player == nil {
|
||||
return nil, errors.New("player not found")
|
||||
}
|
||||
completionRate := 0.0
|
||||
if player.TotalOrders > 0 {
|
||||
completionRate = float64(player.CompletedOrders) / float64(player.TotalOrders)
|
||||
}
|
||||
|
||||
resp = &types.PlayerProfile{
|
||||
Id: player.Id,
|
||||
Rating: player.Rating,
|
||||
TotalOrders: player.TotalOrders,
|
||||
CompletionRate: completionRate,
|
||||
Status: player.Status,
|
||||
Gender: player.Gender,
|
||||
Services: []types.PlayerService{},
|
||||
|
||||
+203
-88
@@ -16,7 +16,12 @@ import urllib.parse
|
||||
import http.cookiejar
|
||||
import os
|
||||
import subprocess
|
||||
from collections.abc import Callable
|
||||
from decimal import Decimal, InvalidOperation
|
||||
from typing import Any
|
||||
|
||||
Body = dict[str, Any]
|
||||
StatusExpectation = int | tuple[int, ...] | list[int] | set[int]
|
||||
|
||||
GATEWAY = "http://127.0.0.1:18080"
|
||||
ADMIN_USERNAME = os.getenv("ADMIN_USERNAME", "admin")
|
||||
@@ -50,7 +55,19 @@ def same_id(left, right):
|
||||
return str(left) == str(right)
|
||||
|
||||
|
||||
def body_text(body):
|
||||
def response_body(data: str) -> Body:
|
||||
if not data:
|
||||
return {}
|
||||
try:
|
||||
parsed = json.loads(data)
|
||||
except json.JSONDecodeError:
|
||||
return {"_raw": data}
|
||||
if isinstance(parsed, dict):
|
||||
return parsed
|
||||
return {"_raw": parsed}
|
||||
|
||||
|
||||
def body_text(body: object):
|
||||
if not isinstance(body, dict):
|
||||
return str(body)
|
||||
if "_raw" in body:
|
||||
@@ -72,15 +89,17 @@ def report_rejected(name, status_code, body, expect_status=(400, 403, 500)):
|
||||
report_check(f"{name} returns error body", bool(body_text(body)), body)
|
||||
|
||||
|
||||
def pick_items(body):
|
||||
def pick_items(body: object) -> list[Body]:
|
||||
if not isinstance(body, dict):
|
||||
return []
|
||||
if isinstance(body.get("items"), list):
|
||||
return body["items"]
|
||||
return [item for item in body["items"] if isinstance(item, dict)]
|
||||
if isinstance(body.get("list"), list):
|
||||
return body["list"]
|
||||
return [item for item in body["list"] if isinstance(item, dict)]
|
||||
return []
|
||||
|
||||
|
||||
def find_item(items, predicate):
|
||||
def find_item(items: list[Body], predicate: Callable[[Body], bool]) -> Body | None:
|
||||
for item in items:
|
||||
if predicate(item):
|
||||
return item
|
||||
@@ -91,7 +110,7 @@ def find_item_by_id(items, item_id):
|
||||
return find_item(items, lambda item: same_id(item.get("id"), item_id))
|
||||
|
||||
|
||||
def user_from(body):
|
||||
def user_from(body: Body) -> Body:
|
||||
user = body.get("user") if isinstance(body, dict) else None
|
||||
return user if isinstance(user, dict) else {}
|
||||
|
||||
@@ -186,7 +205,7 @@ class Session:
|
||||
headers=None,
|
||||
form_data=None,
|
||||
raw_body=None,
|
||||
):
|
||||
) -> tuple[int, Body, dict[str, str]]:
|
||||
hdrs = headers or {}
|
||||
body = None
|
||||
if json_body is not None:
|
||||
@@ -202,35 +221,31 @@ class Session:
|
||||
try:
|
||||
resp = self.opener.open(req, timeout=15)
|
||||
data = resp.read().decode()
|
||||
try:
|
||||
return resp.status, json.loads(data) if data else {}, dict(resp.headers)
|
||||
except json.JSONDecodeError:
|
||||
return resp.status, {"_raw": data}, dict(resp.headers)
|
||||
return resp.status, response_body(data), dict(resp.headers)
|
||||
except urllib.error.HTTPError as e:
|
||||
data = e.read().decode() if e.fp else ""
|
||||
try:
|
||||
return e.code, json.loads(data) if data else {}, dict(e.headers)
|
||||
except json.JSONDecodeError:
|
||||
return e.code, {"_raw": data}, dict(e.headers)
|
||||
return e.code, response_body(data), dict(e.headers)
|
||||
except Exception as e:
|
||||
return 0, {"_error": str(e)}, {}
|
||||
|
||||
def get(self, url, **kw):
|
||||
def get(self, url, **kw) -> tuple[int, Body, dict[str, str]]:
|
||||
return self.request("GET", url, **kw)
|
||||
|
||||
def post(self, url, **kw):
|
||||
def post(self, url, **kw) -> tuple[int, Body, dict[str, str]]:
|
||||
return self.request("POST", url, **kw)
|
||||
|
||||
def post_multipart(self, url, fields, files, headers=None):
|
||||
def post_multipart(
|
||||
self, url, fields, files, headers=None
|
||||
) -> tuple[int, Body, dict[str, str]]:
|
||||
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):
|
||||
def put(self, url, **kw) -> tuple[int, Body, dict[str, str]]:
|
||||
return self.request("PUT", url, **kw)
|
||||
|
||||
def delete(self, url, **kw):
|
||||
def delete(self, url, **kw) -> tuple[int, Body, dict[str, str]]:
|
||||
return self.request("DELETE", url, **kw)
|
||||
|
||||
def csrf_headers(self):
|
||||
@@ -238,7 +253,7 @@ class Session:
|
||||
return {"xsrf-token": token} if token else {}
|
||||
|
||||
|
||||
def report(name, status_code, body, expect_status=200):
|
||||
def report(name, status_code, body, expect_status: StatusExpectation = 200):
|
||||
global passed, failed
|
||||
if isinstance(expect_status, (list, tuple, set)):
|
||||
ok = status_code in expect_status
|
||||
@@ -276,7 +291,7 @@ def skip(name, reason):
|
||||
print(f" [SKIP] {name}: {reason}")
|
||||
|
||||
|
||||
def check_order_status(session, order_id, expected_status, label):
|
||||
def check_order_status(session, order_id, expected_status, label) -> Body:
|
||||
code, body, _ = session.get(f"{GATEWAY}/api/v1/orders/{order_id}")
|
||||
report(f"GET /orders/{order_id} ({label})", code, body)
|
||||
report_check(
|
||||
@@ -521,8 +536,9 @@ def phase3_admin_and_verification(
|
||||
all(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item, role=role: item.get("role") == role
|
||||
and item.get("status") == "pending",
|
||||
lambda item, role=role: (
|
||||
item.get("role") == role and item.get("status") == "pending"
|
||||
),
|
||||
)
|
||||
for role in ("player", "owner")
|
||||
),
|
||||
@@ -539,8 +555,9 @@ def phase3_admin_and_verification(
|
||||
bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: item.get("role") == "owner"
|
||||
and item.get("status") == "pending",
|
||||
lambda item: (
|
||||
item.get("role") == "owner" and item.get("status") == "pending"
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -586,9 +603,11 @@ def phase3_admin_and_verification(
|
||||
bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: item.get("role") == "owner"
|
||||
lambda item: (
|
||||
item.get("role") == "owner"
|
||||
and item.get("status") == "rejected"
|
||||
and item.get("rejectReason") == "test reject flow",
|
||||
and item.get("rejectReason") == "test reject flow"
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -671,8 +690,9 @@ def phase3b_secondary_player(s_admin: Session, s_player: Session):
|
||||
bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: item.get("role") == "player"
|
||||
and item.get("status") == "pending",
|
||||
lambda item: (
|
||||
item.get("role") == "player" and item.get("status") == "pending"
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -698,7 +718,9 @@ def phase3b_secondary_player(s_admin: Session, s_player: Session):
|
||||
report("POST /users/me/switch-role (invited user player)", code, body)
|
||||
code, body, _ = s_player.get(f"{GATEWAY}/api/v1/users/me")
|
||||
report("GET /users/me (invited user player role)", code, body)
|
||||
report_check("invited user current role is player", body.get("role") == "player", body)
|
||||
report_check(
|
||||
"invited user current role is player", body.get("role") == "player", body
|
||||
)
|
||||
|
||||
code, body, _ = s_player.post(
|
||||
f"{GATEWAY}/api/v1/players/me",
|
||||
@@ -739,7 +761,9 @@ def phase5_games(s: Session, s_admin: Session):
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/games")
|
||||
report("GET /games", code, body)
|
||||
report_check("GET /games returns list shape", isinstance(pick_items(body), list), body)
|
||||
report_check(
|
||||
"GET /games returns list shape", isinstance(pick_items(body), list), body
|
||||
)
|
||||
|
||||
csrf_admin = s_admin.csrf_headers()
|
||||
game_name = f"TestGame_{rand_str(4)}"
|
||||
@@ -820,7 +844,9 @@ def phase6_player(s: Session, game_id):
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/players")
|
||||
report("GET /players", code, body)
|
||||
report_check("GET /players returns list shape", isinstance(pick_items(body), list), body)
|
||||
report_check(
|
||||
"GET /players returns list shape", isinstance(pick_items(body), list), body
|
||||
)
|
||||
|
||||
if player_id:
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/players/{player_id}")
|
||||
@@ -866,7 +892,9 @@ def phase6_player(s: Session, game_id):
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/services")
|
||||
report("GET /services", code, body)
|
||||
report_check("GET /services returns list shape", isinstance(pick_items(body), list), body)
|
||||
report_check(
|
||||
"GET /services returns list shape", isinstance(pick_items(body), list), body
|
||||
)
|
||||
|
||||
service_id = svc_body.get("id", 0) if svc_body else 0
|
||||
if service_id:
|
||||
@@ -1058,7 +1086,8 @@ def phase7_shop(
|
||||
report(f"GET /shops/{shop_id} (after announcement delete)", code, body)
|
||||
report_check(
|
||||
"deleted announcement is absent",
|
||||
announcement not in [str(item) for item in body.get("announcements") or []],
|
||||
announcement
|
||||
not in [str(item) for item in body.get("announcements") or []],
|
||||
body,
|
||||
)
|
||||
|
||||
@@ -1155,7 +1184,9 @@ def phase7_shop(
|
||||
code, body, _ = s_owner.get(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/invitations",
|
||||
)
|
||||
report(f"GET /shops/{shop_id}/invitations (after wrong accept)", code, body)
|
||||
report(
|
||||
f"GET /shops/{shop_id}/invitations (after wrong accept)", code, body
|
||||
)
|
||||
pending_invitation = find_item_by_id(pick_items(body), invitation_id)
|
||||
report_check(
|
||||
"wrong invitation accept keeps status pending",
|
||||
@@ -1248,7 +1279,9 @@ def phase7_shop(
|
||||
code, body, _ = s_owner.get(
|
||||
f"{GATEWAY}/api/v1/shops/{shop_id}/invitations",
|
||||
)
|
||||
report(f"GET /shops/{shop_id}/invitations (after reject)", code, body)
|
||||
report(
|
||||
f"GET /shops/{shop_id}/invitations (after reject)", code, body
|
||||
)
|
||||
rejected = find_item_by_id(pick_items(body), reinvite_id)
|
||||
report_check(
|
||||
"rejected invitation has rejected status",
|
||||
@@ -1260,7 +1293,9 @@ def phase7_shop(
|
||||
|
||||
code, body, _ = s_owner.get(f"{GATEWAY}/api/v1/shops/mine")
|
||||
report("GET /shops/mine", code, body)
|
||||
report_check("GET /shops/mine returns owned shop", same_id(body.get("id"), shop_id), body)
|
||||
report_check(
|
||||
"GET /shops/mine returns owned shop", same_id(body.get("id"), shop_id), body
|
||||
)
|
||||
|
||||
return shop_id
|
||||
|
||||
@@ -1331,7 +1366,9 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
)
|
||||
|
||||
if order_id:
|
||||
body = check_order_status(s_consumer, order_id, "pending_payment", "after create")
|
||||
body = check_order_status(
|
||||
s_consumer, order_id, "pending_payment", "after create"
|
||||
)
|
||||
report_check(
|
||||
"created order detail matches participants",
|
||||
same_id(body.get("consumerId"), consumer_user_id)
|
||||
@@ -1348,15 +1385,21 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
headers=s_actor.csrf_headers(),
|
||||
)
|
||||
report_rejected(f"POST /orders/{order_id}/accept (before pay)", code, body)
|
||||
check_order_status(s_consumer, order_id, "pending_payment", "after early accept")
|
||||
check_order_status(
|
||||
s_consumer, order_id, "pending_payment", "after early accept"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/confirm-close",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report_rejected(f"POST /orders/{order_id}/confirm-close (before close request)", code, body)
|
||||
check_order_status(s_consumer, order_id, "pending_payment", "after early confirm-close")
|
||||
report_rejected(
|
||||
f"POST /orders/{order_id}/confirm-close (before close request)", code, body
|
||||
)
|
||||
check_order_status(
|
||||
s_consumer, order_id, "pending_payment", "after early confirm-close"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/pay",
|
||||
@@ -1371,8 +1414,12 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
json_body={},
|
||||
headers=s_actor.csrf_headers(),
|
||||
)
|
||||
report_rejected(f"POST /orders/{order_id}/request-close (before accept)", code, body)
|
||||
check_order_status(s_consumer, order_id, "pending_accept", "after early request-close")
|
||||
report_rejected(
|
||||
f"POST /orders/{order_id}/request-close (before accept)", code, body
|
||||
)
|
||||
check_order_status(
|
||||
s_consumer, order_id, "pending_accept", "after early request-close"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/pay",
|
||||
@@ -1380,7 +1427,9 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
headers=csrf,
|
||||
)
|
||||
report_rejected(f"POST /orders/{order_id}/pay (second time)", code, body)
|
||||
check_order_status(s_consumer, order_id, "pending_accept", "after duplicate pay")
|
||||
check_order_status(
|
||||
s_consumer, order_id, "pending_accept", "after duplicate pay"
|
||||
)
|
||||
|
||||
code, body, _ = s_actor.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/accept",
|
||||
@@ -1389,15 +1438,21 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
)
|
||||
report(f"POST /orders/{order_id}/accept", code, body)
|
||||
body = check_order_status(s_consumer, order_id, "in_progress", "after accept")
|
||||
report_check("accepted order has acceptedAt", bool(body.get("acceptedAt")), body)
|
||||
report_check(
|
||||
"accepted order has acceptedAt", bool(body.get("acceptedAt")), body
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/confirm-close",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report_rejected(f"POST /orders/{order_id}/confirm-close (before request-close)", code, body)
|
||||
check_order_status(s_consumer, order_id, "in_progress", "after early confirm-close")
|
||||
report_rejected(
|
||||
f"POST /orders/{order_id}/confirm-close (before request-close)", code, body
|
||||
)
|
||||
check_order_status(
|
||||
s_consumer, order_id, "in_progress", "after early confirm-close"
|
||||
)
|
||||
|
||||
code, body, _ = s_actor.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/request-close",
|
||||
@@ -1413,7 +1468,9 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order_id}/confirm-close", code, body)
|
||||
check_order_status(s_consumer, order_id, "pending_review", "after confirm-close")
|
||||
check_order_status(
|
||||
s_consumer, order_id, "pending_review", "after confirm-close"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders",
|
||||
@@ -1438,8 +1495,12 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
json_body={"rating": 5, "content": "too early"},
|
||||
headers=csrf,
|
||||
)
|
||||
report_rejected(f"POST /orders/{order2_id}/review (before completion)", code, body)
|
||||
check_order_status(s_consumer, order2_id, "pending_payment", "after early review")
|
||||
report_rejected(
|
||||
f"POST /orders/{order2_id}/review (before completion)", code, body
|
||||
)
|
||||
check_order_status(
|
||||
s_consumer, order2_id, "pending_payment", "after early review"
|
||||
)
|
||||
|
||||
if order_id:
|
||||
code, body, _ = s_consumer.post(
|
||||
@@ -1465,7 +1526,9 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order2_id}/pay (before cancel)", code, body)
|
||||
check_order_status(s_consumer, order2_id, "pending_accept", "second order after pay")
|
||||
check_order_status(
|
||||
s_consumer, order2_id, "pending_accept", "second order after pay"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order2_id}/cancel",
|
||||
@@ -1473,7 +1536,9 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order2_id}/cancel", code, body)
|
||||
check_order_status(s_consumer, order2_id, "cancelled", "second order after cancel")
|
||||
check_order_status(
|
||||
s_consumer, order2_id, "cancelled", "second order after cancel"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order2_id}/pay",
|
||||
@@ -1547,8 +1612,14 @@ def phase8b_review(
|
||||
)
|
||||
report(f"POST /orders/{order_id}/review (counterparty)", code, body)
|
||||
|
||||
order_body = check_order_status(s_consumer, order_id, "completed", "after two reviews")
|
||||
report_check("completed order has completedAt", bool(order_body.get("completedAt")), order_body)
|
||||
order_body = check_order_status(
|
||||
s_consumer, order_id, "completed", "after two reviews"
|
||||
)
|
||||
report_check(
|
||||
"completed order has completedAt",
|
||||
bool(order_body.get("completedAt")),
|
||||
order_body,
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders/{order_id}/reviews")
|
||||
report(f"GET /orders/{order_id}/reviews (after two reviews)", code, body)
|
||||
@@ -1558,8 +1629,12 @@ def phase8b_review(
|
||||
len(reviews) == 2
|
||||
and all(same_id(item.get("orderId"), order_id) for item in reviews)
|
||||
and all(item.get("sealed") is False for item in reviews)
|
||||
and bool(find_item(reviews, lambda item: item.get("content") == "great service"))
|
||||
and bool(find_item(reviews, lambda item: item.get("content") == "smooth buyer")),
|
||||
and bool(
|
||||
find_item(reviews, lambda item: item.get("content") == "great service")
|
||||
)
|
||||
and bool(
|
||||
find_item(reviews, lambda item: item.get("content") == "smooth buyer")
|
||||
),
|
||||
body,
|
||||
)
|
||||
|
||||
@@ -1570,8 +1645,10 @@ def phase8b_review(
|
||||
bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: same_id(item.get("orderId"), order_id)
|
||||
and item.get("sealed") is False,
|
||||
lambda item: (
|
||||
same_id(item.get("orderId"), order_id)
|
||||
and item.get("sealed") is False
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -1587,16 +1664,20 @@ def phase8b_review(
|
||||
bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: same_id(item.get("orderId"), order_id)
|
||||
lambda item: (
|
||||
same_id(item.get("orderId"), order_id)
|
||||
and same_id(item.get("fromUserId"), consumer_user_id)
|
||||
and item.get("content") == "great service",
|
||||
and item.get("content") == "great service"
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
)
|
||||
|
||||
|
||||
def phase8c_dispute(s_consumer: Session, s_actor: Session, player_id, service_id, shop_id):
|
||||
def phase8c_dispute(
|
||||
s_consumer: Session, s_actor: Session, player_id, service_id, shop_id
|
||||
):
|
||||
print("\n=== Phase 8c: Disputes ===")
|
||||
if not player_id or not service_id:
|
||||
skip("Dispute flow", "Missing player or service id")
|
||||
@@ -1643,7 +1724,9 @@ def phase8c_dispute(s_consumer: Session, s_actor: Session, player_id, service_id
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order_id}/pay (dispute flow)", code, body)
|
||||
check_order_status(s_consumer, order_id, "pending_accept", "dispute order after pay")
|
||||
check_order_status(
|
||||
s_consumer, order_id, "pending_accept", "dispute order after pay"
|
||||
)
|
||||
|
||||
code, body, _ = s_actor.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/accept",
|
||||
@@ -1651,7 +1734,9 @@ def phase8c_dispute(s_consumer: Session, s_actor: Session, player_id, service_id
|
||||
headers=s_actor.csrf_headers(),
|
||||
)
|
||||
report(f"POST /orders/{order_id}/accept (dispute flow)", code, body)
|
||||
check_order_status(s_consumer, order_id, "in_progress", "dispute order after accept")
|
||||
check_order_status(
|
||||
s_consumer, order_id, "in_progress", "dispute order after accept"
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/dispute",
|
||||
@@ -1694,8 +1779,9 @@ def phase8c_dispute(s_consumer: Session, s_actor: Session, player_id, service_id
|
||||
bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: same_id(item.get("id"), dispute_id)
|
||||
and item.get("status") == "open",
|
||||
lambda item: (
|
||||
same_id(item.get("id"), dispute_id) and item.get("status") == "open"
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -1746,7 +1832,9 @@ def phase8d_notifications(s: Session):
|
||||
report("GET /notifications", code, body)
|
||||
report_check(
|
||||
"GET /notifications returns list shape",
|
||||
code == 200 and isinstance(pick_items(body), list) and isinstance(body.get("meta"), dict),
|
||||
code == 200
|
||||
and isinstance(pick_items(body), list)
|
||||
and isinstance(body.get("meta"), dict),
|
||||
body,
|
||||
)
|
||||
|
||||
@@ -1812,7 +1900,9 @@ def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
report("GET /favorites", code, body)
|
||||
report_check(
|
||||
"GET /favorites returns list shape",
|
||||
code == 200 and isinstance(pick_items(body), list) and isinstance(body.get("meta"), dict),
|
||||
code == 200
|
||||
and isinstance(pick_items(body), list)
|
||||
and isinstance(body.get("meta"), dict),
|
||||
body,
|
||||
)
|
||||
|
||||
@@ -1833,8 +1923,10 @@ def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
not bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: item.get("targetType") == "player"
|
||||
and same_id(item.get("targetId"), "not-a-snowflake"),
|
||||
lambda item: (
|
||||
item.get("targetType") == "player"
|
||||
and same_id(item.get("targetId"), "not-a-snowflake")
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -1871,9 +1963,11 @@ def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
bool(
|
||||
find_item(
|
||||
favorite_items,
|
||||
lambda item: item.get("targetType") == "player"
|
||||
lambda item: (
|
||||
item.get("targetType") == "player"
|
||||
and same_id(item.get("targetId"), player_id)
|
||||
and same_id(item.get("userId"), user_id),
|
||||
and same_id(item.get("userId"), user_id)
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -1884,18 +1978,19 @@ def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
bool(
|
||||
find_item(
|
||||
favorite_items,
|
||||
lambda item: item.get("targetType") == "shop"
|
||||
lambda item: (
|
||||
item.get("targetType") == "shop"
|
||||
and same_id(item.get("targetId"), shop_id)
|
||||
and same_id(item.get("userId"), user_id),
|
||||
and same_id(item.get("userId"), user_id)
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
)
|
||||
favorite_id = 0
|
||||
for item in favorite_items:
|
||||
if (
|
||||
item.get("targetType") == "player"
|
||||
and str(item.get("targetId")) == str(player_id)
|
||||
if item.get("targetType") == "player" and str(item.get("targetId")) == str(
|
||||
player_id
|
||||
):
|
||||
favorite_id = as_int(item.get("id"))
|
||||
break
|
||||
@@ -1914,7 +2009,9 @@ def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
)
|
||||
report(f"GET /users/{user_id}/favorites/check (after delete)", code, body)
|
||||
if code == 200:
|
||||
report_check("favorite check after delete", body.get("favorited") is False, body)
|
||||
report_check(
|
||||
"favorite check after delete", body.get("favorited") is False, body
|
||||
)
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/favorites")
|
||||
report("GET /favorites (after delete)", code, body)
|
||||
report_check(
|
||||
@@ -1922,8 +2019,10 @@ def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
not bool(
|
||||
find_item(
|
||||
pick_items(body),
|
||||
lambda item: item.get("targetType") == "player"
|
||||
and same_id(item.get("targetId"), player_id),
|
||||
lambda item: (
|
||||
item.get("targetType") == "player"
|
||||
and same_id(item.get("targetId"), player_id)
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -2011,17 +2110,21 @@ def phase9_wallet(s: Session):
|
||||
bool(
|
||||
find_item(
|
||||
transactions,
|
||||
lambda item: item.get("type") == "topup"
|
||||
lambda item: (
|
||||
item.get("type") == "topup"
|
||||
and as_decimal(item.get("amount")) == Decimal("100")
|
||||
and item.get("description") == "topup via alipay",
|
||||
and item.get("description") == "topup via alipay"
|
||||
),
|
||||
)
|
||||
)
|
||||
and bool(
|
||||
find_item(
|
||||
transactions,
|
||||
lambda item: item.get("type") == "withdrawal"
|
||||
lambda item: (
|
||||
item.get("type") == "withdrawal"
|
||||
and as_decimal(item.get("amount")) == Decimal("10")
|
||||
and item.get("description") == "withdraw via alipay",
|
||||
and item.get("description") == "withdraw via alipay"
|
||||
),
|
||||
)
|
||||
),
|
||||
body,
|
||||
@@ -2060,7 +2163,11 @@ def phase10_community(s: Session, user_id):
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/posts")
|
||||
report("GET /posts", code, body)
|
||||
report_check("created post appears in list", bool(find_item_by_id(pick_items(body), post_id)), body)
|
||||
report_check(
|
||||
"created post appears in list",
|
||||
bool(find_item_by_id(pick_items(body), post_id)),
|
||||
body,
|
||||
)
|
||||
|
||||
if post_id:
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/posts/{post_id}")
|
||||
@@ -2181,7 +2288,11 @@ def phase10_community(s: Session, user_id):
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/users/{user_id}/posts")
|
||||
report(f"GET /users/{user_id}/posts", code, body)
|
||||
report_check("user posts contain created post", bool(find_item_by_id(pick_items(body), post_id)), body)
|
||||
report_check(
|
||||
"user posts contain created post",
|
||||
bool(find_item_by_id(pick_items(body), post_id)),
|
||||
body,
|
||||
)
|
||||
|
||||
return post_id
|
||||
|
||||
@@ -2241,7 +2352,9 @@ def phase12_email(s: Session):
|
||||
report("POST /email/verification-code/send (gateway)", code, body)
|
||||
report_check(
|
||||
"email verification send returns request id",
|
||||
code == 200 and bool(body.get("requestId")) and as_int(body.get("expireInSec")) > 0,
|
||||
code == 200
|
||||
and bool(body.get("requestId"))
|
||||
and as_int(body.get("expireInSec")) > 0,
|
||||
body,
|
||||
)
|
||||
|
||||
@@ -2253,7 +2366,9 @@ def phase12_email(s: Session):
|
||||
report("POST /auth/forgot-password/send (gateway)", code, body)
|
||||
report_check(
|
||||
"forgot password send returns request id",
|
||||
code == 200 and bool(body.get("requestId")) and as_int(body.get("expireInSec")) > 0,
|
||||
code == 200
|
||||
and bool(body.get("requestId"))
|
||||
and as_int(body.get("expireInSec")) > 0,
|
||||
body,
|
||||
)
|
||||
|
||||
|
||||
@@ -141,6 +141,98 @@ data:
|
||||
route:
|
||||
cluster: player_api_cluster
|
||||
timeout: 30s
|
||||
- match:
|
||||
prefix: /api/v1/services
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: player_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
- match:
|
||||
path: /api/v1/shops
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: shop_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/shops/[^/]+$"
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: shop_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/shops/[^/]+/players$"
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: shop_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/users/[^/]+/posts$"
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: community_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/users/[^/]+/shop$"
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: shop_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
- match:
|
||||
prefix: /api/v1/posts
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: "GET"
|
||||
route:
|
||||
cluster: community_api_cluster
|
||||
timeout: 30s
|
||||
typed_per_filter_config:
|
||||
envoy.filters.http.ext_authz:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
# - match:
|
||||
# prefix: /api/v1/shop
|
||||
# route:
|
||||
|
||||
Reference in New Issue
Block a user