From e3797db8890d2ff746557a723cb5c4432c5163de Mon Sep 17 00:00:00 2001 From: Bilal Shaikh Date: Mon, 26 Aug 2019 21:50:07 -0400 Subject: [PATCH 1/2] Added testing for app creation --- datanator_rest_api/core.py | 46 +++-------------------- datanator_rest_api/server/AutoResolver.py | 38 +++++++++++++++++++ datanator_rest_api/server/__init__.py | 1 + tests/requirements.txt | 2 +- tests/test_core.py | 22 ++++++++++- tests/test_implementation.py | 29 -------------- tests/test_spec/test_implementation.py | 40 ++++++++++++++++++++ 7 files changed, 106 insertions(+), 72 deletions(-) create mode 100644 datanator_rest_api/server/AutoResolver.py create mode 100644 datanator_rest_api/server/__init__.py delete mode 100644 tests/test_implementation.py create mode 100644 tests/test_spec/test_implementation.py diff --git a/datanator_rest_api/core.py b/datanator_rest_api/core.py index 6e1bb04..ed81b48 100644 --- a/datanator_rest_api/core.py +++ b/datanator_rest_api/core.py @@ -6,51 +6,17 @@ :License: MIT """ import connexion -from connexion.resolver import Resolver as Resolver -from connexion.resolver import RestyResolver -from connexion.mock import MockResolver +from datanator_rest_api.server import AutoResolver import re +def createApp(apiName="DatanatorAPI.yaml", entryModule="datanator_rest_api.routes",specification_dir="./spec",resolver=AutoResolver,validate_responses=False): + app=connexion.App(__name__, specification_dir=specification_dir) + app.add_api(apiName,resolver=resolver(entryModule), validate_responses=validate_responses) + return app -class AutoResolver(RestyResolver): - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - def resolve_operation_id_using_rest_semantics(self, operation): - """ - Resolves the operationId using REST semantics - :type operation: connexion.operations.AbstractOperation - """ - path_match = re.search( - r'^/?(?P([\w\-](?/*)(?P.*)$', operation.path - ) - - def get_controller_name(): - - name = self.default_module_name - resource_name = path_match.group('resource_name') - - if resource_name: - resource_controller_name = resource_name.replace('-', '_') - name += '.' + resource_controller_name - - return name - - def get_path_name(): - return path_match.group('extended_path').replace('/', '.') if path_match.group('extended_path') else '' - - def get_function_name(): - method = operation.method - - return method.lower() - - return '{}.{}{}'.format(get_controller_name(), get_path_name(), get_function_name()) if __name__ == "__main__": - app = connexion.App(__name__, specification_dir='spec/') - app.add_api('DatanatorAPI.yaml', resolver=AutoResolver( - "datanator_rest_api.routes"), validate_responses=False) + app=createApp() app.run(port=8080, debug=True) diff --git a/datanator_rest_api/server/AutoResolver.py b/datanator_rest_api/server/AutoResolver.py new file mode 100644 index 0000000..771173a --- /dev/null +++ b/datanator_rest_api/server/AutoResolver.py @@ -0,0 +1,38 @@ +from connexion.resolver import RestyResolver +import re + + +class AutoResolver(RestyResolver): + + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + def resolve_operation_id_using_rest_semantics(self, operation): + """ + Resolves the operationId using REST semantics + :type operation: connexion.operations.AbstractOperation + """ + path_match = re.search( + r'^/?(?P([\w\-](?/*)(?P.*)$', operation.path + ) + + def get_controller_name(): + + name = self.default_module_name + resource_name = path_match.group('resource_name') + + if resource_name: + resource_controller_name = resource_name.replace('-', '_') + name += '.' + resource_controller_name + + return name + + def get_path_name(): + return path_match.group('extended_path').replace('/', '.') if path_match.group('extended_path') else '' + + def get_function_name(): + method = operation.method + + return method.lower() + + return '{}.{}{}'.format(get_controller_name(), get_path_name(), get_function_name()) \ No newline at end of file diff --git a/datanator_rest_api/server/__init__.py b/datanator_rest_api/server/__init__.py new file mode 100644 index 0000000..bf90028 --- /dev/null +++ b/datanator_rest_api/server/__init__.py @@ -0,0 +1 @@ +from .AutoResolver import AutoResolver \ No newline at end of file diff --git a/tests/requirements.txt b/tests/requirements.txt index 1e6dc4c..35ac398 100644 --- a/tests/requirements.txt +++ b/tests/requirements.txt @@ -1,4 +1,4 @@ flask >= 1.1.0 capturer # to capture standard output in tests mock # to mock python classes and methods -swagger_tester #to confirm api responses meet specification +prance #to parse api \ No newline at end of file diff --git a/tests/test_core.py b/tests/test_core.py index ab2976c..34b8987 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5,12 +5,30 @@ :Copyright: 2019, Karr Lab :License: MIT """ +import connexion +import flask from datanator_rest_api import core import unittest class CoreTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.app = app=core.createApp() + cls.client = cls.app.app.test_client() + cls.client.testing = True def test_1(self): - # Example test - self.assertTrue(True) # example assertion + response = self.client.get('/datanator/') + assert(response.status_code == 200) + + def test_initialization(self): + + self.assertIs(type(self.app),connexion.apps.flask_app.FlaskApp,msg="App did not create properly") + + def test_run(self): + self.assertIs(type(self.client),flask.testing.FlaskClient) + response = self.client.get('/datanator/') + assert(response.status_code == 200) + + diff --git a/tests/test_implementation.py b/tests/test_implementation.py deleted file mode 100644 index c1a11dc..0000000 --- a/tests/test_implementation.py +++ /dev/null @@ -1,29 +0,0 @@ -""" Test the implementation of the API - -:Author: Bilal Shaikh < bilalshaikh42@gmail.com > -:Date: 2019-08-23 -:Copyright: 2019, Karr Lab -:License: MIT -""" -import connexion -import pytest -import datanator_rest_api.core as core -import unittest - - -class ImplementationTestCase(unittest.TestCase): - - def setUp(self): - self.AutoResolver = core.AutoResolver - - self.app = connexion.FlaskApp(__name__) - self.app.add_api('../datanator_rest_api/spec/DatanatorAPI.yaml', resolver=self.AutoResolver( - "datanator_rest_api.routes"), validate_responses=False) - self.client = self.app.app.test_client() - self.client.testing = True - - def test_1(self): - response = self.client.get('/datanator/') - print(type(response.data)) - print(response.data) - assert(response.status_code == 200) diff --git a/tests/test_spec/test_implementation.py b/tests/test_spec/test_implementation.py new file mode 100644 index 0000000..0f95344 --- /dev/null +++ b/tests/test_spec/test_implementation.py @@ -0,0 +1,40 @@ +""" +Test the implementation of the API +:Author: Bilal Shaikh < bilalshaikh42@gmail.com > +:Date: 2019-08-23 +:Copyright: 2019, Karr Lab +:License: MIT +""" +import connexion +import unittest +import datanator_rest_api.core as core +from prance import BaseParser + + +class ImplementationTestCase(unittest.TestCase): + def setUp(self): + + self.AutoResolver = core.AutoResolver + self.app = connexion.App(__name__) + self.app.add_api('../../datanator_rest_api/spec/DatanatorAPI.yaml', resolver=self.AutoResolver( + "datanator_rest_api.routes"), validate_responses=False) + self.client = self.app.app.test_client() + self.client.testing = True + + def test_1(self): + response = self.client.get('/datanator/') + assert(response.status_code == 200) + + +class RoutesTestCase(unittest.TestCase): + @classmethod + def setUpClass(cls): + cls.parser = BaseParser('./datanator_rest_api/spec/DatanatorAPI.yaml') + cls.specification = cls.parser.specification + cls.paths = cls.specification['paths'] + cls.routes = cls.paths.keys() + + def test_routes(self): + for route in self.routes: + print(route) + assert(True) From 5e7c2344ec2cb069eb1b2a43096f1c6dd7391fac Mon Sep 17 00:00:00 2001 From: Bilal Shaikh Date: Tue, 27 Aug 2019 09:53:57 -0400 Subject: [PATCH 2/2] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a2d3e8b..aa7d0e7 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ [![PyPI package](https://img.shields.io/pypi/v/datanator_rest_api.svg)](https://pypi.python.org/pypi/datanator_rest_api) -[![PyPI Status](https://img.shields.io/pypi/status/openapi-spec-validator.svg)](https://pypi.python.org/pypi/datanator_rest_api) +[![PyPI Status](https://img.shields.io/pypi/status/datanator_rest_api.svg)](https://pypi.python.org/pypi/datanator_rest_api) [![Documentation](https://readthedocs.org/projects/datanator-rest-api/badge/?version=latest)](https://docs.karrlab.org/datanator_rest_api) [![Test results](https://circleci.com/gh/KarrLab/datanator_rest_api.svg?style=shield)](https://circleci.com/gh/KarrLab/datanator_rest_api) [![Test coverage](https://coveralls.io/repos/github/KarrLab/datanator_rest_api/badge.svg)](https://coveralls.io/github/KarrLab/datanator_rest_api)