pytest: fail CI when a configured image model id isn't in Sume's list
A pytest that reads your image model ids and checks each against GET /v1/images/models, so a retired id such as gpt-image-1 fails CI before it fails a customer.

Add one pytest that loads every model id your config file names and asserts each appears in the id list from GET /v1/images/models. A retired id fails CI the day you add it, and a catalog change that removes one fails the nightly run. It needs only an API key and takes one request.
This is a contract test for the id, not for output quality. It does not render anything, so it costs nothing.
The test
The catalog response is {"data": [{"id": ...}]}. The test accepts canonical ids and the legacy aliases Sume documents, so an old alias fails with a clear message to normalize it.
import json, os, urllib.request
import pytest
ALIAS_OK = {"gpt-image-2.5": "openai/gpt-image-2.5"}
def listed_ids():
req = urllib.request.Request(
"https://api.sume.com/v1/images/models",
headers={"Authorization": "Bearer " + os.environ["SUME_API_KEY"]})
with urllib.request.urlopen(req, timeout=30) as r:
return {row["id"] for row in json.load(r)["data"]}
def configured_ids():
with open("image-models.json") as f:
return json.load(f)["ids"]
@pytest.mark.parametrize("model_id", configured_ids())
def test_model_id_is_listed(model_id):
ids = listed_ids()
canonical = ALIAS_OK.get(model_id, model_id)
assert canonical in ids, f"{model_id} is not in GET /v1/images/models"What it catches
| Id | Result | Reason |
|---|---|---|
| openai/gpt-image-2.5-sunburst | passes | Listed by Sume |
| gpt-image-1 | fails | Retired by OpenAI on 2026-10-23, not listed by Sume |
| gemini-2.5-flash-image | fails | Retired by Google on 2026-10-02, not listed by Sume |
| google/nano-banana-2 | passes | Listed by Sume |
Run it where it counts
Run the test on every pull request and on a nightly schedule, because the catalog can change without a commit in your repo. For a request-level check of parameters on top of the id, see the preflight post. For the same idea on the Sora side, see the mocked-Sora contract test.
Sources
Related posts
More in Developers
- Python asyncio semaphore: submit transcription jobs with a cap
Run Sume STT submits through asyncio.Semaphore and asyncio.to_thread, honor retry-after on 429, and keep one Idempotency-Key per clip. Tested code.
- Python exceptions for Sume errors: retry on the class, not the status
Map the Sume error envelope code to two exception classes, Retryable and Fatal, so one except clause drives retries. Covers 409, 429, 402 and 503.
- Python pre-flight for a Short: catch Timeline schema errors offline
A Python check for a Sume Timeline body: even width and height, allowed fps, 1 to 1800 seconds, fade limits and start order. Run it before the plan call.
- Python: run one prompt on four AI video models and save the clips
A Python script that sends one prompt to Wan 3.0, Seedance 2.5, Kling 3 and MiniMax H3 on Sume, polls all four jobs and saves each MP4, with costs.
Written by Sume