Unit-test a Sume submit-and-poll loop in Vitest with fake replies
Test a Sume polling loop without spending credits: inject the sleep, replay queued then completed replies, and assert next_poll_after_seconds is honored.

To test a Sume poll loop without paying for a generation, make the HTTP read and the sleep arguments of your function, then feed it canned status replies. Nothing in the loop needs a network: the documented status fields terminal, sume_status and next_poll_after_seconds are enough to drive it. Teams rebuilding video integrations after the Sora API shutdown on 2026-09-24 are rewriting exactly this loop, so it is worth pinning with a test.
What the test should pin
The Sume docs give the loop its contract: poll status_url, stop when terminal is true, sleep next_poll_after_seconds when present and back off otherwise, and read the result only for a completed job.
- A queued reply must not end the loop;
queuedis a normal accepted state. - A reply with
next_poll_after_secondsmust set the next sleep. - A failed or canceled terminal reply must come back to the caller, not throw from a result read, because
GET /v1/jobs/:id/resultanswers409 job_not_completedfor them.
The loop and the test
The sleep is injected, so the test finishes instantly and records every wait it was asked to make. If you use createSumeClient, its fetch option is the matching seam for faking the transport.
import { expect, test } from "vitest";
export async function pollJob(
get: (path: string) => Promise<any>,
id: string,
sleep = (s: number) => new Promise((r) => setTimeout(r, s * 1000)),
) {
for (;;) {
const s = await get(`/v1/jobs/${id}/status`);
if (s.terminal) return s;
await sleep(s.next_poll_after_seconds ?? 5);
}
}
test("polls to terminal and honors next_poll_after_seconds", async () => {
const replies = [
{ terminal: false, sume_status: "queued", next_poll_after_seconds: 7 },
{ terminal: true, sume_status: "completed" },
];
const waits: number[] = [];
const s = await pollJob(async () => replies.shift(), "job_1", async (n) => {
waits.push(n);
});
expect(s.sume_status).toBe("completed");
expect(waits).toEqual([7]);
});Cases worth adding
Add one test per terminal state and one for a deadline. Keep the deadline in your client: a client timeout does not cancel the job, it only stops you watching, so the test should assert that your code stores the job id before giving up.
| Case | Fake reply | Expected behavior |
|---|---|---|
| Queued then done | queued, then completed | Loop continues, then returns |
| Server-paced wait | next_poll_after_seconds set | Sleeps that many seconds |
| Failed job | terminal true, sume_status failed | Returns the record for error handling |
| Your deadline hit | never terminal | Throws your timeout, keeps the job id |
Sources
Related posts
More in Developers
- Test a webhook endpoint before go-live: a Sume CI gate (Python)
Use POST /v1/webhooks/test-deliveries to fire a signed webhook.test at your deployed URL and fail the deploy unless it answers 2xx. Python script included.
- What to measure for Sume API jobs: metrics, labels and alerts
A metrics plan for code that calls the Sume API: submit outcome, queue wait, time to terminal, error code and webhook gap, with low-cardinality labels.
- Which Sume API errors should page an engineer: route by category
Route Sume API failures by category: fix-the-input errors go to the caller, quota to finance, queue to a retry, and only internal or unexpected 5xx to on-call.
- Crash-safe Sume submit: write the intent row and key first
If your process dies after a Sume submit but before storing the job id, a pre-written intent row and Idempotency-Key let the retry return the original job.
Written by Sume