test(real-samples): 用真实素材端到端验证 ①模型→VRM 与 ②动作内部标准

`model-archive.spec.ts` / `mmd-vmd.spec.ts` 用的都是合成夹具与假 Assimp,
证明得了边界,证明不了「真素材能出片」。新增 opt-in 的
`test/real-samples.spec.ts`(6 项),样例或 Assimp 缺失时整体跳过,
CI 与无素材的开发机不受影响:

① 模型 → VRM(两个真实 PMX 压缩包)
   - 初音未来 1.0 MB → 1.10 MB VRM,humanoid 19/22,2 张贴图全内嵌;
   - 奥黛塔 28.1 MB → 26.08 MB VRM,humanoid 21/22,18 张贴图全内嵌,
     39 材质(36 项双面已补回);两者 drivable = true;
   - 断言 VRMC_vrm 1.0、所有 images 带 bufferView 且无 uri、无 MMD 专属
     材质扩展泄漏、蒙皮下标不越界,并**逐字节复算 POSITION 访问器的
     min/max**(glTF 唯一强制填写的边界,客户端据此做视锥剔除与 AABB;
     读访问器必须按 byteStride 走,否则交错缓冲里的 u16 会被读成乱码)。

② 动作内部标准 + 骨骼重绑定(真实 VMD,0.8 MB / 96.7 s)
   - maxFrame 2900 → 2901 帧、标准骨架 31/31 命中、双侧腿部 IK 链齐备;
   - MMD 骨骼名 → joint-map 候选名的重绑定结果逐一比对(15 个键);
   - 15 个直接关节的置信度恒为 1.0(绝不退化成 0.5 的推算值),
     其余 18 点为 0.5,缺失为 0;骨盆归零后投影 max|xy| = 0.85 ≤ 1;
   - NDJSON 往返一致、缩略图 PNG 有效、手腕摆幅非退化。

顺带修 `assimp-locate.spec.ts` 的非密闭性:其用例断言的是 PATH/常见系统
目录查找,但会继承外部 ASSIMP_BIN(assimp-lib.sh 中 ASSIMP_BIN 优先级高于
PATH),开发者按本项目文档设过 ASSIMP_BIN 时 npm test 会假失败。

规模 24 文件 / 289 项 → 25 文件 / 295 项。产物与已发布的 v0.7.4 无关
(发布包不含 web-server/test/),故不新发版本。
This commit is contained in:
2026-09-17 04:11:24 +08:00
parent d1c0dd6e7a
commit 2da02ee423
5 changed files with 533 additions and 9 deletions
+36 -8
View File
@@ -35,6 +35,23 @@ v0.7.4 修订(**场景功能的真正根因:`Scene` 没有注册进 `ENTITIES`**
4. `web-server/scripts/diagnose-scenes.mjs` 保持不变(默认只读),部署 v0.7.4 后运行它
可确认 `scenes` 表与列的实际情况。
v0.7.4 之后的测试补强(**不改产物,故不新发版本**):
1. 新增 `web-server/test/real-samples.spec.ts`(6 项,opt-in):用仓库根
`示例文件/` 下的**真实素材**跑完整管线,补上"合成夹具证明不了真素材能出片"的缺口 ——
* ① 模型→VRM:初音未来 19/22、奥黛塔 21/22 humanoid,`drivable = true`,
18 张贴图全内嵌、36 项双面材质补回,并逐字节校验 **POSITION 访问器的
`min`/`max` 等于真实包围盒**(glTF 规范唯一强制填写的边界,客户端据此剔除)、
蒙皮下标不越界;
* ② 动作内部标准 + 骨骼重绑定:真实 VMD 31 条轨道、标准骨架 31/31 命中、
2901 帧产出 33+2 点与 9 个关节角,15 个直接关节置信度恒为 `1.0`(不退化成推算值);
* 样例或 Assimp 缺失时整体跳过,CI 与无素材的开发机不受影响;
2. 修 `test/assimp-locate.spec.ts` 的非密闭性:该文件的用例断言的是
**PATH / 常见系统目录**的查找行为,但会继承外部 `ASSIMP_BIN`
(`assimp-lib.sh` 里 `ASSIMP_BIN` 优先级高于 PATH)。开发者按本项目文档
在 shell 里设过 `ASSIMP_BIN` 时 `npm test` 会假失败,现已在 `runLib()` 里剔除;
3. 规模 **25 文件 / 295 项**(三个真实样例齐备并装好 Assimp 时的实测数)。
v0.7.3 修订(**场景表不可用不再拖垮整站** —— 生产事故修复):
**另有一个更直接的缺陷同时修掉**:`ScenesService.create()` 当初按**内存存储**写,
判断的是 `file.buffer`,而上传中间件(`storage.module.ts`)用的是 multer 的
@@ -445,21 +462,32 @@ npm run dev # http://localhost:5173(/api 与 /health 代理到 3000)
```bash
cd web-server
npm run contracts:check # 契约版本一致性
npm run contracts:check # 契约版本一致性(6 项契约版本 + 35 条路由 + 三端版本号)
npm run typecheck # TypeScript 严格检查
npm run test # 245 项单元测试 / 19 个文件(VMD 31、模型压缩包 27、
# 模型检查器 25、缩略图 8、骨骼过滤 8、
# 预处理 25、init-env 16、admin-port 16、
# access-log 9、VRM 检查器 6、.env 预检 12、
# Assimp 定位与安装 13、stats 容量与编码 12、
# 缩略图贴图采样 6、MMD 材质对齐 9、模型可用性 5、MMD 骨名重绑定 4、动作预览 8、场景管理 6、场景页 2)
npm run test # 295 项单元测试 / 25 个文件。分布(VMD 31、模型检查器 29、
# 模型压缩包 27、预处理 25、admin-port 16、init-env 16、
# Assimp 定位与安装 13、应用版本 12、目录导出 12、.env 预检 12、
# 访问日志 9、MMD 材质对齐 9、stats 容量与编码 9、场景配置 9、
# 骨骼过滤 8、缩略图 8、动作预览 8、缩略图贴图采样 6、
# 真实素材回归 6、场景降级 6、VRM 检查器 6、模型可用性 5、
# 场景上传 5、实体注册 4、场景端到端(真实 sql.js 库) 4)
# opt-in: test/real-samples.spec.ts 缺样例/Assimp 时跳过 3 项
npm run build # 生产构建
npm run start:prod & # 启动
npm run test:smoke # 57 项端到端冒烟(需要数据库可连通)
npm run test:smoke # 81 项端到端冒烟(需要数据库可连通)
cd ../admin && npm run build # 管理端构建
```
真实素材回归(需要仓库根的 `示例文件/` 与外部 Assimp;样例缺失时自动跳过):
```bash
cd web-server
ASSIMP_BIN=/usr/bin/assimp npm test -- test/real-samples.spec.ts
# 样例放在别处: OPENDANCE_SAMPLES_DIR=/path/to/示例文件 ASSIMP_BIN=... npm test
# 覆盖: 两个真实 PMX→VRM(初音 19/22、奥黛塔 21/22 humanoid) + 真实 VMD→33 点内部标准
```
## 环境变量
| 变量 | 默认 | 说明 |