mirror of
https://github.com/deepseek-ai/deepseek-recipe.git
synced 2026-10-28 17:17:08 +08:00
test(server-py): pin the retryable image-failure status mapping
The example server derived the HTTP status of an `ImageError` from `kind` alone, so an upstream image host answering 503 was reported to the API client as 400 `invalid_request_error` - the client was told its request was malformed, and should not be retried. `ImageError.is_retryable` is the library's own classification of exactly that: `Fetch` is retryable regardless of its source, and `FetchStatus` is retryable at 5xx. That is what the previous commit keys the status off. This adds the coverage the fix left out. A local stub origin answers 503 and 404, and the test pins the status and error type each one maps to. The 503 case fails against the previous revision, so the assertion is load-bearing rather than incidental. Uses only the standard library (http.server, threading), so the suite needs no new dependency.
This commit is contained in:
parent
e5be1db627
commit
d58d86d940
@ -1,6 +1,9 @@
|
||||
"""Smoke tests for the FastAPI example and its inference callback."""
|
||||
|
||||
import importlib.util
|
||||
import threading
|
||||
from contextlib import contextmanager
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@ -121,3 +124,66 @@ def test_web_search_conversion_options():
|
||||
assert accepted.status_code == 200, accepted.text
|
||||
assert response_text("/v1/messages", accepted.json()) == "Hello world!"
|
||||
assert rejected.status_code == 400
|
||||
|
||||
|
||||
class _StubImageHost(BaseHTTPRequestHandler):
|
||||
"""A local origin that answers every GET with a fixed status code."""
|
||||
|
||||
status = 503
|
||||
|
||||
def do_GET(self): # noqa: N802
|
||||
self.send_response(self.status)
|
||||
self.send_header("Content-Length", "0")
|
||||
self.end_headers()
|
||||
|
||||
def log_message(self, *args):
|
||||
pass
|
||||
|
||||
|
||||
@contextmanager
|
||||
def image_host(status):
|
||||
"""Serve one image URL whose origin always answers `status`."""
|
||||
_StubImageHost.status = status
|
||||
server = HTTPServer(("127.0.0.1", 0), _StubImageHost)
|
||||
threading.Thread(target=server.serve_forever, daemon=True).start()
|
||||
try:
|
||||
yield f"http://127.0.0.1:{server.server_address[1]}/image.png"
|
||||
finally:
|
||||
server.shutdown()
|
||||
server.server_close()
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"upstream,expected_status,expected_type",
|
||||
[(503, 500, "internal_error"), (404, 400, "invalid_request_error")],
|
||||
)
|
||||
def test_image_fetch_failure_follows_retryability(
|
||||
upstream, expected_status, expected_type
|
||||
):
|
||||
"""An upstream image failure is the server's to own only when it is retryable.
|
||||
|
||||
`ImageError.is_retryable` is the library's classification: `Fetch` is always
|
||||
retryable and `FetchStatus` is retryable at 5xx. Answering a retryable upstream
|
||||
failure with 400 tells the client its request was malformed and should not be
|
||||
retried, which inverts the classification the library hands over.
|
||||
"""
|
||||
with image_host(upstream) as url:
|
||||
with TestClient(SERVER.app) as client:
|
||||
response = client.post(
|
||||
"/v1/chat/completions",
|
||||
json={
|
||||
"model": "deepseek-flash",
|
||||
"stream": False,
|
||||
"messages": [
|
||||
{
|
||||
"role": "user",
|
||||
"content": [
|
||||
{"type": "text", "text": "describe this"},
|
||||
{"type": "image_url", "image_url": {"url": url}},
|
||||
],
|
||||
}
|
||||
],
|
||||
},
|
||||
)
|
||||
assert response.status_code == expected_status, response.text
|
||||
assert response.json()["error"]["type"] == expected_type
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user