Add function tests for nimble API

This change add a functional test base for nimble and add some functional
tests for instance_type APIs.

Change-Id: I44fc4686891d75884860cab86e8c8e42186b5703
This commit is contained in:
liusheng 2016-10-11 15:39:31 +08:00
parent ffa8fbd39f
commit e7e19fa522
8 changed files with 123 additions and 53 deletions

View File

@ -2,6 +2,6 @@
test_command=OS_STDOUT_CAPTURE=${OS_STDOUT_CAPTURE:-1} \
OS_STDERR_CAPTURE=${OS_STDERR_CAPTURE:-1} \
OS_TEST_TIMEOUT=${OS_TEST_TIMEOUT:-60} \
${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION
${PYTHON:-python} -m subunit.run discover ${OS_TEST_PATH:-./nimble/tests} -t . $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list

View File

View File

@ -16,19 +16,14 @@
# under the License.
"""Base classes for API tests."""
# NOTE: Ported from ceilometer/tests/api.py (subsequently moved to
# ceilometer/tests/api/__init__.py). This should be oslo'ified:
# https://bugs.launchpad.net/ironic/+bug/1255115.
from oslo_config import cfg
import pecan
import pecan.testing
from six.moves.urllib import parse as urlparse
from nimble import objects
from nimble.tests.unit.db import base
PATH_PREFIX = '/v1'
cfg.CONF.import_group('keystone_authtoken', 'keystonemiddleware.auth_token')
@ -40,7 +35,7 @@ class BaseApiTest(base.DbTestCase):
framework.
"""
SOURCE_DATA = {'test_source': {'somekey': '666'}}
PATH_PREFIX = ''
def setUp(self):
super(BaseApiTest, self).setUp()
@ -48,6 +43,8 @@ class BaseApiTest(base.DbTestCase):
group='keystone_authtoken')
cfg.CONF.set_override("admin_user", "admin",
group='keystone_authtoken')
objects.register_all()
self.app = self._make_app()
def reset_pecan():
@ -65,14 +62,13 @@ class BaseApiTest(base.DbTestCase):
'modules': ['nimble.api'],
'static_root': '%s/public' % root_dir,
'template_path': '%s/api/templates' % root_dir,
'acl_public_routes': ['/', '/v1'],
'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,
path_prefix=PATH_PREFIX):
method="post", extra_environ=None, status=None):
"""Sends simulated HTTP request to Pecan test app.
:param path: url path of target service
@ -87,17 +83,16 @@ class BaseApiTest(base.DbTestCase):
:param status: expected status code of response
:param path_prefix: prefix of the url path
"""
full_path = path_prefix + path
print('%s: %s %s' % (method.upper(), full_path, params))
response = getattr(self.app, "%s_json" % method)(
str(full_path),
str(path),
params=params,
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors
)
print('GOT:%s' % response)
if not expect_errors:
response = response.json
return response
def put_json(self, path, params, expect_errors=False, headers=None,
@ -113,7 +108,8 @@ class BaseApiTest(base.DbTestCase):
with the request
:param status: expected status code of response
"""
return self._request_json(path=path, params=params,
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")
@ -131,7 +127,8 @@ class BaseApiTest(base.DbTestCase):
with the request
:param status: expected status code of response
"""
return self._request_json(path=path, params=params,
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")
@ -149,13 +146,14 @@ class BaseApiTest(base.DbTestCase):
with the request
:param status: expected status code of response
"""
return self._request_json(path=path, params=params,
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="patch")
def delete(self, path, expect_errors=False, headers=None,
extra_environ=None, status=None, path_prefix=PATH_PREFIX):
extra_environ=None, status=None):
"""Sends simulated HTTP DELETE request to Pecan test app.
:param path: url path of target service
@ -167,18 +165,16 @@ class BaseApiTest(base.DbTestCase):
:param status: expected status code of response
:param path_prefix: prefix of the url path
"""
full_path = path_prefix + path
print('DELETE: %s' % (full_path))
full_path = self.PATH_PREFIX + path
response = self.app.delete(str(full_path),
headers=headers,
status=status,
extra_environ=extra_environ,
expect_errors=expect_errors)
print('GOT:%s' % response)
return response
def get_json(self, path, expect_errors=False, headers=None,
extra_environ=None, q=[], path_prefix=PATH_PREFIX, **params):
extra_environ=None, q=[], **params):
"""Sends simulated HTTP GET request to Pecan test app.
:param path: url path of target service
@ -192,7 +188,7 @@ class BaseApiTest(base.DbTestCase):
:param path_prefix: prefix of the url path
:param params: content for wsgi.input of request
"""
full_path = path_prefix + path
full_path = self.PATH_PREFIX + path
query_params = {'q.field': [],
'q.value': [],
'q.op': [],
@ -204,7 +200,6 @@ class BaseApiTest(base.DbTestCase):
all_params.update(params)
if q:
all_params.update(query_params)
print('GET: %s %r' % (full_path, all_params))
response = self.app.get(full_path,
params=all_params,
headers=headers,
@ -212,22 +207,4 @@ class BaseApiTest(base.DbTestCase):
expect_errors=expect_errors)
if not expect_errors:
response = response.json
print('GOT:%s' % response)
return response
def validate_link(self, link, bookmark=False):
"""Checks if the given link can get correct data."""
# removes the scheme and net location parts of the link
url_parts = list(urlparse.urlparse(link))
url_parts[0] = url_parts[1] = ''
# bookmark link should not have the version in the URL
if bookmark and url_parts[2].startswith(PATH_PREFIX):
return False
full_path = urlparse.urlunparse(url_parts)
try:
self.get_json(full_path, path_prefix='')
return True
except Exception:
return False

View File

@ -13,13 +13,13 @@
# License for the specific language governing permissions and limitations
# under the License.
from nimble.tests.unit.api import base
from nimble.tests.functional import api
class TestRoot(base.BaseApiTest):
class TestRoot(api.BaseApiTest):
def test_get_root(self):
response = self.get_json('/', path_prefix='')
response = self.get_json('/')
# Check fields are not empty
[self.assertNotIn(f, ['', []]) for f in response]

View File

@ -0,0 +1,20 @@
#
# Copyright 2016 Huawei Technologies Co., Ltd.
#
# 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 nimble.tests.functional import api
class APITestV1(api.BaseApiTest):
PATH_PREFIX = '/v1'

View File

@ -0,0 +1,66 @@
#
# Copyright 2016 Huawei Technologies Co., Ltd.
#
# 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 mock
from nimble.tests.functional.api import v1 as v1_test
class TestInstanceType(v1_test.APITestV1):
TYPE_UUIDS = ['ff28b5a2-73e5-431c-b4b7-1b96b74bca7b',
'94baf80e-2bae-4c3e-8dab-38b5441e7097',
'4bcfff85-c55a-493b-b544-b6c90b8fb397',
'e50fb289-4ee1-47dd-b371-6ca39af12888']
def setUp(self):
super(TestInstanceType, self).setUp()
@mock.patch('oslo_utils.uuidutils.generate_uuid')
def _prepare_instance_types(self, mocked):
mocked.side_effect = self.TYPE_UUIDS
for i in xrange(4):
body = {"name": "test" + str(i),
"description": "just test" + str(i)}
self.post_json('/types', body, status=201)
def test_instance_type_post(self):
body = {"name": "test", "description": "just test"}
resp = self.post_json('/types', body, status=201)
self.assertEqual('test', resp['name'])
self.assertEqual('just test', resp['description'])
self.assertEqual(True, resp['is_public'])
self.assertIn('uuid', resp)
self.assertIn('extra_specs', resp)
self.assertIn('links', resp)
def test_instance_type_get_all(self):
self._prepare_instance_types()
resp = self.get_json('/types')
self.assertEqual(4, len(resp['types']))
def test_instance_type_get_one(self):
self._prepare_instance_types()
resp = self.get_json('/types/' + self.TYPE_UUIDS[0])
self.assertEqual('test0', resp['name'])
self.assertEqual('just test0', resp['description'])
def test_instance_type_delete(self):
self._prepare_instance_types()
resp = self.get_json('/types')
self.assertEqual(4, len(resp['types']))
self.delete('/types/' + self.TYPE_UUIDS[0], status=204)
resp = self.get_json('/types')
self.assertEqual(3, len(resp['types']))

View File

@ -20,7 +20,7 @@ from oslo_config import cfg
from nimble.api import hooks
from nimble.common import context
from nimble.tests.unit.api import base
from nimble.tests import base
class FakeRequest(object):
@ -91,7 +91,7 @@ def fake_headers(admin=False):
return headers
class TestContextHook(base.BaseApiTest):
class TestContextHook(base.BaseTestCase):
@mock.patch.object(context, 'RequestContext')
def test_context_hook(self, mock_ctx):
headers = fake_headers(admin=True)
@ -142,13 +142,16 @@ class TestContextHook(base.BaseApiTest):
reqstate.response.headers['Openstack-Request-Id'])
def test_context_hook_after_miss_context(self):
response = self.get_json('/bad/path',
expect_errors=True)
headers = fake_headers(admin=True)
reqstate = FakeRequestState(headers=headers)
reqstate.request.context = {}
context_hook = hooks.ContextHook(None)
context_hook.after(reqstate)
self.assertNotIn('Openstack-Request-Id',
response.headers)
reqstate.response.headers)
class TestPublicUrlHook(base.BaseApiTest):
class TestPublicUrlHook(base.BaseTestCase):
def test_before_host_url(self):
headers = fake_headers()

View File

@ -1,6 +1,6 @@
[tox]
minversion = 2.0
envlist = py35-constraints,py34-constraints,py27-constraints,pypy-constraints,pep8-constraints
envlist = py35-constraints,py34-constraints,py27-constraints,pypy-constraints,pep8-constraints,functional
skipsdist = True
[testenv]
@ -51,6 +51,10 @@ commands = python setup.py build_sphinx
[testenv:debug]
commands = oslo_debug_helper {posargs}
[testenv:functional]
setenv = OS_TEST_PATH=nimble/tests/functional/
commands = python setup.py testr --slowest --testr-args="{posargs}"
[testenv:debug-constraints]
install_command = {[testenv:common-constraints]install_command}
commands = oslo_debug_helper {posargs}