Files
opendance-windows/build/accept-integration.sh
T
zijunchiang a78a536476 feat(integration): 跨端集成验收(包内客户端 × 真实 Web 服务端)+ 修两个真实缺陷(v0.2.3)
单测打的是假 HTTP 夹具,Web 端测试只验自己那一侧 —— 两端各自绿灯不代表契约字段
真的对得上。本版加了跨端集成验收,并在首次运行就抓到两个真实缺陷。

一、跨端集成验收(T-WIN-019)
  * build/integration/probe-integration.gd:只加载**包内**代码
    (`godot --main-pack <pck> --script <探针>` + res://),打真实服务端并逐项断言:
    /health 六个契约版本、catalog/version 指纹、catalog/export 解析出的四类资源与
    defaultModelId;**场景物理参数逐项比对**(重力/基础风/阵风/风坐标系/地面摩擦坡度/
    碰撞体/出生点/相机/环境)+ Wind.sample(0) + 场景 GLB 可下载;
    动作 NDJSON → 客户端解析帧数 == 服务端帧数、33 点、每帧服务端关节角、
    推导帧率 30 fps、时长 96.67 s;缩略图 PNG;模型 VRM 可下载。
  * build/integration/seed-catalog.mjs:用**真实素材**播种目录
    (示例文件/ 下的真 VMD → 内部标准 33 点;真 PMX → VRM;场景 GLB + 物理参数),
    并把期望值(含从 /health 取的契约版本)写成 JSON。
  * build/accept-integration.sh:编排「服务端可达 → 播种 → 包内探针 → 汇报」。
  实测(v0.2.3 包内 pck × 真实 Web v0.7.4 + 真实 MySQL):全部通过。

二、探针抓到的两个真实缺陷
  1. `CatalogTypes.Motion` 缺 `thumbnail_url`:服务端 catalog/export 从
     data-models 0.6.0 起一直在发 thumbnailUrl(Model/Scene 都有),客户端默默丢掉。
     已补字段与解析(缺失为 ""),新增 tests/test_catalog_types.gd(4 项 20 断言)
     用**服务端真实报文形状**锁住三个资源类型的覆盖,并显式记录一处刻意不消费的差异
     (Model.usable/unusableReason —— 目录只下发可用模型,恒为 true)。
  2. `ApiClient._request()` 对**所有**响应无条件 `raw.get_string_from_utf8()`:
     数据本身没错(二进制路径返回原始字节),但每下载一个文件都会打印
     「Unicode parsing error … Invalid UTF-8 leading byte (N)」噪声,并把整个文件
     (模型可达数十 MB)解码成 String 拷贝后丢弃。改为只在需要文本时解码。

三、顺带修掉测试里的写死契约版本
  tests/test_api_integration.gd 断言 rest-api/websocket-keypoints/skeleton-topology
  均为 "0.1.0"。该用例只在服务端可达时才跑,平时一直 skip,所以契约升到 0.5.0 后
  没有任何地方发现它。改为从仓库 contracts/VERSION 读期望值并与 /health 比对
  (顺带成为「客户端契约快照 vs 服务端」的跨端校验)。

证据:verify.sh 全绿(820 通过 / 0 失败,比上版 +46,因为服务端在线时 API 集成组
真的跑了);包内 pck 跨端集成探针全部通过;check-version/PE 版本资源 = 0.2.3。
2026-09-17 05:18:03 +08:00

136 lines
5.6 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
# ============================================================
# OpenDance Studio Windows 客户端 —— **跨端集成验收**(T-WIN-019)
#
# Windows 发布包里的客户端(包内 pck) × 真实 Web 服务端
#
# 为什么需要它:本仓单测打的是**假 HTTP 夹具**,Web 端测试只验自己那一侧。
# 两端各自绿灯 ≠ 契约字段真的对得上(字段名/嵌套/null/单位/范围都可能一侧对一侧错)。
# 契约 0.5.0 新增的 `scenes[]` 与重力/风向参数尤其如此。
#
# 本脚本:
# 1. 要求一个**已部署好的** Web 服务端(不做部署,与项目边界一致);
# 2. 用真实素材播种一份目录(真 VMD → 内部标准;真 PMX → VRM;场景 GLB + 物理参数),
# 并写出期望值 JSON(`--skip-seed` 可跳过,复用已有目录);
# 3. 用 Godot 加载**发布包里的 pck**(`--main-pack`)跑
# `build/integration/probe-integration.gd`,逐项断言并给出退出码。
#
# 用法:
# GODOT_BIN=/path/to/godot API_BASE=http://127.0.0.1:3000 \
# bash build/accept-integration.sh dist-release/opendance-windows-v0.2.3.zip
#
# # 已播种过、只重跑探针:
# GODOT_BIN=... API_BASE=... bash build/accept-integration.sh <zip> --skip-seed
#
# 环境变量:
# API_BASE Web 服务端基地址(默认 http://127.0.0.1:3000)
# OPENDANCE_SAMPLES_DIR 真实素材目录(默认 仓库根/示例文件)
# EXPECT_PATH 期望值 JSON 输出路径(默认 $WORK_DIR/catalog-expect.json)
# WORK_DIR 解包目录(默认 mktemp -d)
# ============================================================
set -uo pipefail
PROJECT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
cd "$PROJECT_DIR"
TARGET="${1:-}"
SKIP_SEED=0
for arg in "$@"; do [ "$arg" = "--skip-seed" ] && SKIP_SEED=1; done
API_BASE="${API_BASE:-http://127.0.0.1:3000}"
SAMPLES_DIR="${OPENDANCE_SAMPLES_DIR:-$PROJECT_DIR/../示例文件}"
WORK_DIR="${WORK_DIR:-$(mktemp -d)}"
EXPECT_PATH="${EXPECT_PATH:-$WORK_DIR/catalog-expect.json}"
FAILED=0
log() { printf '\033[1;34m[accept-int]\033[0m %s\n' "$*"; }
fail() { printf '\033[1;31m[accept-int] ✗ %s\033[0m\n' "$*" >&2; FAILED=1; }
if [ -z "$TARGET" ] || [ ! -e "$TARGET" ]; then
echo "用法:GODOT_BIN=... API_BASE=... bash build/accept-integration.sh <发布包 zip 或已解压目录> [--skip-seed]" >&2
exit 2
fi
# 必须**先**建目录:期望值 JSON 默认就落在 $WORK_DIR 下,播种阶段就要写它
mkdir -p "$WORK_DIR"
# ---- 0. 服务端可达性 ----
log "服务端:$API_BASE"
HEALTH="$(curl -sf -m 10 "$API_BASE/health" || true)"
if [ -z "$HEALTH" ]; then
fail "GET $API_BASE/health 不可达 —— 请先部署 Web 服务端(本项目不部署它)"
exit 1
fi
echo "$HEALTH" | node -e '
let s = ""; process.stdin.on("data", (d) => (s += d)).on("end", () => {
const j = JSON.parse(s);
console.log(` ✓ 服务端版本 ${j.version}|数据库 ${j.database?.connected ? "已连接" : "未连接"}|scenes.available=${j.scenes?.available}`);
});'
# ---- 1. 播种目录 ----
if [ "$SKIP_SEED" = "1" ]; then
log "跳过播种(--skip-seed);期望值文件:$EXPECT_PATH"
else
log "播种真实目录(真 VMD / 真 PMX / 场景 GLB + 物理参数)…"
if [ ! -d "$SAMPLES_DIR" ]; then
fail "找不到素材目录 $SAMPLES_DIR(用 OPENDANCE_SAMPLES_DIR 指定)"
exit 1
fi
if ! command -v node >/dev/null 2>&1; then
fail "播种需要 node(Web 端已依赖 node,本机应已安装)"
exit 1
fi
API_BASE="$API_BASE" OPENDANCE_SAMPLES_DIR="$SAMPLES_DIR" EXPECT_PATH="$EXPECT_PATH" \
node "$PROJECT_DIR/build/integration/seed-catalog.mjs" || { fail "播种失败"; exit 1; }
fi
if [ ! -f "$EXPECT_PATH" ]; then
fail "缺少期望值文件 $EXPECT_PATH(首次运行不要加 --skip-seed)"
exit 1
fi
# ---- 2. 取包内 pck ----
if [ -f "$TARGET" ]; then
log "解压发布包…"
if ! unzip -qq -o "$TARGET" -d "$WORK_DIR" 2>/dev/null; then
command -v 7z >/dev/null 2>&1 && 7z x -y -o"$WORK_DIR" "$TARGET" >/dev/null || { fail "解压失败"; exit 1; }
fi
PACKAGE_DIR="$(find "$WORK_DIR" -maxdepth 1 -mindepth 1 -type d | head -1)"
else
PACKAGE_DIR="$TARGET"
fi
PCK="$PACKAGE_DIR/OpenDance.pck"
[ -f "$PCK" ] || { fail "找不到包内 pck:$PCK"; exit 1; }
log "包内 pck:$PCK($(stat -c%s "$PCK") 字节)"
# ---- 3. Godot ----
GODOT_BIN="${GODOT_BIN:-}"
if [ -z "$GODOT_BIN" ]; then
for candidate in godot godot4 Godot; do
command -v "$candidate" >/dev/null 2>&1 && { GODOT_BIN="$candidate"; break; }
done
fi
[ -n "$GODOT_BIN" ] || { fail "未找到 Godot(用 GODOT_BIN=<路径> 指定,需与本项目同版本 4.7.x)"; exit 1; }
# ---- 4. 跨端探针 ----
log "跨端集成探针(包内客户端 × $API_BASE)…"
PROBE_LOG="$WORK_DIR/probe-integration.log"
if timeout 600 "$GODOT_BIN" --headless --main-pack "$PCK" \
--script "$PROJECT_DIR/build/integration/probe-integration.gd" \
-- --api-base "$API_BASE" --expect "$EXPECT_PATH" >"$PROBE_LOG" 2>&1; then
PROBE_OK=1
else
PROBE_OK=0
fi
grep -vE "^Godot Engine|^$" "$PROBE_LOG" | sed 's/^/ /'
[ "$PROBE_OK" = 1 ] || fail "跨端集成探针未通过"
echo ""
if [ "$FAILED" -ne 0 ]; then
echo "============================================================"
echo " 跨端集成验收失败(工作目录:$WORK_DIR)"
echo "============================================================"
exit 1
fi
echo "============================================================"
echo " 跨端集成验收全部通过(工作目录:$WORK_DIR)"
echo "============================================================"