Files
tongxunruanjian/scripts/selftest.sh
T

193 lines
10 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# OpenIM + LiveKit 测试环境自测脚本
# 覆盖:管理员令牌、注册两个测试账号、签发用户令牌(登录)、
# 互发文字/语音/文件消息、消息回查、LiveKit 一对一房间双端进房与媒体发布订阅。
# 用法: ./scripts/selftest.sh [服务器内网IP]
set -uo pipefail
cd "$(dirname "$0")/.."
# ---------- 参数与公共函数 ----------
env_get() { grep -E "^$1=" .env | head -1 | cut -d= -f2- | tr -d '"'; }
IP="${1:-$(env_get SERVER_IP)}"
IP="${IP:-127.0.0.1}"
API="http://${IP}:10002"
SECRET="$(env_get OPENIM_SECRET)"; SECRET="${SECRET:-openIM123}"
MINIO_AK="$(env_get MINIO_ACCESS_KEY_ID)"; MINIO_AK="${MINIO_AK:-root}"
MINIO_SK="$(env_get MINIO_SECRET_ACCESS_KEY)"; MINIO_SK="${MINIO_SK:-openIM123}"
MINIO_PORT="$(env_get MINIO_PORT)"; MINIO_PORT="${MINIO_PORT:-10005}"
LK_KEY="$(env_get LIVEKIT_API_KEY)"
LK_SECRET="$(env_get LIVEKIT_API_SECRET)"
LK_PORT="$(env_get LIVEKIT_PORT)"; LK_PORT="${LK_PORT:-7880}"
LK_HTTP="http://${IP}:${LK_PORT}"
LK_WS="ws://${IP}:${LK_PORT}"
LK_CLI_IMAGE="livekit/livekit-cli:v2.18.2"
MC_IMAGE="minio/mc:latest"
FAILED=0
pass() { echo " [通过] $1"; }
fail() { echo " [失败] $1"; FAILED=1; }
note() { echo " [提示] $1"; }
# 优先用宿主机 curl;没有则在容器里跑 curl
# OpenIM 所有 API 都要求 operationID 请求头(链路追踪用,每次请求唯一即可)
new_opid() { echo "selftest-$(date +%s%N 2>/dev/null || date +%s)-$RANDOM"; }
if command -v curl >/dev/null 2>&1; then
http_post() { curl -s -m 15 -X POST "$1" -H 'Content-Type: application/json' -H "operationID: $(new_opid)" ${TOKEN:+-H "token: $TOKEN"} -d "$2"; }
else
note "宿主机无 curl,使用容器化 curl"
http_post() { docker run --rm --network host curlimages/curl:8.10.1 -s -m 15 -X POST "$1" -H 'Content-Type: application/json' -H "operationID: $(new_opid)" ${TOKEN:+-H "token: $TOKEN"} -d "$2"; }
fi
json_ok() { echo "$1" | tr -d ' \n' | grep -q '"errCode":0'; }
json_field() { echo "$1" | sed -n "s/.*\"$2\":\"\([^\"]*\)\".*/\1/p" | head -1; }
echo "==============================================="
echo " OpenIM 自测 (API: $API)"
echo "==============================================="
# ---------- 1. 管理员令牌 ----------
echo "1. 获取管理员令牌(/auth/get_admin_token"
TOKEN=""
resp=$(http_post "$API/auth/get_admin_token" "{\"secret\":\"${SECRET}\",\"userID\":\"imAdmin\"}")
if json_ok "$resp" && [ -n "$(json_field "$resp" token)" ]; then
TOKEN=$(json_field "$resp" token)
pass "管理员令牌获取成功"
else
fail "管理员令牌获取失败: $resp"
echo "OpenIM API 不可用,终止自测。"; exit 1
fi
# ---------- 2. 注册两个测试账号 ----------
echo "2. 注册测试账号 test001 / test002/user/user_register"
resp=$(http_post "$API/user/user_register" "{\"secret\":\"${SECRET}\",\"users\":[{\"userID\":\"test001\",\"nickname\":\"测试一号\"},{\"userID\":\"test002\",\"nickname\":\"测试二号\"}]}")
if json_ok "$resp"; then
pass "两个测试账号注册成功"
else
# 重复执行脚本时账号已存在,属于正常情况
note "注册接口返回非 0(通常是账号已存在,重复自测属正常): $(echo "$resp" | head -c 200)"
fi
# ---------- 3. 签发用户令牌(登录) ----------
echo "3. 签发用户令牌(/auth/get_user_tokenplatformID=5"
resp=$(http_post "$API/auth/get_user_token" "{\"secret\":\"${SECRET}\",\"platformID\":5,\"userID\":\"test001\"}")
T1=$(json_field "$resp" token)
resp=$(http_post "$API/auth/get_user_token" "{\"secret\":\"${SECRET}\",\"platformID\":5,\"userID\":\"test002\"}")
T2=$(json_field "$resp" token)
if [ -n "$T1" ] && [ -n "$T2" ]; then
pass "test001 / test002 均拿到用户令牌(登录链路可用)"
else
fail "用户令牌签发失败"
fi
# ---------- 4. 互发文字消息 ----------
MARK="selftest-$(date +%s)"
echo "4. test001 -> test002 发送文字消息(/msg/send_msg"
resp=$(http_post "$API/msg/send_msg" "{\"sendID\":\"test001\",\"recvID\":\"test002\",\"senderNickname\":\"测试一号\",\"senderPlatformID\":5,\"content\":{\"content\":\"自测文字 ${MARK}\"},\"contentType\":101,\"sessionType\":1}")
if json_ok "$resp"; then pass "文字消息发送成功"; else fail "文字消息发送失败: $(echo "$resp" | head -c 200)"; fi
echo " test002 -> test001 回复文字消息"
resp=$(http_post "$API/msg/send_msg" "{\"sendID\":\"test002\",\"recvID\":\"test001\",\"senderNickname\":\"测试二号\",\"senderPlatformID\":5,\"content\":{\"content\":\"收到 ${MARK}\"},\"contentType\":101,\"sessionType\":1}")
if json_ok "$resp"; then pass "回复消息发送成功"; else fail "回复消息发送失败: $(echo "$resp" | head -c 200)"; fi
# ---------- 5. 上传文件到 MinIO 并发送语音/文件消息 ----------
echo "5. 上传附件到 MinIO 并发送语音、文件消息"
mkdir -p .selftest
echo "openim selftest file ${MARK}" > .selftest/selftest-file.txt
head -c 4096 /dev/urandom 2>/dev/null > .selftest/selftest-voice.amr || printf 'fake-amr-%s' "$MARK" > .selftest/selftest-voice.amr
FILE_SIZE=$(wc -c < .selftest/selftest-file.txt | tr -d ' ')
VOICE_SIZE=$(wc -c < .selftest/selftest-voice.amr | tr -d ' ')
MCHOST="http://${MINIO_AK}:${MINIO_SK}@${IP}:${MINIO_PORT}"
mc_run() { docker run --rm --network host -v "$PWD/.selftest:/data" -e "MC_HOST_local=${MCHOST}" --entrypoint mc "$MC_IMAGE" "$@"; }
if mc_run cp "/data/selftest-file.txt" "local/openim/selftest/${MARK}/" >/dev/null 2>&1 \
&& mc_run cp "/data/selftest-voice.amr" "local/openim/selftest/${MARK}/" >/dev/null 2>&1; then
FILE_URL=$(mc_run share download --expire 168h "local/openim/selftest/${MARK}/selftest-file.txt" 2>/dev/null | grep -o 'http[^ ]*' | head -1)
VOICE_URL=$(mc_run share download --expire 168h "local/openim/selftest/${MARK}/selftest-voice.amr" 2>/dev/null | grep -o 'http[^ ]*' | head -1)
if [ -n "$FILE_URL" ] && [ -n "$VOICE_URL" ]; then
pass "附件已上传 MinIO 并生成访问链接"
resp=$(http_post "$API/msg/send_msg" "{\"sendID\":\"test001\",\"recvID\":\"test002\",\"senderNickname\":\"测试一号\",\"senderPlatformID\":5,\"content\":{\"uuid\":\"\",\"soundPath\":\"\",\"sourceUrl\":\"${VOICE_URL}\",\"dataSize\":${VOICE_SIZE},\"duration\":3},\"contentType\":103,\"sessionType\":1}")
if json_ok "$resp"; then pass "语音消息发送成功"; else fail "语音消息发送失败: $(echo "$resp" | head -c 200)"; fi
resp=$(http_post "$API/msg/send_msg" "{\"sendID\":\"test001\",\"recvID\":\"test002\",\"senderNickname\":\"测试一号\",\"senderPlatformID\":5,\"content\":{\"filePath\":\"\",\"uuid\":\"\",\"sourceUrl\":\"${FILE_URL}\",\"fileName\":\"selftest-file.txt\",\"fileSize\":${FILE_SIZE}},\"contentType\":105,\"sessionType\":1}")
if json_ok "$resp"; then pass "文件消息发送成功"; else fail "文件消息发送失败: $(echo "$resp" | head -c 200)"; fi
else
fail "MinIO 生成下载链接失败"
fi
else
fail "附件上传 MinIO 失败(检查 minio 容器与 ${IP}:${MINIO_PORT} 连通性)"
fi
# ---------- 6. 消息回查 ----------
echo "6. 消息落库回查(/msg/search_msg"
sleep 2
resp=$(http_post "$API/msg/search_msg" "{\"sendID\":\"test001\",\"recvID\":\"test002\",\"contentType\":0,\"sendTime\":\"\",\"sessionType\":1,\"pagination\":{\"pageNumber\":1,\"showNumber\":20}}")
if json_ok "$resp" && echo "$resp" | grep -q "$MARK"; then
pass "文字消息已落库可回查"
else
fail "消息回查未找到刚发送的内容: $(echo "$resp" | head -c 200)"
fi
if echo "$resp" | grep -q "selftest-voice.amr"; then pass "语音消息已落库可回查"; else fail "语音消息回查未找到"; fi
if echo "$resp" | grep -q "selftest-file.txt"; then pass "文件消息已落库可回查"; else fail "文件消息回查未找到"; fi
# ---------- 7. LiveKit 一对一语音房间 ----------
echo "==============================================="
echo " LiveKit 自测 (信令: $LK_WS)"
echo "==============================================="
ROOM="selftest-$(date +%s)"
echo "7. 两名用户进入同一房间,主叫发布音频,验证双向可见"
cleanup_lk() { docker rm -f lk-caller lk-callee >/dev/null 2>&1 || true; }
trap cleanup_lk EXIT
cleanup_lk
lk() { docker run --rm --network host "$LK_CLI_IMAGE" --url "$1" --api-key "$LK_KEY" --api-secret "$LK_SECRET" "${@:2}"; }
# 主叫发布仓库内置的 30 秒 opus 测试音频(--publish-demo 只有视频轨,这里用真实音频)
docker run -d --name lk-caller --network host -v "$PWD/scripts/assets:/data:ro" "$LK_CLI_IMAGE" \
--url "$LK_WS" --api-key "$LK_KEY" --api-secret "$LK_SECRET" \
room join --identity caller --publish /data/test-tone.ogg "$ROOM" >/dev/null 2>&1
# 被叫端必须显式开自动订阅(livekit-cli v2 的 --auto-subscribe 默认为 false
docker run -d --name lk-callee --network host "$LK_CLI_IMAGE" \
--url "$LK_WS" --api-key "$LK_KEY" --api-secret "$LK_SECRET" \
room join --identity callee --auto-subscribe "$ROOM" >/dev/null 2>&1
sleep 10
plist=$(lk "$LK_HTTP" room participants list "$ROOM" 2>&1)
if echo "$plist" | grep -q "caller" && echo "$plist" | grep -q "callee"; then
pass "双方已进入同一房间(房间: $ROOM"
else
fail "房间成员不符,list 输出: $(echo "$plist" | head -c 300)"
fi
# 主叫应已发布音频轨(kind=AUDIO)
if echo "$plist" | grep -qi "AUDIO"; then
pass "主叫音频轨道已发布"
else
note "未从成员列表确认音频轨道(可能输出格式差异),请人工核对: lk room participants list"
fi
# 被叫端日志应出现 track subscribed(媒体链路打通的直接证据)
if docker logs lk-callee 2>&1 | grep -qi "track subscribed"; then
pass "被叫端已订阅到主叫的音频轨道(一对一语音链路打通)"
else
fail "被叫端日志未见轨道订阅记录: $(docker logs lk-callee 2>&1 | tail -5)"
fi
cleanup_lk
trap - EXIT
# ---------- 汇总 ----------
echo "==============================================="
if [ "$FAILED" = "0" ]; then
echo " 自测全部通过 ✔"
echo " 客户端接入参数:"
echo " OpenIM API : $API"
echo " OpenIM WS : ws://${IP}:10001"
echo " LiveKit WS : $LK_WS (key: $LK_KEY)"
else
echo " 自测存在失败项,请根据上方 [失败] 排查(docker compose logs -f openim-server / livekit"
exit 1
fi