From 671eb6a515d8f95c32c922de98a4e87998f442b7 Mon Sep 17 00:00:00 2001 From: Nikolaj Starodubtsev Date: Mon, 21 Oct 2013 16:05:46 +0400 Subject: [PATCH] Add unit tests to api functionality Change-Id: Iae5d8a69d9fb0fa272aa0535f84c0ceeba6dd4d9 --- climate/tests/__init__.py | 1 + climate/tests/api/__init__.py | 14 +++ climate/tests/api/test_app.py | 78 ++++++++++++++++ climate/tests/api/test_context.py | 43 +++++++++ climate/tests/api/test_service.py | 48 ++++++++++ climate/tests/api/test_utils.py | 132 +++++++++++++++++++++++++++ climate/tests/api/test_v1_0.py | 56 ++++++++++++ climate/tests/api/test_validation.py | 52 +++++++++++ 8 files changed, 424 insertions(+) create mode 100644 climate/tests/api/__init__.py create mode 100644 climate/tests/api/test_app.py create mode 100644 climate/tests/api/test_context.py create mode 100644 climate/tests/api/test_service.py create mode 100644 climate/tests/api/test_utils.py create mode 100644 climate/tests/api/test_v1_0.py create mode 100644 climate/tests/api/test_validation.py diff --git a/climate/tests/__init__.py b/climate/tests/__init__.py index 41b1d618..fbda4bd8 100644 --- a/climate/tests/__init__.py +++ b/climate/tests/__init__.py @@ -31,6 +31,7 @@ from climate.openstack.common import test CONF = cfg.CONF CONF.set_override('use_stderr', False) + logging.setup('climate') _DB_CACHE = None diff --git a/climate/tests/api/__init__.py b/climate/tests/api/__init__.py new file mode 100644 index 00000000..e3f3e8d4 --- /dev/null +++ b/climate/tests/api/__init__.py @@ -0,0 +1,14 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. diff --git a/climate/tests/api/test_app.py b/climate/tests/api/test_app.py new file mode 100644 index 00000000..7815e579 --- /dev/null +++ b/climate/tests/api/test_app.py @@ -0,0 +1,78 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import flask +from keystoneclient.middleware import auth_token +from werkzeug import exceptions as werkzeug_exceptions + +from climate.api import app +from climate.api import utils as api_utils +from climate import context +from climate import tests + + +class AppTestCase(tests.TestCase): + def setUp(self): + super(AppTestCase, self).setUp() + + self.app = app + self.api_utils = api_utils + self.context = context + self.flask = flask + self.auth_token = auth_token + + self.render = self.patch(self.api_utils, 'render') + self.context_clear = self.patch(self.context.Context, 'clear') + self.fake_app = self.patch(self.flask, 'Flask') + self.fake_ff = self.patch(self.auth_token, 'filter_factory') + + self.ex = werkzeug_exceptions.HTTPException() + self.ex.code = 1313 + self.ex.description = "my favourite error" + + def test_make_json_error_proper(self): + self.app.make_json_error(self.ex) + self.render.assert_called_once_with( + {'error': 1313, + 'error_message': 'my favourite error'}, status=1313) + + def test_make_json_error_wrong(self): + self.app.make_json_error('wrong') + self.render.assert_called_once_with( + {'error': 500, + 'error_message': 'wrong'}, status=500) + + def test_version_list(self): + self.app.version_list() + self.render.assert_called_once_with({ + "versions": [ + {"id": "v1.0", "status": "CURRENT"}, + ], + }) + + def test_teardown_request(self): + self.app.teardown_request() + self.context_clear.assert_called_once() + + def test_make_app(self): + self.app.make_app() + self.fake_ff.assert_called_once_with(self.fake_app().config, + admin_user='admin', + admin_tenant_name='admin', + auth_port='35357', + auth_protocol='http', + auth_version='v2.0', + admin_password='nova', + auth_host='127.0.0.1') diff --git a/climate/tests/api/test_context.py b/climate/tests/api/test_context.py new file mode 100644 index 00000000..0d06e694 --- /dev/null +++ b/climate/tests/api/test_context.py @@ -0,0 +1,43 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from climate.api import context as api_context +from climate import context +from climate import tests + + +class ContextTestCase(tests.TestCase): + def setUp(self): + super(ContextTestCase, self).setUp() + + self.context = self.patch(context, 'Context') + self.fake_headers = {u'X-User-Id': u'1', + u'X-Tenant-Id': u'1', + u'X-Auth-Token': u'111-111-111', + u'X-Service-Catalog': u'catalog', + u'X-User-Name': u'user_name', + u'X-Tenant-Name': u'tenant_name', + u'X-Roles': u'user_name0, user_name1'} + + def test_ctx_from_headers(self): + api_context.ctx_from_headers(self.fake_headers) + self.context.assert_called_once_with(user_id=u'1', + roles=[u'user_name0', + u'user_name1'], + tenant_name=u'tenant_name', + auth_token=u'111-111-111', + service_catalog=u'catalog', + tenant_id=u'1', + user_name=u'user_name') diff --git a/climate/tests/api/test_service.py b/climate/tests/api/test_service.py new file mode 100644 index 00000000..391a9342 --- /dev/null +++ b/climate/tests/api/test_service.py @@ -0,0 +1,48 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from climate.api import service as service_api +from climate import tests + + +class RPCApiTestCase(tests.TestCase): + def setUp(self): + super(RPCApiTestCase, self).setUp() + self.s_api = service_api + + self.fake_list = [] + self.fake_lease = {} + + self.patch(self.s_api.API, "get_leases").return_value = self.fake_list + self.patch(self.s_api.API, "create_lease").return_value = True + self.patch(self.s_api.API, "get_lease").return_value = self.fake_lease + self.patch(self.s_api.API, "update_lease").return_value = True + self.patch(self.s_api.API, "delete_lease").return_value = True + self.patch(self.s_api.API, "get_plugins").return_value(self.fake_lease) + + def test_get_lease(self): + pass + + def test_create_lease(self): + pass + + def test_update_lease(self): + pass + + def test_delete_lease(self): + pass + + def get_plugins(self): + pass diff --git a/climate/tests/api/test_utils.py b/climate/tests/api/test_utils.py new file mode 100644 index 00000000..ad50e7e7 --- /dev/null +++ b/climate/tests/api/test_utils.py @@ -0,0 +1,132 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import flask + +from climate.api import utils +from climate import tests + + +class Error: + def __init__(self, message=None, code=None): + self.message = message + self.code = code + + +class UtilsTestCase(tests.TestCase): + def setUp(self): + super(UtilsTestCase, self).setUp() + self.utils = utils + self.flask = flask + + self.rest = self.patch(self.utils, "Rest") + self.response = self.patch(self.flask, "Response") + self.abort = self.patch(self.flask, "abort") + self.request = self.patch(self.flask, "request") + + self.error = Error("message", "code") + + def test_get(self): + self.rest.get('rule', status_code=200) + self.rest._mroute.called_once_with('GET', 'rule', 200) + + def test_post(self): + self.rest.post('rule', status_code=202) + self.rest._mroute.called_once_with('POST', 'rule', 200) + + def test_put(self): + self.rest.put('rule', status_code=202) + self.rest._mroute.called_once_with('PUT', 'rule', 202) + + def test_delete(self): + self.rest.delete('rule', status_code=204) + self.rest._mroute.called_once_with('DELETE', 'rule', 204) + + def test_route_ok(self): + pass + + def test_route_fail(self): + pass + + def test_render_wrong_result(self): + self.utils.render(result=['a', 'a'], response_type='application/json', + status='LOL', kwargs={'a': 'b'}) + self.abort.assert_called_once_with( + 500, description="Non-dict and non-empty kwargs passed to render.") + + def test_render_wrong_resp_type(self): + self.utils.render(result={}, response_type="not_app", status='LOL') + self.abort.assert_called_once_with( + 400, description="Content type 'not_app' isn't supported") + + def test_render_ok(self): + self.utils.render(result={}, response_type='application/json', + status='lol') + self.response.assert_called_once_with(mimetype='application/json', + status='lol', response='{}') + + def test_request_data_data(self): + self.request.parsed_data = "data" + self.utils.request_data() + self.request.assert_called_once() + + def test_request_data_file(self): + self.request.file_upload = True + self.utils.request_data() + self.request.assert_called_once() + + def test_request_data_length(self): + self.request.content_length = 0 + self.utils.request_data() + self.request.assert_called_once() + + def test_get_request_args(self): + self.utils.get_request_args() + self.request.assert_called_once() + + def test_abort_and_log(self): + self.utils.abort_and_log(400, "Funny error") + self.abort.called_once_with(400, description="Funny error") + + def test_render_error_message(self): + render = self.patch(self.utils, 'render') + self.utils.render_error_message(404, 'NOT FOUND', 'not_found') + render.assert_called_once_with({'error_name': 'not_found', + 'error_message': 'NOT FOUND', + 'error_code': 404}) + + def test_internal_error_501(self): + error_message = self.patch(self.utils, 'render_error_message') + self.utils.internal_error(501, "Funny error") + error_message.assert_called_once_with( + 501, "Funny error", "NOT_IMPLEMENTED_ERROR") + + def test_internal_error_various(self): + error_message = self.patch(self.utils, 'render_error_message') + self.utils.internal_error(404, "Funny error") + error_message.assert_called_once_with( + 404, "Funny error", "INTERNAL_SERVER_ERROR") + + def test_bad_request(self): + error_message = self.patch(self.utils, 'render_error_message') + self.utils.bad_request(self.error) + error_message.assert_called_once_with( + 400, 'message', 'code') + + def test_not_found(self): + error_message = self.patch(self.utils, 'render_error_message') + self.utils.not_found(self.error) + error_message.assert_called_once_with( + 404, 'message', 'code') diff --git a/climate/tests/api/test_v1_0.py b/climate/tests/api/test_v1_0.py new file mode 100644 index 00000000..2b3a2c65 --- /dev/null +++ b/climate/tests/api/test_v1_0.py @@ -0,0 +1,56 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from climate.api import service as service_api +from climate.api import utils as utils_api +from climate.api import v1_0 as api +from climate import tests + + +class RESTApiTestCase(tests.TestCase): + def setUp(self): + super(RESTApiTestCase, self).setUp() + self.api = api + self.u_api = utils_api + self.s_api = service_api + + self.render = self.patch(self.u_api, "render") + self.get_leases = self.patch(self.s_api.API, 'get_leases') + self.create_lease = self.patch(self.s_api.API, 'create_lease') + self.get_lease = self.patch(self.s_api.API, 'get_lease') + self.update_lease = self.patch(self.s_api.API, 'update_lease') + self.delete_lease = self.patch(self.s_api.API, 'delete_lease') + + self.fake_id = '1' + + def test_lease_list(self): + self.api.leases_list() + self.render.assert_called_once_with(leases=self.get_leases()) + + def test_leases_create(self): + self.api.leases_create(data=None) + self.render.assert_called_once_with(lease=self.create_lease()) + + def test_leases_get(self): + self.api.leases_get(lease_id=self.fake_id) + self.render.assert_called_once_with(lease=self.get_lease()) + + def test_leases_update(self): + self.api.leases_update(lease_id=self.fake_id, data=self.fake_id) + self.render.assert_called_once_with(lease=self.update_lease()) + + def test_leases_delete(self): + self.api.leases_delete(lease_id=self.fake_id) + self.render.assert_called_once() diff --git a/climate/tests/api/test_validation.py b/climate/tests/api/test_validation.py new file mode 100644 index 00000000..c0313aa8 --- /dev/null +++ b/climate/tests/api/test_validation.py @@ -0,0 +1,52 @@ +# Copyright (c) 2013 Mirantis Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from climate.api import service as service_api +from climate.api import utils as api_utils +from climate.api import validation as validation_api +from climate import exceptions +from climate import tests + + +class ValidationTestCase(tests.TestCase): + def setUp(self): + super(ValidationTestCase, self).setUp() + + self.s_api = service_api + self.u_api = api_utils + self.v_api = validation_api + self.exc = exceptions + + self.patch(self.u_api, 'render') + self.not_found = self.patch(self.u_api, 'not_found') + + self.fake_id = 1 + + def test_check_true(self): + fake_get = self.patch(self.s_api.API, 'get_lease').return_value = True + + @self.v_api.check_exists(fake_get, self.fake_id) + def trap(fake_id): + return self.u_api.render(lease_id=self.fake_id) + fake_get.assert_called_once_with() + + def test_check_false(self): + fake_get = self.patch( + self.s_api.API, 'get_lease').side_effect = self.exc.NotFound() + + @self.v_api.check_exists(fake_get, self.fake_id) + def trap(fake_id): + self.u_api.render(lease_id=self.fake_id) + self.not_found.assert_called_once_with()