From bea8f971ad78a52d9e8801da0bae0d0d615ce022 Mon Sep 17 00:00:00 2001 From: Sascha Herzinger <sascha.herzinger@uni.lu> Date: Tue, 18 Jul 2017 14:10:57 +0200 Subject: [PATCH] added a bunch of tests --- fractalis/data/etlhandler.py | 22 ++++-- fractalis/data/etls/ada/handler_ada.py | 18 ++++- .../data/etls/transmart/handler_transmart.py | 48 ++++++++++--- setup.py | 1 + tests/ada_etls/__init__.py | 0 tests/ada_etls/test_etl_boolean.py | 0 tests/ada_etls/test_etl_date.py | 0 tests/ada_etls/test_etl_double.py | 0 tests/ada_etls/test_etl_enum.py | 0 tests/ada_etls/test_etl_integer.py | 0 tests/ada_etls/test_etl_string.py | 0 tests/ada_etls/test_handler_ada.py | 32 +++++++++ tests/transmart_etls/__init__.py | 0 tests/transmart_etls/test_etl_categorical.py | 0 tests/transmart_etls/test_etl_numerical.py | 0 .../transmart_etls/test_handler_transmart.py | 67 +++++++++++++++++++ 16 files changed, 169 insertions(+), 19 deletions(-) create mode 100644 tests/ada_etls/__init__.py create mode 100644 tests/ada_etls/test_etl_boolean.py create mode 100644 tests/ada_etls/test_etl_date.py create mode 100644 tests/ada_etls/test_etl_double.py create mode 100644 tests/ada_etls/test_etl_enum.py create mode 100644 tests/ada_etls/test_etl_integer.py create mode 100644 tests/ada_etls/test_etl_string.py create mode 100644 tests/ada_etls/test_handler_ada.py create mode 100644 tests/transmart_etls/__init__.py create mode 100644 tests/transmart_etls/test_etl_categorical.py create mode 100644 tests/transmart_etls/test_etl_numerical.py create mode 100644 tests/transmart_etls/test_handler_transmart.py diff --git a/fractalis/data/etlhandler.py b/fractalis/data/etlhandler.py index b09e289..5fcb39f 100644 --- a/fractalis/data/etlhandler.py +++ b/fractalis/data/etlhandler.py @@ -30,23 +30,33 @@ class ETLHandler(metaclass=abc.ABCMeta): """ pass - def _get_token_for_credentials(self, server: str, - user: str, passwd: str) -> str: + def _get_token_for_credentials(self, server: str, auth: dict) -> str: """ Authenticate with the server and return a token. :param server: The server to authenticate with. - :param user: The user id. - :param passwd: The password. + :param auth: dict containing credentials to auth with API + :return The token returned by the API. """ raise NotImplementedError() def __init__(self, server, auth): + if not isinstance(server, str) or len(server) < 10: + error = ("{} is not a valid server url.".format(server)) + logger.error(error) + raise ValueError(error) + if not isinstance(auth, dict): + error = "'auth' must be a valid dictionary." + logger.error(error) + raise ValueError(error) self._server = server # if no token is given we have to get one try: self._token = auth['token'] + if not isinstance(self._token, str) or len(self._token) == 0: + raise KeyError except KeyError: - self._token = self._get_token_for_credentials( - server, auth['user'], auth['passwd']) + logger.info('No token has been provided. ' + 'Attempting to authenticate with the API.') + self._token = self._get_token_for_credentials(server, auth) @staticmethod @abc.abstractmethod diff --git a/fractalis/data/etls/ada/handler_ada.py b/fractalis/data/etls/ada/handler_ada.py index d9a028a..e98eee2 100644 --- a/fractalis/data/etls/ada/handler_ada.py +++ b/fractalis/data/etls/ada/handler_ada.py @@ -1,10 +1,15 @@ """This module provides AdaHandler, an implementation of ETLHandler for ADA.""" +import logging + import requests from fractalis.data.etlhandler import ETLHandler +logger = logging.getLogger(__name__) + + class AdaHandler(ETLHandler): """This ETLHandler provides integration with ADA. @@ -25,8 +30,17 @@ class AdaHandler(ETLHandler): return '{} ({})'.format(descriptor['dictionary']['label'], descriptor['data_set']) - def _get_token_for_credentials(self, server: str, - user: str, passwd: str) -> str: + def _get_token_for_credentials(self, server: str, auth: dict) -> str: + try: + user = auth['user'] + passwd = auth['passwd'] + if len(user) == 0 or len(passwd) == 0: + raise KeyError + except KeyError: + error = "The authentication object must contain the non-empty " \ + "fields 'user' and 'passwd'." + logger.error(error) + raise ValueError(error) r = requests.post(url='{}/login'.format(server), headers={'Accept': 'application/json'}, data={'id': user, 'password': passwd}) diff --git a/fractalis/data/etls/transmart/handler_transmart.py b/fractalis/data/etls/transmart/handler_transmart.py index a5d771d..574ef55 100644 --- a/fractalis/data/etls/transmart/handler_transmart.py +++ b/fractalis/data/etls/transmart/handler_transmart.py @@ -1,11 +1,16 @@ """This module provides TransmartHandler, an implementation of ETLHandler for tranSMART.""" +import logging + import requests from fractalis.data.etlhandler import ETLHandler +logger = logging.getLogger(__name__) + + class TransmartHandler(ETLHandler): """This ETLHandler provides integration with tranSMART. @@ -26,16 +31,37 @@ class TransmartHandler(ETLHandler): def make_label(descriptor: dict) -> str: return descriptor['path'] - def _get_token_for_credentials(self, server: str, - user: str, passwd: str) -> str: - r = requests.post(url='{}/oauth/token?grant_type=password' - '&client_id=glowingbear-js' - '&client_secret=' - '&username={}' - '&password={}'.format(server, user, passwd), + def _get_token_for_credentials(self, server: str, auth: dict) -> str: + try: + user = auth['user'] + passwd = auth['passwd'] + if len(user) == 0 or len(passwd) == 0: + raise KeyError + except KeyError: + error = "The authentication object must contain the non-empty " \ + "fields 'user' and 'passwd'." + logger.error(error) + raise ValueError(error) + r = requests.post(url=server + '/oauth/token', + params={ + 'grant_type': 'password', + 'client_id': 'glowingbear-js', + 'client_secret': '', + 'username': user, + 'password': passwd + }, headers={'Accept': 'application/json'}) + auth_error = '' if r.status_code != 200: - raise ValueError("Could not authenticate. Reason: [{}]: {}" - .format(r.status_code, r.text)) - response = r.json() - return response['access_token'] + auth_error = "Could not authenticate. " \ + "Reason: [{}]: {}".format(r.status_code, r.text) + logger.error(auth_error) + raise ValueError(auth_error) + try: + response = r.json() + return response['access_token'] + except ValueError: + auth_error = "Could not authenticate. " \ + "Got unexpected response: '{}'".format(r.text) + logger.error(auth_error) + raise ValueError(auth_error) \ No newline at end of file diff --git a/setup.py b/setup.py index 6e6165f..af72b7e 100644 --- a/setup.py +++ b/setup.py @@ -28,5 +28,6 @@ setup( 'pytest==3.0.3', 'pytest-cov', 'pytest-mock', + 'responses' ] ) diff --git a/tests/ada_etls/__init__.py b/tests/ada_etls/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_etl_boolean.py b/tests/ada_etls/test_etl_boolean.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_etl_date.py b/tests/ada_etls/test_etl_date.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_etl_double.py b/tests/ada_etls/test_etl_double.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_etl_enum.py b/tests/ada_etls/test_etl_enum.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_etl_integer.py b/tests/ada_etls/test_etl_integer.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_etl_string.py b/tests/ada_etls/test_etl_string.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/ada_etls/test_handler_ada.py b/tests/ada_etls/test_handler_ada.py new file mode 100644 index 0000000..d18d23b --- /dev/null +++ b/tests/ada_etls/test_handler_ada.py @@ -0,0 +1,32 @@ +"""This module provides tests for the ada etl handler.""" + +import pytest + +from fractalis.data.etls.ada.handler_ada import AdaHandler + + +# noinspection PyMissingOrEmptyDocstring,PyMissingTypeHints +class TestAdaHandler: + + @pytest.fixture(scope='function', params=[ + {'server': 'http://foo.bar', 'auth': ''}, + {'server': 'http://foo.bar', 'auth': {}}, + {'server': 'http://foo.bar', 'auth': {'abc': 'abc'}}, + {'server': 'http://foo.bar', 'auth': {'token': ''}}, + {'server': 'http://foo.bar', 'auth': {'token': object}}, + {'server': 'http://foo.bar', 'auth': {'user': 'foo'}}, + {'server': 'http://foo.bar', 'auth': {'passwd': 'foo'}}, + {'server': 'http://foo.bar', 'auth': {'user': '', 'passwd': ''}}, + {'server': 'http://foo.bar', 'auth': {'user': 'foo', 'passwd': ''}}, + {'server': 'http://foo.bar', 'auth': {'user': '', 'passwd': 'foo'}}, + {'server': 'http://foo.bar', + 'auth': {'user': '', 'passwd': '', 'foo': 'bar'}}, + {'server': '', 'auth': {'token': 'foo'}}, + {'server': object, 'auth': {'token': 'foo'}} + ]) + def bad_init_args(self, request): + return request.param + + def test_throws_if_bad_init_args(self, bad_init_args): + with pytest.raises(ValueError): + AdaHandler(**bad_init_args) \ No newline at end of file diff --git a/tests/transmart_etls/__init__.py b/tests/transmart_etls/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/transmart_etls/test_etl_categorical.py b/tests/transmart_etls/test_etl_categorical.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/transmart_etls/test_etl_numerical.py b/tests/transmart_etls/test_etl_numerical.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/transmart_etls/test_handler_transmart.py b/tests/transmart_etls/test_handler_transmart.py new file mode 100644 index 0000000..958fca8 --- /dev/null +++ b/tests/transmart_etls/test_handler_transmart.py @@ -0,0 +1,67 @@ +"""This module provides tests for the transmart etl handler.""" + +import pytest +import responses +import requests + +from fractalis.data.etls.transmart.handler_transmart import TransmartHandler + + +# noinspection PyMissingOrEmptyDocstring,PyMissingTypeHints +class TestTransmartHandler: + + @pytest.fixture(scope='function', params=[ + {'server': 'http://foo.bar', 'auth': ''}, + {'server': 'http://foo.bar', 'auth': {}}, + {'server': 'http://foo.bar', 'auth': {'abc': 'abc'}}, + {'server': 'http://foo.bar', 'auth': {'token': ''}}, + {'server': 'http://foo.bar', 'auth': {'token': object}}, + {'server': 'http://foo.bar', 'auth': {'user': 'foo'}}, + {'server': 'http://foo.bar', 'auth': {'passwd': 'foo'}}, + {'server': 'http://foo.bar', 'auth': {'user': '', 'passwd': ''}}, + {'server': 'http://foo.bar', 'auth': {'user': 'foo', 'passwd': ''}}, + {'server': 'http://foo.bar', 'auth': {'user': '', 'passwd': 'foo'}}, + {'server': 'http://foo.bar', + 'auth': {'user': '', 'passwd': '', 'foo': 'bar'}}, + {'server': '', 'auth': {'token': 'foo'}}, + {'server': object, 'auth': {'token': 'foo'}} + ]) + def bad_init_args(self, request): + return request.param + + def test_throws_if_bad_init_args(self, bad_init_args): + with pytest.raises(ValueError): + TransmartHandler(**bad_init_args) + + def test_returns_token_for_credentials(self): + with responses.RequestsMock() as response: + response.add(response.POST, 'http://foo.bar/oauth/token', + body='{"access_token":"foo-token","token_type":"bearer","expires_in":43185,"scope":"read write"}', + status=200, + content_type='application/json') + tmh = TransmartHandler(server='http://foo.bar', + auth={'user': 'foo', 'passwd': 'bar'}) + assert tmh._token == 'foo-token' + + def test_auth_raises_exception_for_non_json_return(self): + with responses.RequestsMock() as response: + response.add(response.POST, 'http://foo.bar/oauth/token', + body='123{//}', + status=200, + content_type='application/json') + with pytest.raises(ValueError) as e: + TransmartHandler(server='http://foo.bar', + auth={'user': 'foo', 'passwd': 'bar'}) + assert 'unexpected response' in e + + + def test_auth_raises_exception_for_non_200_return(self): + with responses.RequestsMock() as response: + response.add(response.POST, 'http://foo.bar/oauth/token', + body='something', + status=400, + content_type='application/json') + with pytest.raises(ValueError) as e: + TransmartHandler(server='http://foo.bar', + auth={'user': 'foo', 'passwd': 'bar'}) + assert '[400]' in e -- GitLab