Skip to content

Commit c019e56

Browse files
Added Async api client tests
1 parent cbc28f7 commit c019e56

2 files changed

Lines changed: 280 additions & 0 deletions

File tree

tests/unit/asyncio_client/__init__.py

Whitespace-only changes.
Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
import pytest
2+
from unittest.mock import AsyncMock, MagicMock, patch
3+
from conductor.asyncio_client.adapters.api_client_adapter import ApiClientAdapter
4+
from conductor.asyncio_client.http.exceptions import ApiException
5+
from conductor.asyncio_client.http.api_response import ApiResponse
6+
7+
8+
@pytest.fixture
9+
def mock_config():
10+
config = MagicMock()
11+
config.host = "http://test.com"
12+
config.api_key = {"api_key": "test_token"}
13+
config.auth_key = "test_key"
14+
config.auth_secret = "test_secret"
15+
return config
16+
17+
18+
@pytest.fixture
19+
def adapter(mock_config):
20+
client_adapter = ApiClientAdapter()
21+
client_adapter.configuration = mock_config
22+
client_adapter.rest_client = AsyncMock()
23+
return client_adapter
24+
25+
26+
def test_get_default():
27+
ApiClientAdapter._default = None
28+
instance1 = ApiClientAdapter.get_default()
29+
instance2 = ApiClientAdapter.get_default()
30+
assert instance1 is instance2
31+
assert isinstance(instance1, ApiClientAdapter)
32+
33+
34+
@pytest.mark.asyncio
35+
async def test_call_api_success(adapter):
36+
mock_response = MagicMock()
37+
mock_response.status = 200
38+
adapter.rest_client.request = AsyncMock(return_value=mock_response)
39+
40+
result = await adapter.call_api("GET", "http://test.com/api")
41+
42+
assert result == mock_response
43+
adapter.rest_client.request.assert_called_once()
44+
45+
46+
@pytest.mark.asyncio
47+
async def test_call_api_401_retry(adapter):
48+
mock_response = MagicMock()
49+
mock_response.status = 401
50+
adapter.rest_client.request = AsyncMock(return_value=mock_response)
51+
adapter.refresh_authorization_token = AsyncMock(return_value="new_token")
52+
53+
result = await adapter.call_api(
54+
"GET", "http://test.com/api", {"X-Authorization": "old_token"}
55+
)
56+
57+
assert result == mock_response
58+
assert adapter.rest_client.request.call_count == 2
59+
assert adapter.refresh_authorization_token.called
60+
61+
62+
@pytest.mark.asyncio
63+
async def test_call_api_401_token_endpoint_no_retry(adapter):
64+
mock_response = MagicMock()
65+
mock_response.status = 401
66+
adapter.rest_client.request = AsyncMock(return_value=mock_response)
67+
adapter.refresh_authorization_token = AsyncMock()
68+
69+
result = await adapter.call_api("POST", "http://test.com/token")
70+
71+
assert result == mock_response
72+
adapter.rest_client.request.assert_called_once()
73+
adapter.refresh_authorization_token.assert_not_called()
74+
75+
76+
@pytest.mark.asyncio
77+
async def test_call_api_exception(adapter):
78+
adapter.rest_client.request = AsyncMock(
79+
side_effect=ApiException(status=500, reason="Server Error")
80+
)
81+
82+
with pytest.raises(ApiException):
83+
await adapter.call_api("GET", "http://test.com/api")
84+
85+
86+
def test_response_deserialize_success(adapter):
87+
mock_response = MagicMock()
88+
mock_response.data = b'{"test": "data"}'
89+
mock_response.status = 200
90+
mock_response.getheader.return_value = "application/json; charset=utf-8"
91+
mock_response.getheaders.return_value = {"content-type": "application/json"}
92+
93+
response_types_map = {"200": "object"}
94+
adapter.deserialize = MagicMock(return_value={"test": "data"})
95+
96+
result = adapter.response_deserialize(mock_response, response_types_map)
97+
98+
assert isinstance(result, ApiResponse)
99+
assert result.status_code == 200
100+
assert result.data == {"test": "data"}
101+
102+
103+
def test_response_deserialize_bytearray(adapter):
104+
mock_response = MagicMock()
105+
mock_response.data = b"binary data"
106+
mock_response.status = 200
107+
mock_response.getheaders.return_value = {}
108+
109+
response_types_map = {"200": "bytearray"}
110+
111+
result = adapter.response_deserialize(mock_response, response_types_map)
112+
113+
assert result.data == b"binary data"
114+
115+
116+
def test_response_deserialize_file(adapter):
117+
mock_response = MagicMock()
118+
mock_response.data = b"file content"
119+
mock_response.status = 200
120+
mock_response.getheaders.return_value = {}
121+
122+
response_types_map = {"200": "file"}
123+
adapter._ApiClientAdapter__deserialize_file = MagicMock(return_value="file_object")
124+
125+
result = adapter.response_deserialize(mock_response, response_types_map)
126+
127+
assert result.data == "file_object"
128+
129+
130+
def test_response_deserialize_with_xx_status(adapter):
131+
mock_response = MagicMock()
132+
mock_response.data = b'{"test": "data"}'
133+
mock_response.status = 201
134+
mock_response.getheader.return_value = "application/json; charset=utf-8"
135+
mock_response.getheaders.return_value = {"content-type": "application/json"}
136+
137+
response_types_map = {"2XX": "object"}
138+
adapter.deserialize = MagicMock(return_value={"test": "data"})
139+
140+
result = adapter.response_deserialize(mock_response, response_types_map)
141+
142+
assert result.status_code == 201
143+
144+
145+
def test_response_deserialize_error_status(adapter):
146+
mock_response = MagicMock()
147+
mock_response.data = b'{"error": "message"}'
148+
mock_response.status = 400
149+
mock_response.getheader.return_value = "application/json; charset=utf-8"
150+
mock_response.getheaders.return_value = {"content-type": "application/json"}
151+
152+
response_types_map = {"400": "object"}
153+
adapter.deserialize = MagicMock(return_value={"error": "message"})
154+
155+
with pytest.raises(ApiException):
156+
adapter.response_deserialize(mock_response, response_types_map)
157+
158+
159+
def test_response_deserialize_no_data_assertion(adapter):
160+
mock_response = MagicMock()
161+
mock_response.data = None
162+
163+
with pytest.raises(AssertionError) as exc_info:
164+
adapter.response_deserialize(mock_response, {})
165+
166+
assert "RESTResponse.read() must be called" in str(exc_info.value)
167+
168+
169+
@pytest.mark.asyncio
170+
async def test_refresh_authorization_token(adapter):
171+
mock_token_response = {"token": "new_token_value"}
172+
adapter.obtain_new_token = AsyncMock(return_value=mock_token_response)
173+
174+
result = await adapter.refresh_authorization_token()
175+
176+
assert result == "new_token_value"
177+
assert adapter.configuration.api_key["api_key"] == "new_token_value"
178+
179+
180+
@pytest.mark.asyncio
181+
async def test_obtain_new_token(adapter):
182+
mock_response = MagicMock()
183+
mock_response.data = b'{"token": "test_token"}'
184+
mock_response.read = AsyncMock()
185+
adapter.call_api = AsyncMock(return_value=mock_response)
186+
adapter.param_serialize = MagicMock(
187+
return_value=(
188+
"POST",
189+
"/token",
190+
{},
191+
{"key_id": "test_key", "key_secret": "test_secret"},
192+
)
193+
)
194+
195+
result = await adapter.obtain_new_token()
196+
197+
assert result == {"token": "test_token"}
198+
adapter.call_api.assert_called_once()
199+
mock_response.read.assert_called_once()
200+
201+
202+
@pytest.mark.asyncio
203+
async def test_obtain_new_token_with_patch():
204+
with patch(
205+
"conductor.asyncio_client.adapters.api_client_adapter.GenerateTokenRequest"
206+
) as mock_generate_token:
207+
mock_token_request = MagicMock()
208+
mock_token_request.to_dict.return_value = {
209+
"key_id": "test_key",
210+
"key_secret": "test_secret",
211+
}
212+
mock_generate_token.return_value = mock_token_request
213+
214+
client_adapter = ApiClientAdapter()
215+
client_adapter.configuration = MagicMock()
216+
client_adapter.configuration.auth_key = "test_key"
217+
client_adapter.configuration.auth_secret = "test_secret"
218+
client_adapter.param_serialize = MagicMock(
219+
return_value=("POST", "/token", {}, {})
220+
)
221+
222+
mock_response = MagicMock()
223+
mock_response.data = b'{"token": "test_token"}'
224+
mock_response.read = AsyncMock()
225+
client_adapter.call_api = AsyncMock(return_value=mock_response)
226+
227+
result = await client_adapter.obtain_new_token()
228+
229+
assert result == {"token": "test_token"}
230+
mock_generate_token.assert_called_once_with(
231+
key_id="test_key", key_secret="test_secret"
232+
)
233+
234+
235+
def test_response_deserialize_encoding_detection(adapter):
236+
mock_response = MagicMock()
237+
mock_response.data = b'{"test": "data"}'
238+
mock_response.status = 200
239+
mock_response.getheader.return_value = "application/json; charset=iso-8859-1"
240+
mock_response.getheaders.return_value = {"content-type": "application/json"}
241+
242+
response_types_map = {"200": "object"}
243+
adapter.deserialize = MagicMock(return_value={"test": "data"})
244+
245+
result = adapter.response_deserialize(mock_response, response_types_map)
246+
247+
assert result.status_code == 200
248+
adapter.deserialize.assert_called_once()
249+
250+
251+
def test_response_deserialize_no_content_type(adapter):
252+
mock_response = MagicMock()
253+
mock_response.data = b'{"test": "data"}'
254+
mock_response.status = 200
255+
mock_response.getheader.return_value = None
256+
mock_response.getheaders.return_value = {}
257+
258+
response_types_map = {"200": "object"}
259+
adapter.deserialize = MagicMock(return_value={"test": "data"})
260+
261+
result = adapter.response_deserialize(mock_response, response_types_map)
262+
263+
assert result.status_code == 200
264+
adapter.deserialize.assert_called_once()
265+
266+
267+
def test_response_deserialize_no_match_content_type(adapter):
268+
mock_response = MagicMock()
269+
mock_response.data = b'{"test": "data"}'
270+
mock_response.status = 200
271+
mock_response.getheader.return_value = "application/json"
272+
mock_response.getheaders.return_value = {}
273+
274+
response_types_map = {"200": "object"}
275+
adapter.deserialize = MagicMock(return_value={"test": "data"})
276+
277+
result = adapter.response_deserialize(mock_response, response_types_map)
278+
279+
assert result.status_code == 200
280+
adapter.deserialize.assert_called_once()

0 commit comments

Comments
 (0)