pytest and respx: fake a Sume job that is queued, then completed

Test your Sume poll loop with respx: a queued, a processing and a completed response in order, with sleep injected so the test finishes in milliseconds.

5 min readSume
All posts

To unit-test a Sume poll loop without the network, mock httpx with respx and give the route a list of responses: queued, then processing, then completed. Inject the sleep function so the test does not wait, and assert that the loop stops on terminal and honors next_poll_after_seconds.

The shapes come from Jobs and results: the status read carries terminal, sume_status (queued, processing, completed, failed, canceled) and next_poll_after_seconds, which is null once the job is terminal.

What does the test look like?

The function under test takes its client and its sleep, so both are easy to fake.

import httpx, respx

def poll(client, job_id, sleep):
    while True:
        d = client.get(f"/v1/jobs/{job_id}/status").json()["data"]
        if d["terminal"]:
            return d["sume_status"]
        sleep(max(d["next_poll_after_seconds"] or 2, 2))

def body(status, terminal, wait):
    return httpx.Response(200, json={"data": {"sume_status": status,
        "terminal": terminal, "next_poll_after_seconds": wait}})

@respx.mock
def test_poll_stops_on_terminal():
    route = respx.get("https://api.sume.com/v1/jobs/job_1/status")
    route.side_effect = [body("queued", False, 5),
                         body("processing", False, 3),
                         body("completed", True, None)]
    waits = []
    with httpx.Client(base_url="https://api.sume.com") as c:
        assert poll(c, "job_1", waits.append) == "completed"
    assert waits == [5, 3] and route.call_count == 3

Which cases deserve a test?

Poll-loop cases worth a test, derived from Sume docs read 2026-10-04
CaseFake responseAssert
Normal finishqueued, processing, completedStops on the third read, no extra call
Failed jobstatus failed, terminal trueReturns failed; no result read
Server asks for a longer gapnext_poll_after_seconds 30Sleep gets at least 30
Transient 429 on a read429 with retry-afterLoop backs off and continues, run not abandoned
Terminal with null waitnext_poll_after_seconds nullNo TypeError on max()

What do people forget?

  • next_poll_after_seconds is null for terminal jobs; the loop above only reads it for non-terminal ones, but a naive max(None, 2) raises.
  • Asserting on call count catches a loop that polls once too often, which in production is spend against your read budget.
  • A fake 429 on a poll is a read failure, not a job failure. Errors and rate limits says to back off using retry-after.

How do you test the deadline without waiting?

Pass the clock in as well as the sleep. A loop that takes now() and sleep() as parameters can be driven by a fake clock that jumps forward on every sleep, so a test for your poll deadline runs instantly and asserts the exact behavior you want: raise a timeout that carries the job id, and do not call cancel. Cancelling is a separate decision, because the cancel endpoint only succeeds before generation starts and answers 409 job_generation_already_started afterwards.

A second useful fake is a status read that returns failed with an error object. Assert that your code reads the failure from the job record rather than calling /result, which answers 409 job_not_completed for anything that is not completed.

Where does this leave production?

Pair the unit test with one real call against a development workspace. The mock proves your loop's logic; only a real job proves the prompt and the account.

Sources

Related posts

More in Developers

All Developers posts

Written by Sume