|
1 | 1 | from __future__ import annotations
|
2 | 2 |
|
| 3 | +import json |
3 | 4 | from typing import TYPE_CHECKING
|
4 | 5 |
|
| 6 | +import httpx |
5 | 7 | import pytest
|
| 8 | +import respx |
6 | 9 |
|
7 | 10 | from apify_client._errors import ApifyApiError
|
8 | 11 | from apify_client._http_client import HTTPClient, HTTPClientAsync
|
9 | 12 |
|
10 | 13 | if TYPE_CHECKING:
|
| 14 | + from collections.abc import AsyncIterator, Iterator |
| 15 | + |
11 | 16 | from pytest_httpserver import HTTPServer
|
12 | 17 |
|
13 | 18 | _TEST_PATH = '/errors'
|
|
17 | 22 | 'invalidItems': {'0': ["should have required property 'name'"], '1': ["should have required property 'name'"]}
|
18 | 23 | }
|
19 | 24 |
|
| 25 | +RAW_ERROR = ( |
| 26 | + b'{\n' |
| 27 | + b' "error": {\n' |
| 28 | + b' "type": "insufficient-permissions",\n' |
| 29 | + b' "message": "Insufficient permissions for the Actor run. Make sure you\'' |
| 30 | + b're passing a correct API token and that it has the required permissions."\n' |
| 31 | + b' }\n' |
| 32 | + b'}' |
| 33 | +) |
| 34 | + |
20 | 35 |
|
21 | 36 | @pytest.fixture
|
22 | 37 | def test_endpoint(httpserver: HTTPServer) -> str:
|
@@ -48,3 +63,53 @@ async def test_async_client_apify_api_error_with_data(test_endpoint: str) -> Non
|
48 | 63 | assert e.value.message == _EXPECTED_MESSAGE
|
49 | 64 | assert e.value.type == _EXPECTED_TYPE
|
50 | 65 | assert e.value.data == _EXPECTED_DATA
|
| 66 | + |
| 67 | + |
| 68 | +def test_client_apify_api_error_streamed() -> None: |
| 69 | + """Test that client correctly throws ApifyApiError when the response has stream.""" |
| 70 | + |
| 71 | + error = json.loads(RAW_ERROR.decode()) |
| 72 | + |
| 73 | + class ByteStream(httpx._types.SyncByteStream): |
| 74 | + def __iter__(self) -> Iterator[bytes]: |
| 75 | + yield RAW_ERROR |
| 76 | + |
| 77 | + def close(self) -> None: |
| 78 | + pass |
| 79 | + |
| 80 | + stream_url = 'http://some-stream-url.com' |
| 81 | + |
| 82 | + client = HTTPClient() |
| 83 | + |
| 84 | + with respx.mock() as respx_mock: |
| 85 | + respx_mock.get(url=stream_url).mock(return_value=httpx.Response(stream=ByteStream(), status_code=403)) |
| 86 | + with pytest.raises(ApifyApiError) as e: |
| 87 | + client.call(method='GET', url=stream_url, stream=True, parse_response=False) |
| 88 | + |
| 89 | + assert e.value.message == error['error']['message'] |
| 90 | + assert e.value.type == error['error']['type'] |
| 91 | + |
| 92 | + |
| 93 | +async def test_async_client_apify_api_error_streamed() -> None: |
| 94 | + """Test that async client correctly throws ApifyApiError when the response has stream.""" |
| 95 | + |
| 96 | + error = json.loads(RAW_ERROR.decode()) |
| 97 | + |
| 98 | + class AsyncByteStream(httpx._types.AsyncByteStream): |
| 99 | + async def __aiter__(self) -> AsyncIterator[bytes]: |
| 100 | + yield RAW_ERROR |
| 101 | + |
| 102 | + async def aclose(self) -> None: |
| 103 | + pass |
| 104 | + |
| 105 | + stream_url = 'http://some-stream-url.com' |
| 106 | + |
| 107 | + client = HTTPClientAsync() |
| 108 | + |
| 109 | + with respx.mock() as respx_mock: |
| 110 | + respx_mock.get(url=stream_url).mock(return_value=httpx.Response(stream=AsyncByteStream(), status_code=403)) |
| 111 | + with pytest.raises(ApifyApiError) as e: |
| 112 | + await client.call(method='GET', url=stream_url, stream=True, parse_response=False) |
| 113 | + |
| 114 | + assert e.value.message == error['error']['message'] |
| 115 | + assert e.value.type == error['error']['type'] |
0 commit comments