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
- Voiceover longer than the video: speed up, trim or lengthen picture
A Sume TTS voiceover runs 35 seconds over a 30-second video. Compare speed up to 1.5x, a shorter script or a longer Timeline slot, with cost and a Python check.
- VS Code chat.mcp.autostart newAndOutdated: Sume's first run
VS Code can start MCP servers automatically when their config changes. Learn the autostart modes and the trust dialog before you add Sume's hosted server.
- waitForRun timeout: why SumeRunTimeoutError can arrive early
The Sume SDK's waitForRun checks its deadline before it sleeps, so SumeRunTimeoutError can fire up to one poll interval early. The run keeps going.
- waitForRun 429 and 503: the streak resets only on a clean read
Sume SDK waitForRun counts consecutive failed reads, resets only on a clean one, and retries the final result read so a late 429 cannot lose it.
Written by Sume