|
|
#!/usr/bin/env bash
|
|
|
# ============================================================
|
|
|
# 红包 HTTP 接口测试:create_order / created_callback
|
|
|
#
|
|
|
# 路由(与 internal/api/router.go 一致):
|
|
|
# POST ${HOST}/redpacket/create_order
|
|
|
# POST ${HOST}/redpacket/created_callback
|
|
|
#
|
|
|
# 鉴权:两接口均不在白名单,需在 Header 携带 token(见 protocol/constant constant.Token = "token")。
|
|
|
# 追踪:Header 需携带 operationID。
|
|
|
#
|
|
|
# 依赖:curl、jq;自动拉管理员 token 时另需 python3。
|
|
|
#
|
|
|
# 用法示例:
|
|
|
# chmod +x scripts/test/redpacket_api_test.sh
|
|
|
# GROUP_ID=你的群ID USER_ID=你的用户ID ./scripts/test/redpacket_api_test.sh
|
|
|
# ./scripts/test/redpacket_api_test.sh --host http://127.0.0.1:10002 --group-id xxx --try-callback
|
|
|
# TOKEN=已有用户token GROUP_ID=xxx ./scripts/test/redpacket_api_test.sh --skip-token-smoke
|
|
|
#
|
|
|
# 说明:
|
|
|
# - create_order 在 packetType=0(拼手气固定份)时要求 scopeType=GROUP 且当前用户在该群内。
|
|
|
# - 若 RPC 侧未配置 EVM chain client,created_callback 可走「离线」路径:传任意非空 txHash,
|
|
|
# 并在 body 中提供与订单一致的 packetID(见 internal/rpc/redpacket resolveCreatedPacket EVM 分支)。
|
|
|
# - 生产环境若已接链,created_callback 需真实上链交易哈希,此时请自行设置 TX_HASH / PACKET_ID。
|
|
|
# ============================================================
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
HOST="${HOST:-http://127.0.0.1:10002}"
|
|
|
USER_ID="${USER_ID:-5694418935}"
|
|
|
PLATFORM_ID="${PLATFORM_ID:-2}"
|
|
|
ADMIN_TOKEN="${ADMIN_TOKEN:-}"
|
|
|
OPENIM_SECRET="${OPENIM_SECRET:-openIM123}"
|
|
|
ADMIN_USER_ID="${ADMIN_USER_ID:-imAdmin}"
|
|
|
TOKEN="${TOKEN:-}"
|
|
|
|
|
|
GROUP_ID="${GROUP_ID:-}"
|
|
|
CHAIN_TYPE="${CHAIN_TYPE:-EVM}"
|
|
|
CHAIN_ID="${CHAIN_ID:-0}"
|
|
|
SCOPE_TYPE="${SCOPE_TYPE:-GROUP}"
|
|
|
PACKET_TYPE="${PACKET_TYPE:-0}"
|
|
|
CREATOR_WALLET="${CREATOR_WALLET:-0x0000000000000000000000000000000000000001}"
|
|
|
TOKEN_ADDR="${TOKEN_ADDR:-0x0000000000000000000000000000000000000000}"
|
|
|
TOTAL_AMOUNT="${TOTAL_AMOUNT:-100}"
|
|
|
TOTAL_SHARES="${TOTAL_SHARES:-5}"
|
|
|
EXPIRY_AT="${EXPIRY_AT:-0}"
|
|
|
REMARK="${REMARK:-api-test}"
|
|
|
|
|
|
TRY_CALLBACK="${TRY_CALLBACK:-0}"
|
|
|
TX_HASH="${TX_HASH:-0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}"
|
|
|
CALLBACK_PACKET_ID="${CALLBACK_PACKET_ID:-}"
|
|
|
|
|
|
SKIP_TOKEN_SMOKE="${SKIP_TOKEN_SMOKE:-0}"
|
|
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
|
case "$1" in
|
|
|
--host) HOST="$2"; shift 2 ;;
|
|
|
--user-id) USER_ID="$2"; shift 2 ;;
|
|
|
--platform-id) PLATFORM_ID="$2"; shift 2 ;;
|
|
|
--group-id) GROUP_ID="$2"; shift 2 ;;
|
|
|
--token) TOKEN="$2"; shift 2 ;;
|
|
|
--try-callback) TRY_CALLBACK="1"; shift ;;
|
|
|
--skip-token-smoke) SKIP_TOKEN_SMOKE="1"; shift ;;
|
|
|
*)
|
|
|
echo "未知参数: $1"
|
|
|
exit 1
|
|
|
;;
|
|
|
esac
|
|
|
done
|
|
|
|
|
|
need_cmd() {
|
|
|
command -v "$1" >/dev/null 2>&1 || {
|
|
|
echo "缺少依赖命令: $1"
|
|
|
exit 1
|
|
|
}
|
|
|
}
|
|
|
|
|
|
need_cmd curl
|
|
|
need_cmd jq
|
|
|
|
|
|
op_id() {
|
|
|
echo "redpacket-test-$$-$(date +%s%N)"
|
|
|
}
|
|
|
|
|
|
get_admin_token() {
|
|
|
local uid body resp token last_resp
|
|
|
local -a candidates=("${ADMIN_USER_ID}" "openIM123456" "imAdmin")
|
|
|
last_resp=""
|
|
|
|
|
|
for uid in "${candidates[@]}"; do
|
|
|
body="{\"secret\":\"${OPENIM_SECRET}\",\"userID\":\"${uid}\"}"
|
|
|
resp="$(curl -sS -X POST "${HOST}/auth/get_admin_token" \
|
|
|
-H "Content-Type: application/json" \
|
|
|
-H "operationID: $(op_id)" \
|
|
|
-d "$body")"
|
|
|
last_resp="$resp"
|
|
|
|
|
|
token="$(python3 - <<'PY' "$resp"
|
|
|
import json
|
|
|
import sys
|
|
|
|
|
|
raw = sys.argv[1]
|
|
|
try:
|
|
|
obj = json.loads(raw)
|
|
|
except Exception:
|
|
|
print("")
|
|
|
raise SystemExit(0)
|
|
|
|
|
|
token = ""
|
|
|
if isinstance(obj, dict):
|
|
|
data = obj.get("data")
|
|
|
if isinstance(data, dict):
|
|
|
token = data.get("token") or data.get("Token") or ""
|
|
|
if not token:
|
|
|
token = obj.get("token") or obj.get("Token") or ""
|
|
|
print(token)
|
|
|
PY
|
|
|
)"
|
|
|
if [[ -n "$token" ]]; then
|
|
|
echo "自动获取管理员 token 成功,userID=${uid}" >&2
|
|
|
printf '%s' "$token"
|
|
|
return 0
|
|
|
fi
|
|
|
done
|
|
|
|
|
|
echo "get_admin_token raw response: $last_resp" >&2
|
|
|
echo "自动获取管理员 token 失败,请检查 HOST/OPENIM_SECRET/ADMIN_USER_ID 或直接设置 ADMIN_TOKEN" >&2
|
|
|
exit 1
|
|
|
}
|
|
|
|
|
|
resolve_user_token() {
|
|
|
if [[ -n "${TOKEN}" ]]; then
|
|
|
echo "使用环境变量/参数 TOKEN(跳过 get_user_token)" >&2
|
|
|
return 0
|
|
|
fi
|
|
|
|
|
|
need_cmd python3
|
|
|
|
|
|
if [[ -z "${ADMIN_TOKEN}" ]]; then
|
|
|
echo "==> ADMIN_TOKEN 未设置,尝试自动获取管理员 token" >&2
|
|
|
ADMIN_TOKEN="$(get_admin_token)"
|
|
|
fi
|
|
|
|
|
|
echo "==> 获取用户 token(userID=${USER_ID})" >&2
|
|
|
local TOKEN_RESP
|
|
|
TOKEN_RESP=$(curl -sS -X POST \
|
|
|
-H "Content-Type: application/json" \
|
|
|
-H "operationID: $(op_id)" \
|
|
|
-H "token: ${ADMIN_TOKEN}" \
|
|
|
-d "{\"userID\":\"${USER_ID}\",\"platformID\":${PLATFORM_ID}}" \
|
|
|
"${HOST}/auth/get_user_token")
|
|
|
|
|
|
local ERR_CODE
|
|
|
ERR_CODE=$(echo "${TOKEN_RESP}" | jq -r '.errCode // "null"')
|
|
|
if [[ "${ERR_CODE}" != "0" ]]; then
|
|
|
echo "获取用户 token 失败: ${TOKEN_RESP}" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
TOKEN=$(echo "${TOKEN_RESP}" | jq -r '.data.token // empty')
|
|
|
if [[ -z "${TOKEN}" ]]; then
|
|
|
echo "token 为空: ${TOKEN_RESP}" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
echo "用户 token 获取成功" >&2
|
|
|
}
|
|
|
|
|
|
# ─── 用例:无 token 应被 GinParseToken 拒绝 ─────────────────
|
|
|
if [[ "${SKIP_TOKEN_SMOKE}" != "1" ]]; then
|
|
|
echo "==> 用例:POST /redpacket/create_order 无 token(应返回 errCode != 0)"
|
|
|
NO_TOKEN_RESP=$(curl -sS -X POST \
|
|
|
-H "Content-Type: application/json" \
|
|
|
-H "operationID: $(op_id)" \
|
|
|
-d '{"chainType":"EVM","chainID":1,"groupID":"x","scopeType":"GROUP","packetType":0,"token":"0x0000000000000000000000000000000000000000","totalAmount":"1","totalShares":1,"creatorWallet":"0x0000000000000000000000000000000000000001"}' \
|
|
|
"${HOST}/redpacket/create_order")
|
|
|
echo "${NO_TOKEN_RESP}" | jq .
|
|
|
NT_ERR=$(echo "${NO_TOKEN_RESP}" | jq -r '.errCode // "null"')
|
|
|
if [[ "${NT_ERR}" == "0" ]]; then
|
|
|
echo "预期无 token 时 errCode != 0,实际 errCode=0" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
echo "无 token 用例通过(errCode=${NT_ERR})"
|
|
|
else
|
|
|
echo "==> 跳过无 token 用例(SKIP_TOKEN_SMOKE=1 或 --skip-token-smoke)"
|
|
|
fi
|
|
|
|
|
|
if [[ -z "${GROUP_ID}" ]]; then
|
|
|
echo "错误:未设置 GROUP_ID。固定份红包(packetType=0)需要 scopeType=GROUP 且 group_id 非空。" >&2
|
|
|
echo "示例:GROUP_ID=你的群ID USER_ID=在群内的用户 ./scripts/test/redpacket_api_test.sh" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
resolve_user_token
|
|
|
|
|
|
echo "==> POST /redpacket/create_order"
|
|
|
CREATE_BODY=$(jq -n \
|
|
|
--arg chainType "${CHAIN_TYPE}" \
|
|
|
--argjson chainID "${CHAIN_ID}" \
|
|
|
--arg groupID "${GROUP_ID}" \
|
|
|
--arg scopeType "${SCOPE_TYPE}" \
|
|
|
--argjson packetType "${PACKET_TYPE}" \
|
|
|
--arg token "${TOKEN_ADDR}" \
|
|
|
--arg totalAmount "${TOTAL_AMOUNT}" \
|
|
|
--argjson totalShares "${TOTAL_SHARES}" \
|
|
|
--argjson expiryAt "${EXPIRY_AT}" \
|
|
|
--arg remark "${REMARK}" \
|
|
|
--arg creatorWallet "${CREATOR_WALLET}" \
|
|
|
'{
|
|
|
chainType: $chainType,
|
|
|
chainID: $chainID,
|
|
|
groupID: $groupID,
|
|
|
scopeType: $scopeType,
|
|
|
packetType: $packetType,
|
|
|
token: $token,
|
|
|
totalAmount: $totalAmount,
|
|
|
totalShares: $totalShares,
|
|
|
expiryAt: $expiryAt,
|
|
|
remark: $remark,
|
|
|
creatorWallet: $creatorWallet
|
|
|
}')
|
|
|
|
|
|
CREATE_RESP=$(curl -sS -X POST \
|
|
|
-H "Content-Type: application/json" \
|
|
|
-H "operationID: $(op_id)" \
|
|
|
-H "token: ${TOKEN}" \
|
|
|
-d "${CREATE_BODY}" \
|
|
|
"${HOST}/redpacket/create_order")
|
|
|
|
|
|
echo "${CREATE_RESP}" | jq .
|
|
|
|
|
|
CO_ERR=$(echo "${CREATE_RESP}" | jq -r '.errCode // "null"')
|
|
|
if [[ "${CO_ERR}" != "0" ]]; then
|
|
|
echo "create_order 失败(errCode=${CO_ERR})。请确认 USER_ID/TOKEN 对应用户在 GROUP_ID 群内,且 totalAmount 可被 totalShares 整除(固定份)。" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
BIZ_ID=$(echo "${CREATE_RESP}" | jq -r '.data.bizID // empty')
|
|
|
if [[ -z "${BIZ_ID}" ]]; then
|
|
|
echo "create_order 返回 errCode=0 但 data.bizID 为空: ${CREATE_RESP}" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
echo "create_order 成功,bizID=${BIZ_ID}"
|
|
|
|
|
|
if [[ "${TRY_CALLBACK}" != "1" ]]; then
|
|
|
echo "==> 未调用 created_callback(设置 TRY_CALLBACK=1 或传入 --try-callback 以继续)"
|
|
|
echo " 离线 EVM:可设置 CALLBACK_PACKET_ID(默认用时间戳十进制字符串);TX_HASH 可用环境变量 TX_HASH 覆盖。"
|
|
|
exit 0
|
|
|
fi
|
|
|
|
|
|
if [[ -z "${CALLBACK_PACKET_ID}" ]]; then
|
|
|
CALLBACK_PACKET_ID="$(date +%s)"
|
|
|
fi
|
|
|
|
|
|
echo "==> POST /redpacket/created_callback(bizID=${BIZ_ID}, packetID=${CALLBACK_PACKET_ID})"
|
|
|
CALLBACK_BODY=$(jq -n \
|
|
|
--arg bizID "${BIZ_ID}" \
|
|
|
--arg txHash "${TX_HASH}" \
|
|
|
--arg packetID "${CALLBACK_PACKET_ID}" \
|
|
|
--arg groupID "${GROUP_ID}" \
|
|
|
--arg scopeType "${SCOPE_TYPE}" \
|
|
|
'{
|
|
|
bizID: $bizID,
|
|
|
txHash: $txHash,
|
|
|
packetID: $packetID,
|
|
|
groupID: $groupID,
|
|
|
scopeType: $scopeType
|
|
|
}')
|
|
|
|
|
|
CALLBACK_RESP=$(curl -sS -X POST \
|
|
|
-H "Content-Type: application/json" \
|
|
|
-H "operationID: $(op_id)" \
|
|
|
-H "token: ${TOKEN}" \
|
|
|
-d "${CALLBACK_BODY}" \
|
|
|
"${HOST}/redpacket/created_callback")
|
|
|
|
|
|
echo "${CALLBACK_RESP}" | jq .
|
|
|
|
|
|
CB_ERR=$(echo "${CALLBACK_RESP}" | jq -r '.errCode // "null"')
|
|
|
if [[ "${CB_ERR}" != "0" ]]; then
|
|
|
echo "created_callback 失败(errCode=${CB_ERR})。若已配置链上客户端,请使用真实交易哈希或关闭 TRY_CALLBACK。" >&2
|
|
|
exit 1
|
|
|
fi
|
|
|
|
|
|
echo "created_callback 成功,红包状态应已更新为 ACTIVE(视部署与链配置而定)。"
|
|
|
echo "测试通过: /redpacket/create_order + /redpacket/created_callback"
|