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