From a894ba3d9ddb0f7918a8b4ef0e94aac304af5295 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joa=CC=83o=20Male=CC=81s?= Date: Fri, 20 Sep 2024 15:59:33 +0100 Subject: [PATCH] CP-2386 Add tests to Core. --- .github/workflows/python.yaml | 2 +- .../src/thousandeyes_sdk/core/api_client.py | 2 + thousandeyes-sdk-core/test/test_api_client.py | 124 ++++++++++++++++++ .../test/test_api_response.py | 48 +++++++ .../test/test_thousandeyes_retry.py | 43 ++++++ 5 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 thousandeyes-sdk-core/test/test_api_client.py create mode 100644 thousandeyes-sdk-core/test/test_api_response.py create mode 100644 thousandeyes-sdk-core/test/test_thousandeyes_retry.py diff --git a/.github/workflows/python.yaml b/.github/workflows/python.yaml index bf520065..4529c02a 100644 --- a/.github/workflows/python.yaml +++ b/.github/workflows/python.yaml @@ -23,7 +23,7 @@ jobs: - name: Install and test modules run: | pip install pytest - for module in $(find . -maxdepth 1 -type d -name "thousandeyes-sdk-*" ! -name "thousandeyes-sdk-core" | cut -c 3-); do + for module in $(find . -maxdepth 1 -type d -name "thousandeyes-sdk-*" | cut -c 3-); do pip install -e $module pytest $module done diff --git a/thousandeyes-sdk-core/src/thousandeyes_sdk/core/api_client.py b/thousandeyes-sdk-core/src/thousandeyes_sdk/core/api_client.py index d8305ed4..16aa9859 100644 --- a/thousandeyes-sdk-core/src/thousandeyes_sdk/core/api_client.py +++ b/thousandeyes-sdk-core/src/thousandeyes_sdk/core/api_client.py @@ -428,6 +428,8 @@ class ApiClient: # convert str to class if klass in self.NATIVE_TYPES_MAPPING: klass = self.NATIVE_TYPES_MAPPING[klass] + elif klass == 'dict': + return data else: klass = getattr(models, klass) diff --git a/thousandeyes-sdk-core/test/test_api_client.py b/thousandeyes-sdk-core/test/test_api_client.py new file mode 100644 index 00000000..2cef9ff4 --- /dev/null +++ b/thousandeyes-sdk-core/test/test_api_client.py @@ -0,0 +1,124 @@ +import pytest +import datetime +from unittest.mock import Mock, patch +from thousandeyes_sdk.core.api_client import ApiClient +from thousandeyes_sdk.core.configuration import Configuration +from thousandeyes_sdk.core import rest +from thousandeyes_sdk.core.exceptions import ApiException + + +@pytest.fixture +def api_client(): + config = Configuration() + return ApiClient(configuration=config) + + +def test_api_client_initialization(api_client): + assert api_client.configuration is not None + assert isinstance(api_client.rest_client, rest.RESTClientObject) + assert api_client.default_headers == {} + assert api_client.cookie is None + + +def test_set_default_header(api_client): + api_client.set_default_header('X-Test-Header', 'test_value') + assert api_client.default_headers['X-Test-Header'] == 'test_value' + + +def test_user_agent_property(api_client): + api_client.user_agent = 'test-agent' + assert api_client.user_agent == 'test-agent' + + +def test_get_default(): + default_client = ApiClient.get_default() + assert isinstance(default_client, ApiClient) + + +def test_set_default(): + new_default = ApiClient() + ApiClient.set_default(new_default) + assert ApiClient.get_default() == new_default + + +def test_param_serialize(api_client): + method, url, headers, _, __ = api_client.param_serialize( + method='GET', + resource_path='/test/{id}', + path_params={'id': 1}, + query_params={'aid': '12'}, + header_params={'X-Test': 'test_value'} + ) + assert method == 'GET' + print(url + "3") + assert url == api_client.configuration.host + '/test/1?aid=12' + assert headers['X-Test'] == 'test_value' + + +@patch('thousandeyes_sdk.core.rest.RESTClientObject.request') +def test_call_api(mock_request, api_client): + mock_response = Mock() + mock_response.data = b'{"dummyKey": "someValue"}' + mock_response.status = 200 + mock_request.return_value = mock_response + + response = api_client.call_api( + method='GET', + url='/tests', + body=None, + post_params=None, + _request_timeout=None + ) + assert response.data == b'{"dummyKey": "someValue"}' + assert response.status == 200 + + +@patch('thousandeyes_sdk.core.rest.RESTClientObject.request') +def test_call_api_exception(mock_request, api_client): + mock_request.side_effect = ApiException(status=404, reason="Not Found") + with pytest.raises(ApiException): + api_client.call_api( + method='GET', + url='/tests', + body=None, + post_params=None, + _request_timeout=None + ) + + +def test_response_deserialize(api_client): + mock_response = Mock() + mock_response.data = b'{"dummyKey": "someValue"}' + mock_response.status = 200 + mock_response.getheader.return_value = 'application/json' + mock_response.getheaders.return_value = {} + + response = api_client.response_deserialize( + response_data=mock_response, + response_types_map={'200': 'dict'}, + models={} + ) + assert response.data == {"dummyKey": "someValue"} + assert response.status_code == 200 + + +def test_sanitize_for_serialization(api_client): + data = { + 'str': 'value', + 'int': 1, + 'float': 1.1, + 'bool': True, + 'datetime': datetime.datetime(2023, 1, 1), + 'date': datetime.date(2023, 1, 1), + 'list': [1, 2, 3], + 'dict': {'key': 'value'} + } + sanitized = api_client.sanitize_for_serialization(data) + assert sanitized['str'] == 'value' + assert sanitized['int'] == 1 + assert sanitized['float'] == 1.1 + assert sanitized['bool'] is True + assert sanitized['datetime'] == '2023-01-01T00:00:00' + assert sanitized['date'] == '2023-01-01' + assert sanitized['list'] == [1, 2, 3] + assert sanitized['dict'] == {'key': 'value'} diff --git a/thousandeyes-sdk-core/test/test_api_response.py b/thousandeyes-sdk-core/test/test_api_response.py new file mode 100644 index 00000000..d3b74a35 --- /dev/null +++ b/thousandeyes-sdk-core/test/test_api_response.py @@ -0,0 +1,48 @@ +import pytest +from pydantic import ValidationError +from thousandeyes_sdk.core.api_response import ApiResponse + + +def test_api_response_valid(): + response = ApiResponse( + status_code=200, + headers={"Content-Type": "application/json"}, + data={"key": "value"}, + raw_data=b'{"key": "value"}' + ) + assert response.status_code == 200 + assert response.headers == {"Content-Type": "application/json"} + assert response.data == {"key": "value"} + assert response.raw_data == b'{"key": "value"}' + + +def test_api_response_missing_optional_headers(): + response = ApiResponse( + status_code=200, + data={"key": "value"}, + raw_data=b'{"key": "value"}' + ) + assert response.status_code == 200 + assert response.headers is None + assert response.data == {"key": "value"} + assert response.raw_data == b'{"key": "value"}' + + +def test_api_response_invalid_status_code(): + with pytest.raises(ValidationError): + ApiResponse( + status_code="200", # Invalid type + headers={"Content-Type": "application/json"}, + data={"key": "value"}, + raw_data=b'{"key": "value"}' + ) + + +def test_api_response_invalid_raw_data(): + with pytest.raises(ValidationError): + ApiResponse( + status_code=200, + headers={"Content-Type": "application/json"}, + data={"key": "value"}, + raw_data="raw data" # Invalid type + ) diff --git a/thousandeyes-sdk-core/test/test_thousandeyes_retry.py b/thousandeyes-sdk-core/test/test_thousandeyes_retry.py new file mode 100644 index 00000000..944e5dd6 --- /dev/null +++ b/thousandeyes-sdk-core/test/test_thousandeyes_retry.py @@ -0,0 +1,43 @@ +import time +import pytest +from unittest.mock import Mock +from urllib3 import HTTPResponse +from thousandeyes_sdk.core.thousandeyes_retry import ThousandEyesRetry + + +def test_is_retry_on_429(): + retry = ThousandEyesRetry() + assert retry.is_retry("GET", 429) is True + + +def test_is_retry_on_other_status(): + retry = ThousandEyesRetry(status_forcelist=[500]) + assert retry.is_retry("GET", 500) is True + assert retry.is_retry("GET", 404) is False + + +def test_get_retry_after_with_custom_header(): + response = Mock(spec=HTTPResponse) + response.headers = { + "x-organization-rate-limit-reset": str(int(time.time()) + 100)} + retry = ThousandEyesRetry() + assert retry.get_retry_after(response) == pytest.approx(100, rel=1) + + +def test_get_retry_after_with_no_headers(): + response = Mock(spec=HTTPResponse) + response.headers = {} + retry = ThousandEyesRetry() + assert retry.get_retry_after(response) is None + + +def test_parse_reset_header_valid(): + retry = ThousandEyesRetry() + future_time = str(int(time.time()) + 100) + assert retry._parse_reset_header(future_time) == pytest.approx(100, rel=1) + + +def test_parse_reset_header_invalid(): + retry = ThousandEyesRetry() + assert retry._parse_reset_header("invalid") is None + assert retry._parse_reset_header(None) is None