跳转至

场次录像 API 测试报告

属性 值
文档类型 测试报告
测试对象 场次录像 API(POST https://record.pp-cdn.org/api/record/split,见 recorder-api.md)
测试时间 2026-09-29(北京时间)
测试方式 自动化集成测试(对生产端点)+ 按文档示例的手工请求校验
测试环境 生产:https://record.pp-cdn.org;专用测试 App(appId 已脱敏),simple 鉴权模式
结论 通过:文档描述的正确路径、鉴权、字段校验、状态码与静默 no-op 行为均与实现一致;因测试时段该 App 名下 table 无在线推流,成功路径(round-start → round-end 实际落盘)被跳过

1. 测试目标

按 recorder-api.md 逐条验证对外接口的行为是否符合文档约定:

  1. 服务地址与 HTTP 方法可用,返回统一的 {code,msg,data} 信封;
  2. simple 模式签名算法与时间容忍窗口;
  3. 必填字段与非法字符的校验(HTTP 400);
  4. 签名错误 / time 过期(HTTP 401);
  5. 「结束一个从未开始过的场次」为静默 no-op 成功(HTTP 200);
  6. 无在线流时的错误码语义(HTTP 500 + code=50001)。

本报告只覆盖 simple 模式。advance 模式(HMAC-SHA256 + nonce)与每秒 500 次的限流(HTTP 429) 按文档说明需在部署侧显式开启、且对共享生产端点主动触发限流并不合适,故本次不覆盖。

2. 测试方法

  • 自动化集成测试:对生产端点运行覆盖上述场景的测试用例,逐条断言 HTTP 状态码与信封 code。
  • 手工请求校验:按文档 §7 的签名公式(md5(appSecret + time + appId + tableId + [gameRound] + gameId)) 手工构造请求,核对实际返回正文,避免只依赖断言而看不到原始响应。

签名基线(与文档 §4.1 一致):

base = appSecret + time + appId + tableId
if gameRound:  base += gameRound
base += gameId
token = md5(base).hexdigest()

3. 执行结果汇总

场景 期望 实测 结论
服务端点返回文档信封 2xx / 4xx / 5xx 且为 {code,msg,data} 返回文档信封 PASS
场次开始(有在线流) 200 无在线流 → 500 code=50001 SKIP
场次结束(从未开始) 200 静默 no-op 200 {"code":200,"msg":"OK","data":null} PASS
缺少 Authorization 400 400 PASS
请求体非法 JSON 400 400 PASS
缺少 tableId 400 400 missing parameters. PASS
tableId 含非法字符 400 400 ... contain invalid characters. PASS
签名错误(错误 appSecret) 401 401 invalid token! PASS
time 超出容忍窗口 401 401 token expired! PASS
无在线流的 tableId 500 code=50001 500 code=50001 PASS
冲突 owner 的 round-start 500 无在线流,前置条件不成立 SKIP
完整 round-start → round-end 200 → 200 无在线流,前置条件不成立 SKIP

合计:10 项执行,8 PASS,3 SKIP,0 FAIL。

4. 关键请求 / 响应实录

以下为按文档 §7 手工构造的请求的实际响应(time 为请求构造时刻的 unix 秒,Authorization 为 按上式算出的 32 位 md5):

A. 结束一个从未开始过的场次(预期静默 no-op 成功)

POST /api/record/split
Authorization: <md5>
{"time":"1759130000","appId":"app_***","tableId":"table","gameId":"itest-never-xxx","gameRound":"gc-itest-xxx"}
HTTP 200 {"code":200,"msg":"OK","data":null}

B. 场次开始但该 table 当前无在线流(预期 500 + 专用 code)

HTTP 500 {"code":50001,"msg":"stream for app \"app_***\" table \"table\" is not online","data":""}

C. 签名错误(预期 401)

HTTP 401 {"code":401,"msg":"invalid token!","data":""}

D. 缺少 tableId(预期 400)

HTTP 400 {"code":400,"msg":"missing parameters.","data":""}

E. tableId 含非法字符 table/../etc(预期 400)

HTTP 400 {"code":400,"msg":"appId/tableId/gameId/gameRound/appEnv contain invalid characters.","data":""}

F. time 取 1 小时前(超出 simple 模式 ±30s 窗口,预期 401)

HTTP 401 {"code":401,"msg":"token expired!","data":""}

实测确认:文档 §5 的两点关键约定成立——(1) 冲突/业务性拒绝与「无在线流」都使用 HTTP 500, 二者只能靠信封 code(50001 vs 其它)或 msg 区分,不能只看 HTTP 状态码; (2) 结束一个从未开始过的场次是无条件成功,不会因为没有匹配的 round-start 而报错。

5. 未通过 / 跳过说明

以下用例的前置条件在测试时段不成立,因此跳过,不代表接口缺陷:

  • 场次开始 / 完整 round-start → round-end:tableId=table 在当前时刻没有任何 view 在线推流, round-start 按文档预期返回 code=50001。待该 table 实际开始推流后即可重跑成功路径。
  • 冲突 owner 的 round-start:需要先有一个成功的 round-start 持有 table,前置条件不成立。

其余所有不依赖在线流的场景均已覆盖并通过。

6. 结论

  • recorder-api.md 描述的服务地址、两阶段协议、simple 鉴权、字段校验、状态码与 no-op 语义 与实际部署一致,本次重测全部通过。
  • 接入方在实现时请务必以信封 code / msg(而非 HTTP 状态码)区分「无在线流」 (50001)与其它业务性拒绝,并注意「开始后不要立即结束」的分段时长限制(文档 §8)。

7. 修订记录

日期 修订 说明
2026-09-29 初稿 对生产端点重测;成功路径因无在线流跳过,其余全部通过