test(ws): WebSocket 契约一致性验收(独立实现 × 包内 PoseServer)+ 故意不合规的负例服务端
tests/test_pose_server.gd 用的是**同引擎内的客户端**(Godot WebSocketPeer),而真实
对端是 Android(Kotlin/OkHttp)——同引擎自测会共享同样的假设(超时判定、分包、
文本/二进制边界、关闭语义、子协议协商),于是「测试全绿」不等于「Android 连得上」。
契约 §7 也写明:静默超时要用保活避免,而不是靠重连掩盖;这句话只有用独立实现的
客户端实测才算验过。
新增三个文件:
* build/integration/probe-ws.gd —— 把**包内** PoseServer 跑起来(--main-pack +
只加载包内代码),把连接/控制帧/帧/断开事件汇总成 JSON 报告;自身不做判定
(避免「用被测实现判定被测实现」)。支持 --stop-file 提前收尾。
* build/integration/ws-client.mjs —— Node 实现的契约一致性客户端(用内置
WebSocket,零依赖),逐条跑 §1 §1.1 §2 §3.1-§3.6 §5 §6 §7 的场景。
* build/accept-ws.sh —— 编排:解包 → 起探针 → 等监听 → 跑客户端 → 收报告 →
核对客户端看不到的那一半(frames_dropped / malformed_frames / 控制帧计数 /
device_replaced / 关闭原因 / 静默超时消息带毫秒数与阈值)。
实测(**已发布**的 v0.2.3 资产重新下载后跑):客户端 30 通过 / 0 失败 + 服务端侧
16 项事实全过。关键两条:
* 保活对照:只发应用层 ping 持续 19 秒(> 静默超时 15 秒)连接**仍保持打开**;
* 静默超时:完全不发数据 → close(1001,"timeout"),实测 15011 ms(阈值 15000 ms);
另有:子协议协商、welcome 声明保活参数且 heartbeat(5000) < silentTimeout(15000)/2、
pong 原样回传 sentAtMs、同 deviceId 替换旧连接(1000 "replaced by new connection")、
bye → 1000、协议不匹配 → bye + 1002、72 帧含故意跳号(服务端 frames_dropped=3)
与故意 100 字节非法帧(malformed_frames=1)。
反向验证:新增**故意不合规的服务端** build/integration/probe-ws-bad.gd
(welcome 不声明保活字段、不带 sessionId、收到 ping 不回 pong、不做 deviceId 去重),
对同一客户端 → 14 项 ✗、退出码 1,证明断言不是空转。
踩坑记录(已写入任务文档):
1. --script 模式下从无类型数组取 peer,peer.get_packet() 返回类型推不出来,
var x := … 直接 Parse Error;
2. 复核静默超时消息时写了否定式断言 !includes("静默超时 1"),把正确行为判成失败 ——
断言应写成正向模式匹配;
3. 探针原本只能按固定时长跑,编排脚本得猜时间;改成客户端跑完 touch stop 文件。
顺带:package-release.sh 结尾把三条发布前验收(产物自证 / WS 契约一致性 / 跨端集成)
一次列全。不影响已发布的 v0.2.3 产物,故不新发版本。
This commit is contained in:
@@ -123,6 +123,31 @@ exe 的 PE 版本资源 → **用 Godot 直接加载包内 pck 跑 180 帧** →
|
||||
已对**已发布的 v0.2.2 资产**(重新下载)跑通,并用「篡改包内 contracts/VERSION」与
|
||||
「期望版本写错」两个负例反向验证过。
|
||||
|
||||
### WebSocket 契约一致性验收(T-WIN-020)
|
||||
|
||||
`tests/test_pose_server.gd` 用的是**同引擎内的客户端**,而真实对端是
|
||||
**Android(Kotlin/OkHttp)** —— 同引擎自测会共享同样的假设(超时判定、分包、
|
||||
文本/二进制边界、关闭语义、子协议协商)。因此另配一套**独立实现**的客户端:
|
||||
|
||||
```bash
|
||||
GODOT_BIN=/path/to/godot bash build/accept-ws.sh dist-release/opendance-windows-v0.2.3.zip
|
||||
# 跳过耗时的两个场景(保活 19s / 静默超时 15s):
|
||||
GODOT_BIN=... bash build/accept-ws.sh <zip> --skip-slow
|
||||
```
|
||||
|
||||
* `build/integration/probe-ws.gd`:把**包内** PoseServer 跑起来并汇总事件成 JSON(自身不判定);
|
||||
* `build/integration/ws-client.mjs`:Node 实现的契约一致性客户端(无依赖,用内置 `WebSocket`),
|
||||
逐条跑 §1 §1.1 §2 §3.1-§3.6 §5 §6 §7 的场景;
|
||||
* `build/accept-ws.sh`:编排两者,并核对**客户端看不到的那一半**(服务端侧
|
||||
`frames_dropped` / `malformed_frames` / 控制帧计数 / 去重事件 / 关闭原因)。
|
||||
|
||||
实测(已发布的 v0.2.3 资产):客户端 **30 通过 / 0 失败** + 服务端侧 16 项事实全过,
|
||||
其中最关键的是**保活对照**——只发应用层 `ping` 持续 19 秒(> 静默超时 15 秒)连接
|
||||
**仍保持**,而完全不发数据的连接在 **15011 ms**(阈值 15000 ms)被 `close(1001, "timeout")`。
|
||||
反向验证用一个**故意不合规的服务端**(`build/integration/probe-ws-bad.gd`:welcome 不声明
|
||||
保活字段、不回 pong、不做去重)跑同一客户端 → 14 项 ✗、退出码 1,证明断言不是空转。
|
||||
详见 [`tasks/020-ws-contract-conformance.md`](tasks/020-ws-contract-conformance.md)。
|
||||
|
||||
当前版本:**v0.2.3**(`project.godot` 的 `application/config/version`,
|
||||
与 `project.yaml`、`export_presets.cfg`、导出产物名、Git tag、「关于」清单展示、
|
||||
以及 exe 的 PE 版本资源全部一致)。
|
||||
|
||||
Reference in New Issue
Block a user