Per-Session Recording API Test Report¶
| Attribute | Value |
|---|---|
| Document type | Test report |
| Subject | Per-session recording API (POST https://record.pp-cdn.org/api/record/split, see recorder-api.md) |
| Tested | 2026-09-29 (Beijing time) |
| Method | Automated integration tests (against the production endpoint) + manual requests following the doc examples |
| Environment | Production https://record.pp-cdn.org; dedicated test app (appId redacted), simple auth mode |
| Result | Pass: the documented happy path, auth, field validation, status codes and silent no-op all match the implementation; the success path (round-start → round-end actually writing files) was skipped because no stream was online for that app's table during the window |
1. Goal¶
Verify, item by item against recorder-api.md, that the public interface behaves as documented:
- The endpoint and HTTP method are available and return the unified
{code,msg,data}envelope; - The
simplemode signing algorithm and time tolerance window; - Required-field and illegal-character validation (HTTP 400);
- Bad signature / expired
time(HTTP 401); - "End a session that never started" is a silent no-op success (HTTP 200);
- Error-code semantics with no online stream (HTTP 500 +
code=50001).
This report covers
simplemode only.advancemode (HMAC-SHA256 + nonce) and the 500 req/s rate limit (HTTP 429) require explicit deployment-side enablement, and deliberately triggering rate limiting on a shared production endpoint is inappropriate, so they are out of scope here.
2. Method¶
- Automated integration tests: run cases covering the scenarios above against the
production endpoint, asserting the HTTP status and envelope
code. - Manual request checks: build requests by hand using the doc's §7 signing formula
(
md5(appSecret + time + appId + tableId + [gameRound] + gameId)) and inspect the raw response body, so results don't rely on assertions alone.
Signing baseline (matching doc §4.1):
base = appSecret + time + appId + tableId
if gameRound: base += gameRound
base += gameId
token = md5(base).hexdigest()
3. Results summary¶
| Scenario | Expected | Actual | Result |
|---|---|---|---|
| Endpoint returns the documented envelope | 2xx / 4xx / 5xx as {code,msg,data} |
Documented envelope | PASS |
| Session start (with an online stream) | 200 | No online stream → 500 code=50001 |
SKIP |
| Session end (never started) | 200 silent no-op | 200 {"code":200,"msg":"OK","data":null} |
PASS |
Missing Authorization |
400 | 400 | PASS |
| Request body not valid JSON | 400 | 400 | PASS |
Missing tableId |
400 | 400 missing parameters. |
PASS |
tableId with illegal characters |
400 | 400 ... contain invalid characters. |
PASS |
| Bad signature (wrong appSecret) | 401 | 401 invalid token! |
PASS |
time outside the tolerance window |
401 | 401 token expired! |
PASS |
tableId with no online stream |
500 code=50001 |
500 code=50001 |
PASS |
| Conflicting owner's round-start | 500 | No online stream; precondition unmet | SKIP |
| Full round-start → round-end | 200 → 200 | No online stream; precondition unmet | SKIP |
Total: 10 executed, 8 PASS, 3 SKIP, 0 FAIL.
4. Key request / response captures¶
Below are actual responses to manual requests built per doc §7 (time is the Unix second
at request construction; Authorization is the 32-char md5 computed by the formula above):
A. End a session that never started (expect silent no-op success)
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. Session start but no stream online for the table (expect 500 + dedicated code)
HTTP 500 {"code":50001,"msg":"stream for app \"app_***\" table \"table\" is not online","data":""}
C. Bad signature (expect 401)
HTTP 401 {"code":401,"msg":"invalid token!","data":""}
D. Missing tableId (expect 400)
HTTP 400 {"code":400,"msg":"missing parameters.","data":""}
E. tableId with illegal characters table/../etc (expect 400)
HTTP 400 {"code":400,"msg":"appId/tableId/gameId/gameRound/appEnv contain invalid characters.","data":""}
F. time one hour ago (outside simple mode's ±30s window, expect 401)
HTTP 401 {"code":401,"msg":"token expired!","data":""}
Confirmed: the two key conventions in doc §5 hold — (1) conflict/business rejections and "no online stream" both use HTTP 500 and can only be told apart by the envelope
code(50001vs others) ormsg, not by the HTTP status alone; (2) ending a session that never started succeeds unconditionally, without erroring for a missing round-start.
5. Skipped cases¶
The following cases' preconditions did not hold during the test window, so they were skipped — this is not an interface defect:
- Session start / full round-start → round-end:
tableId=tablehad no view publishing at that moment, so round-start returnedcode=50001as documented. The success path can be re-run once the table actually starts publishing. - Conflicting owner's round-start: requires a prior successful round-start holding the table; precondition unmet.
All other scenarios not depending on an online stream were covered and passed.
6. Conclusion¶
- The endpoint, two-phase protocol,
simpleauth, field validation, status codes and no-op semantics described inrecorder-api.mdmatch the deployed service; this re-test passed in full. - Integrators should distinguish "no online stream" (
50001) from other business rejections using the envelopecode/msg(not the HTTP status), and mind the "don't end immediately after start" segment-duration constraint (doc §8).
7. Revision history¶
| Date | Revision | Notes |
|---|---|---|
| 2026-09-29 | Draft | Re-tested against production; success path skipped for lack of an online stream, everything else passed |