Merge branch 'main-merge-base-a'
# Conflicts: # deploy/dev/docker-compose.yml # deploy/dev/envoy.yaml # desc/api/dispute.api # desc/api/review.api # desc/api/search.api
This commit is contained in:
+21
-16
@@ -29,7 +29,7 @@ docker compose down
|
||||
|
||||
构建脚本会扫描 `app/` 下所有 `api`、`rpc`、`mq`、`adapter` 入口,通过 `docker buildx bake` 并行构建所有服务镜像,生成 `juwan/<service>-<type>:dev`。
|
||||
|
||||
端到端接口测试走网关 `http://127.0.0.1:18080`,`18801-18809` 是各服务的直连端口,不经过认证链路。
|
||||
端到端接口测试走网关 `http://127.0.0.1:18080`,`18801-18814` 是各服务的直连端口,不经过认证链路。
|
||||
|
||||
如需只启动部分服务:
|
||||
|
||||
@@ -39,21 +39,26 @@ docker compose up -d postgres redis snowflake player-rpc player-api
|
||||
|
||||
## 端口映射
|
||||
|
||||
| 服务 | 宿主机端口 |
|
||||
| --------------- | ---------- |
|
||||
| PostgreSQL | 15432 |
|
||||
| Redis | 16379 |
|
||||
| Kafka | 19092 |
|
||||
| Envoy Gateway | 18080 |
|
||||
| users-api | 18801 |
|
||||
| player-api | 18802 |
|
||||
| game-api | 18803 |
|
||||
| shop-api | 18804 |
|
||||
| order-api | 18805 |
|
||||
| wallet-api | 18806 |
|
||||
| community-api | 18807 |
|
||||
| objectstory-api | 18808 |
|
||||
| email-api | 18809 |
|
||||
| 服务 | 宿主机端口 |
|
||||
| ---------------- | ---------- |
|
||||
| PostgreSQL | 15432 |
|
||||
| Redis | 16379 |
|
||||
| Kafka | 19092 |
|
||||
| Envoy Gateway | 18080 |
|
||||
| users-api | 18801 |
|
||||
| player-api | 18802 |
|
||||
| game-api | 18803 |
|
||||
| shop-api | 18804 |
|
||||
| order-api | 18805 |
|
||||
| wallet-api | 18806 |
|
||||
| community-api | 18807 |
|
||||
| objectstory-api | 18808 |
|
||||
| email-api | 18809 |
|
||||
| chat-api | 18810 |
|
||||
| review-api | 18811 |
|
||||
| dispute-api | 18812 |
|
||||
| notification-api | 18813 |
|
||||
| search-api | 18814 |
|
||||
|
||||
## 环境变量
|
||||
|
||||
|
||||
@@ -121,6 +121,14 @@ services:
|
||||
condition: service_started
|
||||
chat-api:
|
||||
condition: service_started
|
||||
review-api:
|
||||
condition: service_started
|
||||
dispute-api:
|
||||
condition: service_started
|
||||
notification-api:
|
||||
condition: service_started
|
||||
search-api:
|
||||
condition: service_started
|
||||
|
||||
ratelimit:
|
||||
image: envoyproxy/ratelimit:05c08d03
|
||||
@@ -268,6 +276,58 @@ services:
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
|
||||
review-rpc:
|
||||
image: juwan/review-rpc:dev
|
||||
container_name: juwan-review-rpc
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
snowflake:
|
||||
condition: service_started
|
||||
|
||||
dispute-rpc:
|
||||
image: juwan/dispute-rpc:dev
|
||||
container_name: juwan-dispute-rpc
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
snowflake:
|
||||
condition: service_started
|
||||
|
||||
notification-rpc:
|
||||
image: juwan/notification-rpc:dev
|
||||
container_name: juwan-notification-rpc
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
snowflake:
|
||||
condition: service_started
|
||||
|
||||
search-rpc:
|
||||
image: juwan/search-rpc:dev
|
||||
container_name: juwan-search-rpc
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
depends_on:
|
||||
postgres:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_healthy
|
||||
snowflake:
|
||||
condition: service_started
|
||||
|
||||
# ==================== API 层 ====================
|
||||
users-api:
|
||||
image: juwan/users-api:dev
|
||||
@@ -392,6 +452,54 @@ services:
|
||||
chat-rpc:
|
||||
condition: service_started
|
||||
|
||||
review-api:
|
||||
image: juwan/review-api:dev
|
||||
container_name: juwan-review-api
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
- "18811:8888"
|
||||
depends_on:
|
||||
review-rpc:
|
||||
condition: service_started
|
||||
order-rpc:
|
||||
condition: service_started
|
||||
|
||||
dispute-api:
|
||||
image: juwan/dispute-api:dev
|
||||
container_name: juwan-dispute-api
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
- "18812:8888"
|
||||
depends_on:
|
||||
dispute-rpc:
|
||||
condition: service_started
|
||||
order-rpc:
|
||||
condition: service_started
|
||||
|
||||
notification-api:
|
||||
image: juwan/notification-api:dev
|
||||
container_name: juwan-notification-api
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
- "18813:8888"
|
||||
depends_on:
|
||||
notification-rpc:
|
||||
condition: service_started
|
||||
|
||||
search-api:
|
||||
image: juwan/search-api:dev
|
||||
container_name: juwan-search-api
|
||||
restart: unless-stopped
|
||||
env_file: .env
|
||||
ports:
|
||||
- "18814:8888"
|
||||
depends_on:
|
||||
search-rpc:
|
||||
condition: service_started
|
||||
|
||||
# ==================== MQ ====================
|
||||
email-mq:
|
||||
image: juwan/email-mq:dev
|
||||
|
||||
@@ -224,6 +224,32 @@ static_resources:
|
||||
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
|
||||
disabled: true
|
||||
|
||||
- match:
|
||||
path: /api/v1/search
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
route:
|
||||
cluster: search_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/recommendations
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
route:
|
||||
cluster: search_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:
|
||||
@@ -249,6 +275,29 @@ static_resources:
|
||||
cluster: user_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/users/[0-9]+/favorites/check$"
|
||||
route:
|
||||
cluster: search_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/users/[0-9]+/reviews$"
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
route:
|
||||
cluster: review_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/users
|
||||
route:
|
||||
@@ -285,6 +334,22 @@ static_resources:
|
||||
cluster: shop_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/orders/[0-9]+/review.*"
|
||||
route:
|
||||
cluster: review_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/orders/[0-9]+/dispute$"
|
||||
route:
|
||||
cluster: dispute_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
prefix: /api/v1/orders
|
||||
route:
|
||||
@@ -327,6 +392,37 @@ static_resources:
|
||||
cluster: objectstory_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
prefix: /api/v1/reviews
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
route:
|
||||
cluster: review_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/disputes
|
||||
route:
|
||||
cluster: dispute_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
prefix: /api/v1/notifications
|
||||
route:
|
||||
cluster: notification_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
prefix: /api/v1/favorites
|
||||
route:
|
||||
cluster: search_api_cluster
|
||||
timeout: 30s
|
||||
|
||||
- match:
|
||||
prefix: /
|
||||
direct_response:
|
||||
@@ -571,6 +667,28 @@ static_resources:
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
- match:
|
||||
prefix: /api/v1/reviews
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
- match:
|
||||
safe_regex:
|
||||
google_re2: {}
|
||||
regex: "^/api/v1/users/[0-9]+/reviews$"
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
- match:
|
||||
path: /api/v1/search
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
- match:
|
||||
path: /api/v1/recommendations/home
|
||||
headers:
|
||||
- name: ":method"
|
||||
exact_match: GET
|
||||
- match:
|
||||
prefix: /api/v1
|
||||
requires:
|
||||
@@ -748,6 +866,62 @@ static_resources:
|
||||
address: chat-api
|
||||
port_value: 8888
|
||||
|
||||
- name: review_api_cluster
|
||||
connect_timeout: 2s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: review_api_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: review-api
|
||||
port_value: 8888
|
||||
|
||||
- name: dispute_api_cluster
|
||||
connect_timeout: 2s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: dispute_api_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: dispute-api
|
||||
port_value: 8888
|
||||
|
||||
- name: notification_api_cluster
|
||||
connect_timeout: 2s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: notification_api_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: notification-api
|
||||
port_value: 8888
|
||||
|
||||
- name: search_api_cluster
|
||||
connect_timeout: 2s
|
||||
type: STRICT_DNS
|
||||
lb_policy: ROUND_ROBIN
|
||||
load_assignment:
|
||||
cluster_name: search_api_cluster
|
||||
endpoints:
|
||||
- lb_endpoints:
|
||||
- endpoint:
|
||||
address:
|
||||
socket_address:
|
||||
address: search-api
|
||||
port_value: 8888
|
||||
|
||||
- name: authz_adapter_cluster
|
||||
connect_timeout: 0.5s
|
||||
type: STRICT_DNS
|
||||
|
||||
@@ -27,6 +27,7 @@ ordered=(
|
||||
dispute/disputes.sql
|
||||
dispute/dispute_timeline.sql
|
||||
review/reviews.sql
|
||||
notification/notifications.sql
|
||||
search/favorites.sql
|
||||
)
|
||||
|
||||
|
||||
+243
-1
@@ -926,6 +926,13 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
report(f"POST /orders/{order_id}/reorder", code, body)
|
||||
|
||||
if order2_id:
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order2_id}/pay",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order2_id}/pay (before cancel)", code, body)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order2_id}/cancel",
|
||||
json_body={},
|
||||
@@ -947,6 +954,237 @@ def phase8_order(s_consumer: Session, s_actor: Session, player_id, service_id, s
|
||||
return order_id
|
||||
|
||||
|
||||
def phase8b_review(s_consumer: Session, order_id, player_id):
|
||||
print("\n=== Phase 8b: Reviews ===")
|
||||
if not order_id:
|
||||
skip("Review flow", "No pending_review order id")
|
||||
return
|
||||
|
||||
csrf = s_consumer.csrf_headers()
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/review",
|
||||
json_body={"rating": 5, "content": "great service"},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order_id}/review", code, body)
|
||||
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders/{order_id}/reviews")
|
||||
report(f"GET /orders/{order_id}/reviews", code, body)
|
||||
if code == 200:
|
||||
report_check(
|
||||
f"GET /orders/{order_id}/reviews shape",
|
||||
isinstance(pick_items(body), list) and isinstance(body.get("meta"), dict),
|
||||
body,
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/reviews?limit=20")
|
||||
report("GET /reviews?limit=20", code, body)
|
||||
|
||||
if player_id:
|
||||
code, body, _ = s_consumer.get(
|
||||
f"{GATEWAY}/api/v1/users/{player_id}/reviews?limit=20",
|
||||
)
|
||||
report(f"GET /users/{player_id}/reviews?limit=20", code, body)
|
||||
|
||||
|
||||
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")
|
||||
return 0
|
||||
|
||||
csrf = s_consumer.csrf_headers()
|
||||
payload = {
|
||||
"playerId": player_id,
|
||||
"serviceId": service_id,
|
||||
"quantity": 1,
|
||||
"note": "test dispute order",
|
||||
}
|
||||
if shop_id:
|
||||
payload["shopId"] = shop_id
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders",
|
||||
json_body=payload,
|
||||
headers=csrf,
|
||||
)
|
||||
report("POST /orders (create for dispute)", code, body)
|
||||
order_obj = body.get("order", {}) if isinstance(body, dict) else {}
|
||||
order_id = order_obj.get("id", 0) if isinstance(order_obj, dict) else 0
|
||||
if not order_id:
|
||||
skip("Dispute flow", "No order id returned")
|
||||
return 0
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/pay",
|
||||
json_body={},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order_id}/pay (dispute flow)", 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 (dispute flow)", code, body)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/orders/{order_id}/dispute",
|
||||
json_body={
|
||||
"reason": "test dispute reason",
|
||||
"evidence": ["http://example.com/evidence.jpg"],
|
||||
},
|
||||
headers=csrf,
|
||||
)
|
||||
report(f"POST /orders/{order_id}/dispute", code, body)
|
||||
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/orders/{order_id}/dispute")
|
||||
report(f"GET /orders/{order_id}/dispute", code, body)
|
||||
dispute_id = as_int(body.get("id")) if isinstance(body, dict) else 0
|
||||
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/disputes")
|
||||
report("GET /disputes", code, body)
|
||||
|
||||
code, body, _ = s_consumer.get(f"{GATEWAY}/api/v1/disputes?status=open")
|
||||
report("GET /disputes?status=open", code, body)
|
||||
|
||||
if dispute_id:
|
||||
code, body, _ = s_actor.post(
|
||||
f"{GATEWAY}/api/v1/disputes/{dispute_id}/response",
|
||||
json_body={
|
||||
"reason": "test respondent guard",
|
||||
"evidence": ["http://example.com/response.jpg"],
|
||||
},
|
||||
headers=s_actor.csrf_headers(),
|
||||
)
|
||||
report(
|
||||
f"POST /disputes/{dispute_id}/response (expect participant check)",
|
||||
code,
|
||||
body,
|
||||
expect_status=(400, 403, 500),
|
||||
)
|
||||
|
||||
code, body, _ = s_consumer.post(
|
||||
f"{GATEWAY}/api/v1/disputes/{dispute_id}/appeal",
|
||||
json_body={"reason": "test appeal guard"},
|
||||
headers=csrf,
|
||||
)
|
||||
report(
|
||||
f"POST /disputes/{dispute_id}/appeal (expect status check)",
|
||||
code,
|
||||
body,
|
||||
expect_status=(400, 403, 500),
|
||||
)
|
||||
|
||||
return dispute_id
|
||||
|
||||
|
||||
def phase8d_notifications(s: Session):
|
||||
print("\n=== Phase 8d: Notifications ===")
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/notifications")
|
||||
report("GET /notifications", code, body)
|
||||
|
||||
items = pick_items(body) if code == 200 and isinstance(body, dict) else []
|
||||
if items:
|
||||
notification_id = as_int(items[0].get("id"))
|
||||
if notification_id:
|
||||
code, body, _ = s.put(
|
||||
f"{GATEWAY}/api/v1/notifications/{notification_id}/read",
|
||||
json_body={},
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report(f"PUT /notifications/{notification_id}/read", code, body)
|
||||
else:
|
||||
skip("PUT /notifications/:id/read", "No notification item returned")
|
||||
|
||||
code, body, _ = s.put(
|
||||
f"{GATEWAY}/api/v1/notifications/read-all",
|
||||
json_body={},
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report("PUT /notifications/read-all", code, body)
|
||||
|
||||
|
||||
def phase8e_search_and_favorites(s: Session, user_id, player_id, shop_id):
|
||||
print("\n=== Phase 8e: Search & Favorites ===")
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/search?q=LOL&limit=10")
|
||||
report("GET /search?q=LOL&limit=10", code, body)
|
||||
if code == 200:
|
||||
report_check(
|
||||
"GET /search response shape",
|
||||
isinstance(pick_items(body), list) and isinstance(body.get("meta"), dict),
|
||||
body,
|
||||
)
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/recommendations/home?limit=10")
|
||||
report("GET /recommendations/home?limit=10", code, body)
|
||||
if code == 200:
|
||||
report_check(
|
||||
"GET /recommendations/home response shape",
|
||||
isinstance(pick_items(body), list) and isinstance(body.get("meta"), dict),
|
||||
body,
|
||||
)
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/favorites")
|
||||
report("GET /favorites", code, body)
|
||||
|
||||
if not player_id or not user_id:
|
||||
skip("Favorites mutation flow", "Missing user or player id")
|
||||
return
|
||||
|
||||
code, body, _ = s.post(
|
||||
f"{GATEWAY}/api/v1/favorites",
|
||||
json_body={"targetType": "player", "targetId": str(player_id)},
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report("POST /favorites (player)", code, body)
|
||||
|
||||
code, body, _ = s.get(
|
||||
f"{GATEWAY}/api/v1/users/{user_id}/favorites/check"
|
||||
f"?targetType=player&targetId={player_id}",
|
||||
)
|
||||
report(f"GET /users/{user_id}/favorites/check (player)", code, body)
|
||||
if code == 200:
|
||||
report_check("favorite check after add", body.get("favorited") is True, body)
|
||||
|
||||
if shop_id:
|
||||
code, body, _ = s.post(
|
||||
f"{GATEWAY}/api/v1/favorites",
|
||||
json_body={"targetType": "shop", "targetId": str(shop_id)},
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report("POST /favorites (shop)", code, body)
|
||||
|
||||
code, body, _ = s.get(f"{GATEWAY}/api/v1/favorites")
|
||||
report("GET /favorites (after add)", code, body)
|
||||
favorite_id = 0
|
||||
for item in pick_items(body):
|
||||
if (
|
||||
item.get("targetType") == "player"
|
||||
and str(item.get("targetId")) == str(player_id)
|
||||
):
|
||||
favorite_id = as_int(item.get("id"))
|
||||
break
|
||||
report_check("locate favorite id", bool(favorite_id), {"id": favorite_id})
|
||||
|
||||
if favorite_id:
|
||||
code, body, _ = s.delete(
|
||||
f"{GATEWAY}/api/v1/favorites/{favorite_id}",
|
||||
headers=s.csrf_headers(),
|
||||
)
|
||||
report(f"DELETE /favorites/{favorite_id}", code, body)
|
||||
|
||||
code, body, _ = s.get(
|
||||
f"{GATEWAY}/api/v1/users/{user_id}/favorites/check"
|
||||
f"?targetType=player&targetId={player_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)
|
||||
|
||||
|
||||
def phase9_wallet(s: Session):
|
||||
print("\n=== Phase 9: Wallet ===")
|
||||
csrf = s.csrf_headers()
|
||||
@@ -1242,7 +1480,11 @@ def main():
|
||||
player_id, service_id = phase6_player(s_user, game_id)
|
||||
shop_id = phase7_shop(s_user, s_consumer, user_id, invited_player_id)
|
||||
|
||||
phase8_order(s_consumer, s_user, player_id, service_id, shop_id)
|
||||
order_id = phase8_order(s_consumer, s_user, player_id, service_id, shop_id)
|
||||
phase8b_review(s_consumer, order_id, player_id)
|
||||
phase8c_dispute(s_consumer, s_user, player_id, service_id, shop_id)
|
||||
phase8d_notifications(s_consumer)
|
||||
phase8e_search_and_favorites(s_consumer, consumer_user_id, player_id, shop_id)
|
||||
phase9_wallet(s_user)
|
||||
phase10_community(s_user, user_id)
|
||||
phase11_objectstory(s_user)
|
||||
|
||||
Reference in New Issue
Block a user