fix(vision): file non immagine e grafo malformato non escono più come 500 (#15)

Worker: Image.open().convert() in try/except (422 "image is not
decodable"); il grafo deve essere un dict (422 altrimenti); run_graph
distingue costruzione (GraphBuildError -> 422) da esecuzione (500 con
motivo). Router: verifica dei magic bytes (PNG/JPEG/BMP/TIFF/WebP/GIF)
prima di chiamare il worker.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 22:37:34 +02:00
parent 92f130479a
commit 9afe1e712b
5 changed files with 153 additions and 7 deletions
+52
View File
@@ -95,3 +95,55 @@ async def test_a_malformed_graph_is_refused_not_crashed():
assert response.status_code == 422
assert "99" in response.json()["detail"]
# ---------------------------------------------------------------------------
# #15: a non-image upload, a graph that is not a dict, or a tool with no
# "kind" used to reach the worker as an unhandled exception - a naked 500,
# and then a 502 with no reason once the backend forwarded it.
# ---------------------------------------------------------------------------
@pytest.mark.asyncio
async def test_a_non_image_upload_is_refused_not_crashed():
"""A text file declared image/png: Image.open() must not escape as 500."""
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://worker") as client:
response = await client.post(
"/run",
files={"image": ("piece.png", b"i am not an image", "image/png")},
data={"graph": json.dumps(a_circle_graph())},
)
assert response.status_code == 422
assert "not decodable" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_a_graph_that_is_not_an_object_is_refused():
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://worker") as client:
response = await client.post(
"/run",
files={"image": ("piece.png", a_disc_png(), "image/png")},
data={"graph": "[]"},
)
assert response.status_code == 422
assert "object" in response.json()["detail"].lower()
@pytest.mark.asyncio
async def test_a_tool_without_a_kind_is_refused_not_crashed():
graph = {"schema_version": 1, "name": "p", "tools": [{"id": "a", "name": "x"}]}
transport = ASGITransport(app=app)
async with AsyncClient(transport=transport, base_url="http://worker") as client:
response = await client.post(
"/run",
files={"image": ("piece.png", a_disc_png(), "image/png")},
data={"graph": json.dumps(graph)},
)
assert response.status_code == 422
assert "malformed graph" in response.json()["detail"].lower()
assert "kind" in response.json()["detail"].lower()