Test a Sume webhook receiver with node:test and signed fixtures
Three node:test cases that sign their own Sume webhook bodies: fresh, rotation header, and the three rejections. Runs with node --test.

You can test a Sume webhook receiver without Sume sending anything: sign a body yourself with HMAC-SHA256 over timestamp.rawbody, prefix it with sume-v1=, and assert on the verifier. The 27-line file below uses only node:test, node:assert and node:crypto, covers the happy path, the rotation header and three rejections, and runs with node --test.
Write these before you rotate a signing secret. Rotation sends two comma-separated signatures for 24 hours, and a verifier that was only ever tested with one signature is the usual way it breaks; the bug in verifyWebhook returns false during a Sume secret rotation is exactly that case.
What each case proves
| Case | Expected | Rule it checks |
|---|---|---|
| Fresh signature, one entry | true | Basic HMAC over timestamp.rawbody |
| New and current signature, comma-separated | true | Accept any sume-v1 entry |
| Body changed by one space | false | Raw bytes are signed |
| Timestamp 600 seconds old | false | Five-minute replay window |
| Empty secret | false | Never verify against nothing |
The test file
It imports the verifier from verify.mjs, a file holding the verifySume function from the rotation post linked above and signs fixtures with createHmac. Swap the import for your own function if you wrote one.
import { test } from "node:test";
import assert from "node:assert/strict";
import { createHmac } from "node:crypto";
import { verifySume } from "./verify.mjs";
const secret = "whsec_test";
const body = JSON.stringify({ event: "job.completed", job_id: "job_1" });
const now = () => Math.floor(Date.now() / 1000);
const sign = (s, ts, b = body) => "sume-v1=" + createHmac("sha256", s).update(`${ts}.${b}`).digest("hex");
const headers = (sig, ts) => new Headers({
"x-sume-webhook-timestamp": String(ts), "x-sume-webhook-signature": sig });
const check = (h, over = {}) => verifySume({ body, headers: h, secret, ...over });
test("accepts a fresh signature", async () => {
const ts = now();
assert.equal(await check(headers(sign(secret, ts), ts)), true);
});
test("accepts either entry during a rotation", async () => {
const ts = now();
assert.equal(await check(headers(`${sign("new", ts)},${sign(secret, ts)}`, ts)), true);
});
test("rejects a tampered body, stale timestamp and empty secret", async () => {
const ts = now();
assert.equal(await check(headers(sign(secret, ts, body + " "), ts)), false);
assert.equal(await check(headers(sign(secret, ts - 600), ts - 600)), false);
assert.equal(await check(headers(sign("", ts), ts), { secret: "" }), false);
});Running it
Add one more case per behavior you rely on: a missing signature header, a non-numeric timestamp, and a header whose entries are all wrong. Each is a one-line variation on the helpers above, and each is a way a hand-rolled verifier throws instead of returning false. A thrown exception in a webhook route becomes a 500, and a 500 makes Sume retry the delivery up to 10 times.
node --test hook.test.mjson Node 20 or newer.
Limits
These tests share the code that signs and verifies, so they prove the verifier matches the written scheme, not that Sume signs what the docs say. One real delivery or a dashboard Send test, which posts a signed webhook.test payload to a URL you type, closes that gap. Test the route as well: the usual production failure is a framework that parsed the body before your handler saw it, which a unit test of the verifier cannot catch.
Sources
Related posts
More in Developers
- TikTok API posting: your app must not add a watermark or promo text
TikTok's guidelines say apps must not add a brand name, logo, watermark, link or promo text to posted content. What that means for an AI video pipeline.
- TikTok Content Posting API rate limits: 20, 6 and 30 per minute
TikTok limits creator info to 20 requests per minute, post init to 6, and status checks to 30, each per access token. Design your queue around the tightest one.
- TikTok Content Posting API changelog 2026: no posting changes listed
TikTok's developer changelog lists no Content Posting API entry in 2026; the latest are Data Portability and Research Tools. What to still re-check.
- TikTok upload chunk rules: 5 MB to 64 MB, up to 1,000 chunks
TikTok FILE_UPLOAD chunks must be 5 to 64 MB, the last up to 128 MB, 1,000 chunks max. A short planner computes the Content-Range for each PUT.
Written by Sume