|
| 1 | +from queue import Queue |
| 2 | +from types import SimpleNamespace |
| 3 | +from unittest.mock import Mock |
| 4 | + |
| 5 | +import httpx |
| 6 | +import pytest |
| 7 | + |
| 8 | +from langfuse._task_manager.media_manager import MediaManager |
| 9 | + |
| 10 | + |
| 11 | +def _upload_response(status_code: int, text: str = "") -> httpx.Response: |
| 12 | + request = httpx.Request("PUT", "https://example.com/upload") |
| 13 | + return httpx.Response(status_code=status_code, request=request, text=text) |
| 14 | + |
| 15 | + |
| 16 | +def _upload_job() -> dict: |
| 17 | + return { |
| 18 | + "media_id": "media-id", |
| 19 | + "content_bytes": b"payload", |
| 20 | + "content_type": "image/jpeg", |
| 21 | + "content_length": 7, |
| 22 | + "content_sha256_hash": "sha256hash", |
| 23 | + "trace_id": "trace-id", |
| 24 | + "observation_id": None, |
| 25 | + "field": "input", |
| 26 | + } |
| 27 | + |
| 28 | + |
| 29 | +def test_media_upload_retries_on_retryable_http_status(): |
| 30 | + media_api = Mock() |
| 31 | + media_api.get_upload_url.return_value = SimpleNamespace( |
| 32 | + upload_url="https://example.com/upload", |
| 33 | + media_id="media-id", |
| 34 | + ) |
| 35 | + media_api.patch.return_value = None |
| 36 | + |
| 37 | + httpx_client = Mock() |
| 38 | + httpx_client.put.side_effect = [ |
| 39 | + _upload_response(503, "temporary failure"), |
| 40 | + _upload_response(200, "ok"), |
| 41 | + ] |
| 42 | + |
| 43 | + manager = MediaManager( |
| 44 | + api_client=SimpleNamespace(media=media_api), |
| 45 | + httpx_client=httpx_client, |
| 46 | + media_upload_queue=Queue(), |
| 47 | + max_retries=3, |
| 48 | + ) |
| 49 | + |
| 50 | + manager._process_upload_media_job(data=_upload_job()) |
| 51 | + |
| 52 | + assert httpx_client.put.call_count == 2 |
| 53 | + media_api.patch.assert_called_once() |
| 54 | + assert media_api.patch.call_args.kwargs["upload_http_status"] == 200 |
| 55 | + |
| 56 | + |
| 57 | +def test_media_upload_gives_up_on_non_retryable_http_status(): |
| 58 | + media_api = Mock() |
| 59 | + media_api.get_upload_url.return_value = SimpleNamespace( |
| 60 | + upload_url="https://example.com/upload", |
| 61 | + media_id="media-id", |
| 62 | + ) |
| 63 | + media_api.patch.return_value = None |
| 64 | + |
| 65 | + httpx_client = Mock() |
| 66 | + httpx_client.put.return_value = _upload_response(403, "forbidden") |
| 67 | + |
| 68 | + manager = MediaManager( |
| 69 | + api_client=SimpleNamespace(media=media_api), |
| 70 | + httpx_client=httpx_client, |
| 71 | + media_upload_queue=Queue(), |
| 72 | + max_retries=3, |
| 73 | + ) |
| 74 | + |
| 75 | + with pytest.raises(httpx.HTTPStatusError): |
| 76 | + manager._process_upload_media_job(data=_upload_job()) |
| 77 | + |
| 78 | + assert httpx_client.put.call_count == 1 |
| 79 | + media_api.patch.assert_called_once() |
| 80 | + assert media_api.patch.call_args.kwargs["upload_http_status"] == 403 |
0 commit comments