diff options
author | bobzhou <zhoubo8@lenovo.com> | 2018-12-17 17:41:39 +0800 |
---|---|---|
committer | bobzhou <zhoubo8@lenovo.com> | 2018-12-17 17:41:39 +0800 |
commit | 31560e9a26e6a7e2e65d18924c938ee7a7683fb3 (patch) | |
tree | 26ec12331a2ec612a0f51c7866fbe41262ca0fe1 /cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api | |
parent | fc1b7908a77b5cce763f92b3d314057345f3ab7a (diff) |
upload cyborg base mitaka version
Change-Id: Iff52024026c9eff899246be70cc89fe6e65befff
Signed-off-by: bobzhou <zhoubo8@lenovo.com>
Diffstat (limited to 'cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api')
6 files changed, 409 insertions, 0 deletions
diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/__init__.py diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/base.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/base.py new file mode 100644 index 0000000..75578fd --- /dev/null +++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/base.py @@ -0,0 +1,214 @@ +# Copyright 2017 Huawei Technologies Co.,LTD. +# All Rights Reserved. +# +# 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. + +"""Base classes for API tests.""" + +from oslo_config import cfg +import pecan +import pecan.testing + +from cyborg.tests.unit.db import base + + +cfg.CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token') + + +class BaseApiTest(base.DbTestCase): + """Pecan controller functional testing class. + + Used for functional tests of Pecan controllers where you need to + test your literal application and its integration with the + framework. + """ + + PATH_PREFIX = '' + + def setUp(self): + super(BaseApiTest, self).setUp() + cfg.CONF.set_override("auth_version", "v3", + group='keystone_authtoken') + cfg.CONF.set_override("admin_user", "admin", + group='keystone_authtoken') + self.app = self._make_app() + + def reset_pecan(): + pecan.set_config({}, overwrite=True) + + self.addCleanup(reset_pecan) + + def _make_app(self): + # Determine where we are so we can set up paths in the config + root_dir = self.get_path() + + self.app_config = { + 'app': { + 'root': 'cyborg.api.controllers.root.RootController', + 'modules': ['cyborg.api'], + 'static_root': '%s/public' % root_dir, + 'template_path': '%s/api/templates' % root_dir, + 'acl_public_routes': ['/', '/v1/.*'], + }, + } + return pecan.testing.load_test_app(self.app_config) + + def _request_json(self, path, params, expect_errors=False, headers=None, + method="post", extra_environ=None, status=None): + """Sends simulated HTTP request to Pecan test app. + + :param path: url path of target service + :param params: content for wsgi.input of request + :param expect_errors: Boolean value; whether an error is expected based + on request + :param headers: a dictionary of headers to send along with the request + :param method: Request method type. Appropriate method function call + should be used rather than passing attribute in. + :param extra_environ: a dictionary of environ variables to send along + with the request + :param status: expected status code of response + """ + response = getattr(self.app, "%s_json" % method)( + str(path), + params=params, + headers=headers, + status=status, + extra_environ=extra_environ, + expect_errors=expect_errors + ) + return response + + def post_json(self, path, params, expect_errors=False, headers=None, + extra_environ=None, status=None): + """Sends simulated HTTP POST request to Pecan test app. + + :param path: url path of target service + :param params: content for wsgi.input of request + :param expect_errors: Boolean value; whether an error is expected based + on request + :param headers: a dictionary of headers to send along with the request + :param extra_environ: a dictionary of environ variables to send along + with the request + :param status: expected status code of response + """ + full_path = self.PATH_PREFIX + path + return self._request_json(path=full_path, params=params, + expect_errors=expect_errors, + headers=headers, extra_environ=extra_environ, + status=status, method="post") + + def gen_headers(self, context, **kw): + """Generate a header for a simulated HTTP request to Pecan test app. + + :param context: context that store the client user information. + :param kw: key word aguments, used to overwrite the context attribute. + + note: "is_public_api" is not in headers, it should be in environ + variables to send along with the request. We can pass it by + extra_environ when we call delete, get_json or other method request. + """ + ct = context.to_dict() + ct.update(kw) + headers = { + 'X-User-Name': ct.get("user_name") or "user", + 'X-User-Id': + ct.get("user") or "1d6d686bc2c949ddb685ffb4682e0047", + 'X-Project-Name': ct.get("project_name") or "project", + 'X-Project-Id': + ct.get("tenant") or "86f64f561b6d4f479655384572727f70", + 'X-User-Domain-Id': + ct.get("domain_id") or "bd5eeb7d0fb046daaf694b36f4df5518", + 'X-User-Domain-Name': ct.get("domain_name") or "no_domain", + 'X-Auth-Token': + ct.get("auth_token") or "b9764005b8c145bf972634fb16a826e8", + 'X-Roles': ct.get("roles") or "cyborg" + } + + return headers + + def get_json(self, path, expect_errors=False, headers=None, + extra_environ=None, q=None, **params): + """Sends simulated HTTP GET request to Pecan test app. + + :param path: url path of target service + :param expect_errors: Boolean value; whether an error is expected based + on request + :param headers: a dictionary of headers to send along with the request + :param extra_environ: a dictionary of environ variables to send along + with the request + :param q: list of queries consisting of: field, value, op, and type + keys + :param path_prefix: prefix of the url path + :param params: content for wsgi.input of request + """ + full_path = self.PATH_PREFIX + path + q = q or [] + query_params = { + 'q.field': [], + 'q.value': [], + 'q.op': [], + } + for query in q: + for name in ['field', 'op', 'value']: + query_params['q.%s' % name].append(query.get(name, '')) + all_params = {} + all_params.update(params) + if q: + all_params.update(query_params) + response = self.app.get(full_path, + params=all_params, + headers=headers, + extra_environ=extra_environ, + expect_errors=expect_errors) + if not expect_errors: + response = response.json + return response + + def patch_json(self, path, params, expect_errors=False, headers=None, + extra_environ=None, status=None): + """Sends simulated HTTP PATCH request to Pecan test app. + + :param path: url path of target service + :param params: content for wsgi.input of request + :param expect_errors: Boolean value; whether an error is expected based + on request + :param headers: a dictionary of headers to send along with the request + :param extra_environ: a dictionary of environ variables to send along + with the request + :param status: expected status code of response + """ + full_path = self.PATH_PREFIX + path + return self._request_json(path=full_path, params=params, + expect_errors=expect_errors, + headers=headers, extra_environ=extra_environ, + status=status, method="put") + + def delete(self, path, expect_errors=False, headers=None, + extra_environ=None, status=None): + """Sends simulated HTTP DELETE request to Pecan test app. + + :param path: url path of target service + :param expect_errors: Boolean value; whether an error is expected based + on request + :param headers: a dictionary of headers to send along with the request + :param extra_environ: a dictionary of environ variables to send along + with the request + :param status: expected status code of response + """ + full_path = self.PATH_PREFIX + path + response = self.app.delete(full_path, + headers=headers, + status=status, + extra_environ=extra_environ, + expect_errors=expect_errors) + return response diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/__init__.py diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/__init__.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/__init__.py diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/base.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/base.py new file mode 100644 index 0000000..c16eaee --- /dev/null +++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/base.py @@ -0,0 +1,21 @@ +# Copyright 2017 Huawei Technologies Co.,LTD. +# All Rights Reserved. +# +# 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 cyborg.tests.unit.api import base + + +class APITestV1(base.BaseApiTest): + + PATH_PREFIX = '/v1' diff --git a/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/test_accelerators.py b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/test_accelerators.py new file mode 100644 index 0000000..9f606a4 --- /dev/null +++ b/cyborg_enhancement/mitaka_version/cyborg/cyborg/tests/unit/api/controllers/v1/test_accelerators.py @@ -0,0 +1,174 @@ +# Copyright 2017 Huawei Technologies Co.,LTD. +# All Rights Reserved. +# +# 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 datetime +import mock +from oslo_utils import timeutils +from six.moves import http_client + +from cyborg.conductor import rpcapi +from cyborg.tests.unit.api.controllers.v1 import base as v1_test +from cyborg.tests.unit.db import utils as db_utils +from cyborg.tests.unit.objects import utils as obj_utils + +def gen_post_body(**kw): + return db_utils.get_test_accelerator(**kw) + + +def _rpcapi_accelerator_create(context, obj_acc): + """Fake used to mock out the conductor RPCAPI's accelerator_create method. + + Performs creation of the accelerator object and returns the created + accelerator as-per the real method. + """ + obj_acc.create(context) + return obj_acc + + + +class TestPost(v1_test.APITestV1): + + ACCELERATOR_UUID = '10efe63d-dfea-4a37-ad94-4116fba50981' + + def setUp(self): + super(TestPost, self).setUp() + self.headers = self.gen_headers(self.context) + + p = mock.patch.object(rpcapi.ConductorAPI, 'accelerator_create') + self.mock_create = p.start() + self.mock_create.side_effect = _rpcapi_accelerator_create + self.addCleanup(p.stop) + + @mock.patch('oslo_utils.uuidutils.generate_uuid') + def test_post(self, mock_uuid): + mock_uuid.return_value = self.ACCELERATOR_UUID + + body = gen_post_body(name='post_accelerator') + response = self.post_json('/accelerators', body, headers=self.headers) + self.assertEqual(http_client.CREATED, response.status_int) + response = response.json + self.assertEqual(self.ACCELERATOR_UUID, response['uuid']) + self.assertEqual(body['name'], response['name']) + self.mock_create.assert_called_once_with(mock.ANY, mock.ANY, mock.ANY) + + +class TestList(v1_test.APITestV1): + + def setUp(self): + super(TestList, self).setUp() + self.accs = [] + for i in range(3): + acc = obj_utils.create_test_accelerator(self.context) + self.accs.append(acc) + self.acc = self.accs[0] + self.context.tenant = self.acc.project_id + self.headers = self.gen_headers(self.context) + + def test_get_one(self): + data = self.get_json('/accelerators/%s' % self.acc.uuid, + headers=self.headers) + self.assertEqual(self.acc.uuid, data['uuid']) + self.assertIn('acc_capability', data) + self.assertIn('acc_type', data) + self.assertIn('created_at', data) + self.assertIn('description', data) + self.assertIn('device_type', data) + self.assertIn('links', data) + self.assertIn('name', data) + self.assertIn('product_id', data) + self.assertIn('project_id', data) + self.assertIn('remotable', data) + self.assertIn('updated_at', data) + self.assertIn('user_id', data) + self.assertIn('vendor_id', data) + + def test_get_all(self): + data = self.get_json('/accelerators', headers=self.headers) + self.assertEqual(3, len(data['accelerators'])) + data_uuids = [d['uuid'] for d in data['accelerators']] + acc_uuids = [acc.uuid for acc in self.accs] + self.assertItemsEqual(acc_uuids, data_uuids) + + +def _rpcapi_accelerator_update(context, obj_acc): + """Fake used to mock out the conductor RPCAPI's accelerator_update method. + + Performs update of the accelerator object and returns the updated + accelerator as-per the real method. + """ + obj_acc.save(context) + return obj_acc + + +class TestPut(v1_test.APITestV1): + + def setUp(self): + super(TestPut, self).setUp() + self.acc = obj_utils.create_test_accelerator(self.context) + self.context.tenant = self.acc.project_id + self.headers = self.gen_headers(self.context) + + p = mock.patch.object(rpcapi.ConductorAPI, 'accelerator_update') + self.mock_update = p.start() + self.mock_update.side_effect = _rpcapi_accelerator_update + self.addCleanup(p.stop) + + @mock.patch.object(timeutils, 'utcnow') + def test_put(self, mock_utcnow): + test_time = datetime.datetime(2012, 12, 12, 12, 12) + mock_utcnow.return_value = test_time + + description = 'new-description' + response = self.patch_json('/accelerators/%s' % self.acc.uuid, + [{'path': '/description', + 'value': description, + 'op': 'replace'}], + headers=self.headers) + self.assertEqual(http_client.OK, response.status_code) + data = self.get_json('/accelerators/%s' % self.acc.uuid, + headers=self.headers) + self.assertEqual(description, data['description']) + return_updated_at = timeutils.parse_isotime( + data['updated_at']).replace(tzinfo=None) + self.assertEqual(test_time, return_updated_at) + self.mock_update.assert_called_once_with(mock.ANY, mock.ANY) + + +def _rpcapi_accelerator_delete(context, obj_acc): + """Fake used to mock out the conductor RPCAPI's accelerator_delete method. + + Performs deletion of the accelerator object as-per the real method. + """ + obj_acc.destroy(context) + + +class TestDelete(v1_test.APITestV1): + + def setUp(self): + super(TestDelete, self).setUp() + self.acc = obj_utils.create_test_accelerator(self.context) + self.context.tenant = self.acc.project_id + self.headers = self.gen_headers(self.context) + + p = mock.patch.object(rpcapi.ConductorAPI, 'accelerator_delete') + self.mock_delete = p.start() + self.mock_delete.side_effect = _rpcapi_accelerator_delete + self.addCleanup(p.stop) + + def test_delete(self): + response = self.delete('/accelerators/%s' % self.acc.uuid, + headers=self.headers) + self.assertEqual(http_client.NO_CONTENT, response.status_code) + self.mock_delete.assert_called_once_with(mock.ANY, mock.ANY) |