test(integration): 跨端验收补两类契约边界(不可用模型 / 场景 config 缺字段)

正常路径之外的**字段边界**同样只在跨端跑起来才暴露。配合 Web v0.7.5 增加:

1. 不可用模型:用 Assimp 把真实 PMX 转成 GLB 后入库(等价"管线只产出 GLB"),
   Web 侧断言 usable=false、原因准确(骨架达标时不得说"humanoid 不达标")、
   不在 catalog/export、defaultModelId 不变;Windows 侧(包内探针)断言
   bundle.models 里没有任何非 VRM 模型、按 id 找不到该模型、defaultModelId 仍指向可用 VRM。
2. 场景 config 只给 {physics:{gravity:{y:-9.8}}}:Web 侧断言服务端补齐默认
   (碰撞体 auto、地面 0.6/45°、静风、相机 follow、环境光 1、出生点 1 条);
   Windows 侧断言客户端解析出的**同一组默认值**逐项一致。

实测(Web v0.7.5 × Windows v0.2.5 包内 pck,真实 MySQL):
  ✓ usable=false 且原因为「未产出 VRM(格式为 GLB;humanoid 覆盖 19/22 已达标)…」
  ✓ 目录里的模型全是 VRM/不可用模型 #2 不在目录里/defaultModelId 仍为 1
  ✓ 最简配置:重力 -9.8 保留,碰撞体 auto、地面 0.6/45°、静风、相机 follow、
    环境光 1.0、出生点 1 条,两端逐项一致
跨端集成验收全部通过。

顺带修掉播种脚本自身的顺序错误(把依赖 catalog 的断言插到了 catalog 取回之前),
以及把断言改成「先取目录再断言」。不影响发布产物(build/ 已排除出 pck)。
This commit is contained in:
2026-09-18 03:05:51 +08:00
parent e0ba4ea833
commit cc8b6ba729
4 changed files with 173 additions and 4 deletions
+39
View File
@@ -245,6 +245,45 @@ func _run() -> void:
else:
_fail("动作缩略图下载失败:%s" % thumb["error"])
# ---------- 5b. ② 目录边界:不可用模型不得出现在客户端目录里(①)----------
var nonVrm: Array = []
for m in models:
if str(m.format) != "vrm":
nonVrm.append("%s(%s)" % [m.name, m.format])
_check(nonVrm.is_empty(),
"目录里的模型全是 VRM(不可用模型不进目录):%s" % ("OK" if nonVrm.is_empty() else str(nonVrm)))
if _expect.has("unusableModelId") and _expect["unusableModelId"] != null:
var unusable_id := int(_expect["unusableModelId"])
_check(bundle.find_model(unusable_id) == null,
"不可用模型 #%d 不在客户端目录里" % unusable_id)
_check(bundle.default_model_id == _expect["modelId"],
"defaultModelId 仍指向可用的 VRM 模型(实际 %d)" % bundle.default_model_id)
# ---------- 5c. ④ 场景 config 缺字段时的默认值(契约 0.5.0 边界)----------
if _expect.has("minimalSceneId") and _expect.has("minimalSceneExpect"):
var minimal: CatalogTypes.Scene = bundle.find_scene(int(_expect["minimalSceneId"]))
if minimal == null:
_fail("目录里找不到最简配置场景 #%d" % int(_expect["minimalSceneId"]))
else:
var want_min: Dictionary = _expect["minimalSceneExpect"]
var cfg2: CatalogTypes.SceneConfig = minimal.config
_check(_near(cfg2.physics.gravity.y, float(want_min["gravityY"])),
"最简配置:重力保留 %.1f" % cfg2.physics.gravity.y)
_check(cfg2.physics.colliders == str(want_min["colliders"]),
"最简配置:碰撞体补默认 %s" % cfg2.physics.colliders)
_check(_near(cfg2.physics.ground.friction, float(want_min["groundFriction"]))
and _near(cfg2.physics.ground.slope_limit, float(want_min["groundSlopeLimit"])),
"最简配置:地面默认 摩擦 %.2f / 坡度 %.0f°" % [
cfg2.physics.ground.friction, cfg2.physics.ground.slope_limit])
_check(cfg2.physics.wind.is_calm() == bool(want_min["windCalm"]),
"最简配置:风默认静风(is_calm=%s)" % str(cfg2.physics.wind.is_calm()))
_check(cfg2.camera.mode == str(want_min["cameraMode"]),
"最简配置:相机默认 %s" % cfg2.camera.mode)
_check(_near(cfg2.environment.ambient_intensity, float(want_min["ambientIntensity"])),
"最简配置:环境光默认 %.1f" % cfg2.environment.ambient_intensity)
_check(cfg2.spawn_points.size() <= 1,
"最简配置:出生点默认最多一条(实际 %d)" % cfg2.spawn_points.size())
# ---------- 6. ① 模型资源可下载 ----------
var model: CatalogTypes.Model = bundle.find_model(_expect["modelId"])
if model == null: