Add support for using Falcon 2.0.0

Falcon 2.0.0 introduces some breaking changes. The relevant ones here are:

- falcon.testing.TestCase.api property was removed
- falcon.testing.TestBase class was removed

This commit also switches the project to use stestr, update sphinx dependency
and add lower-constraints job

Change-Id: I34255a603c069ed3b1e52017634ff01147473221
Story: 2005695
Task: 35688
This commit is contained in:
akhiljain23 2019-07-05 11:56:47 +05:30 committed by Akhil jain
parent 6647b88cbe
commit 8ec3b67b77
11 changed files with 99 additions and 81 deletions

View File

@ -1,9 +0,0 @@
[DEFAULT]
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 $PWD $LISTOPT $IDOPTION
test_id_option=--load-list $IDFILE
test_list_option=--list
group_regex=monasca_events_api\.tests\.unit(?:\.|_)([^_]+)

View File

@ -51,6 +51,7 @@
- project: - project:
templates: templates:
- openstack-cover-jobs - openstack-cover-jobs
- openstack-lower-constraints-jobs
- openstack-python-jobs - openstack-python-jobs
- openstack-python36-jobs - openstack-python36-jobs
- openstack-python37-jobs - openstack-python37-jobs

28
lower-constraints.txt Normal file
View File

@ -0,0 +1,28 @@
bashate==0.2
coverage==4.0
doc8==0.6.0
eventlet==0.18.2
falcon==2.0.0
fixtures==3.0.0
keystonemiddleware==4.12.0
mock==2.0
monasca-common==1.4.0
openstackdocstheme==1.16.0
os-api-ref==1.0.0
oslo.config==6.1.0
oslo.context==2.14.0
oslo.log==3.22.0
oslo.middleware==3.27.0
oslo.policy==1.23.0
oslo.serialization==1.10.0
oslo.utils==3.20.0
oslotest==1.10.0
Paste==2.0.2
PasteDeploy==1.5.0
pbr==2.0.0
reno==2.5.0
simplejson==3.5.1
six==1.10.0
sphinx==1.6.2
stestr==1.0.0
voluptuous==0.8.9

View File

@ -17,7 +17,6 @@ import falcon
from oslo_log import log from oslo_log import log
from monasca_common.rest import exceptions from monasca_common.rest import exceptions
from monasca_common.rest import utils as rest_utils
LOG = log.getLogger(__name__) LOG = log.getLogger(__name__)
@ -31,9 +30,7 @@ def read_json_msg_body(req):
:raises falcon.HTTPBadRequest: :raises falcon.HTTPBadRequest:
""" """
try: try:
msg = req.stream.read() return req.media
json_msg = rest_utils.from_json(msg)
return json_msg
except exceptions.DataConversionException as ex: except exceptions.DataConversionException as ex:
LOG.debug(ex) LOG.debug(ex)
raise falcon.HTTPBadRequest('Bad request', raise falcon.HTTPBadRequest('Bad request',

View File

@ -100,18 +100,12 @@ class PolicyFixture(fixtures.Fixture):
rules[rule.name] = rule.check_str rules[rule.name] = rule.check_str
class MockedApi(falcon.API): class BaseApiTestCase(BaseTestCase, testing.TestCase):
"""Mocked API.
Subclasses :py:class:`falcon.API` in order to overwrite def setUp(self):
request_type property with custom :py:class:`request.Request` super(BaseApiTestCase, self).setUp()
""" self.app = falcon.API(request_type=request.Request)
def __init__(self): # NOTE: Falcon 2.0.0 switches the default for this from
super(MockedApi, self).__init__( # True to False so we explicitly set it here to prevent the behaviour
media_type=falcon.DEFAULT_MEDIA_TYPE, # changing between versions.
request_type=request.Request self.app.req_options.strip_url_path_trailing_slash = True
)
class BaseApiTestCase(BaseTestCase, testing.TestBase):
api_class = MockedApi

View File

@ -27,7 +27,7 @@ ENDPOINT = '/events'
def _init_resource(test): def _init_resource(test):
resource = events.Events() resource = events.Events()
test.api.add_route(ENDPOINT, resource) test.app.add_route(ENDPOINT, resource)
return resource return resource
@ -44,7 +44,7 @@ class TestEventsApi(base.BaseApiTestCase):
json_file_path) json_file_path)
with open(patch_to_req_simple_event_file, 'r') as fi: with open(patch_to_req_simple_event_file, 'r') as fi:
body = fi.read() body = fi.read()
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -53,7 +53,7 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body=body body=body
) )
self.assertEqual(falcon.HTTP_200, self.srmock.status) self.assertEqual(falcon.HTTP_200, response.status)
def test_should_multiple_events(self, bulk_processor): def test_should_multiple_events(self, bulk_processor):
events_resource = _init_resource(self) events_resource = _init_resource(self)
@ -64,7 +64,7 @@ class TestEventsApi(base.BaseApiTestCase):
json_file_path) json_file_path)
with open(req_multiple_events_json, 'r') as fi: with open(req_multiple_events_json, 'r') as fi:
body = fi.read() body = fi.read()
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -73,12 +73,12 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body=body body=body
) )
self.assertEqual(falcon.HTTP_200, self.srmock.status) self.assertEqual(falcon.HTTP_200, response.status)
def test_should_fail_empty_body(self, bulk_processor): def test_should_fail_empty_body(self, bulk_processor):
events_resource = _init_resource(self) events_resource = _init_resource(self)
events_resource._processor = bulk_processor events_resource._processor = bulk_processor
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -87,7 +87,7 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body='' body=''
) )
self.assertEqual(falcon.HTTP_400, self.srmock.status) self.assertEqual(falcon.HTTP_422, response.status)
def test_should_fail_missing_timestamp_in_body(self, bulk_processor): def test_should_fail_missing_timestamp_in_body(self, bulk_processor):
events_resource = _init_resource(self) events_resource = _init_resource(self)
@ -99,7 +99,7 @@ class TestEventsApi(base.BaseApiTestCase):
with open(patch_to_req_simple_event_file, 'r') as fi: with open(patch_to_req_simple_event_file, 'r') as fi:
events = json.load(fi)['events'] events = json.load(fi)['events']
body = {'events': [events]} body = {'events': [events]}
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -108,13 +108,13 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body=json.dumps(body) body=json.dumps(body)
) )
self.assertEqual(falcon.HTTP_422, self.srmock.status) self.assertEqual(falcon.HTTP_422, response.status)
def test_should_fail_missing_events_in_body(self, bulk_processor): def test_should_fail_missing_events_in_body(self, bulk_processor):
events_resource = _init_resource(self) events_resource = _init_resource(self)
events_resource._processor = bulk_processor events_resource._processor = bulk_processor
body = {'timestamp': '2012-10-29T13:42:11Z+0200'} body = {'timestamp': '2012-10-29T13:42:11Z+0200'}
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -123,13 +123,13 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body=json.dumps(body) body=json.dumps(body)
) )
self.assertEqual(falcon.HTTP_422, self.srmock.status) self.assertEqual(falcon.HTTP_422, response.status)
def test_should_fail_missing_content_type(self, bulk_processor): def test_should_fail_missing_content_type(self, bulk_processor):
events_resource = _init_resource(self) events_resource = _init_resource(self)
events_resource._processor = bulk_processor events_resource._processor = bulk_processor
body = {'timestamp': '2012-10-29T13:42:11Z+0200'} body = {'timestamp': '2012-10-29T13:42:11Z+0200'}
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -137,13 +137,13 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body=json.dumps(body) body=json.dumps(body)
) )
self.assertEqual(falcon.HTTP_400, self.srmock.status) self.assertEqual(falcon.HTTP_400, response.status)
def test_should_fail_wrong_content_type(self, bulk_processor): def test_should_fail_wrong_content_type(self, bulk_processor):
events_resource = _init_resource(self) events_resource = _init_resource(self)
events_resource._processor = bulk_processor events_resource._processor = bulk_processor
body = {'timestamp': '2012-10-29T13:42:11Z+0200'} body = {'timestamp': '2012-10-29T13:42:11Z+0200'}
self.simulate_request( response = self.simulate_request(
path=ENDPOINT, path=ENDPOINT,
method='POST', method='POST',
headers={ headers={
@ -152,7 +152,7 @@ class TestEventsApi(base.BaseApiTestCase):
}, },
body=json.dumps(body) body=json.dumps(body)
) )
self.assertEqual(falcon.HTTP_415, self.srmock.status) self.assertEqual(falcon.HTTP_415, response.status)
class TestApiEventsVersion(base.BaseApiTestCase): class TestApiEventsVersion(base.BaseApiTestCase):

View File

@ -14,7 +14,6 @@
import falcon import falcon
import mock import mock
import ujson as json
from monasca_events_api.app.controller import healthchecks from monasca_events_api.app.controller import healthchecks
from monasca_events_api.app.healthcheck import kafka_check as healthcheck from monasca_events_api.app.healthcheck import kafka_check as healthcheck
@ -25,16 +24,19 @@ ENDPOINT = '/healthcheck'
class TestApiHealthChecks(base.BaseApiTestCase): class TestApiHealthChecks(base.BaseApiTestCase):
def before(self): def setUp(self):
super(TestApiHealthChecks, self).setUp()
self.resource = healthchecks.HealthChecks() self.resource = healthchecks.HealthChecks()
self.api.add_route( self.app.add_route(
ENDPOINT, ENDPOINT,
self.resource self.resource
) )
def test_should_return_200_for_head(self): def test_should_return_200_for_head(self):
self.simulate_request(ENDPOINT, method='HEAD') res = self.simulate_request(
self.assertEqual(falcon.HTTP_NO_CONTENT, self.srmock.status) path=ENDPOINT,
method='HEAD')
self.assertEqual(falcon.HTTP_NO_CONTENT, res.status)
@mock.patch('monasca_events_api.app.healthcheck.' @mock.patch('monasca_events_api.app.healthcheck.'
'kafka_check.KafkaHealthCheck') 'kafka_check.KafkaHealthCheck')
@ -43,17 +45,16 @@ class TestApiHealthChecks(base.BaseApiTestCase):
'OK') 'OK')
self.resource._kafka_check = kafka_check self.resource._kafka_check = kafka_check
ret = self.simulate_request(ENDPOINT, res = self.simulate_request(path=ENDPOINT,
headers={ headers={
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
decode='utf8',
method='GET') method='GET')
self.assertEqual(falcon.HTTP_OK, self.srmock.status) self.assertEqual(falcon.HTTP_OK, res.status)
ret = json.loads(ret) res = res.json
self.assertIn('kafka', ret) self.assertIn('kafka', res)
self.assertEqual('OK', ret.get('kafka')) self.assertEqual('OK', res.get('kafka'))
@mock.patch('monasca_events_api.app.healthcheck.' @mock.patch('monasca_events_api.app.healthcheck.'
'kafka_check.KafkaHealthCheck') 'kafka_check.KafkaHealthCheck')
@ -64,14 +65,13 @@ class TestApiHealthChecks(base.BaseApiTestCase):
err_str) err_str)
self.resource._kafka_check = kafka_check self.resource._kafka_check = kafka_check
ret = self.simulate_request(ENDPOINT, res = self.simulate_request(path=ENDPOINT,
headers={ headers={
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, },
decode='utf8',
method='GET') method='GET')
self.assertEqual(falcon.HTTP_SERVICE_UNAVAILABLE, self.srmock.status) self.assertEqual(falcon.HTTP_SERVICE_UNAVAILABLE, res.status)
ret = json.loads(ret) res = res.json
self.assertIn('kafka', ret) self.assertIn('kafka', res)
self.assertEqual(err_str, ret.get('kafka')) self.assertEqual(err_str, res.get('kafka'))

View File

@ -15,7 +15,6 @@
from six.moves.urllib.parse import urlparse as urlparse from six.moves.urllib.parse import urlparse as urlparse
import falcon import falcon
import ujson as json
from monasca_events_api.app.controller import versions from monasca_events_api.app.controller import versions
from monasca_events_api.tests.unit import base from monasca_events_api.tests.unit import base
@ -27,24 +26,25 @@ def _get_versioned_url(version_id):
class TestVersionApi(base.BaseApiTestCase): class TestVersionApi(base.BaseApiTestCase):
def before(self): def setUp(self):
super(TestVersionApi, self).setUp()
self.versions = versions.Versions() self.versions = versions.Versions()
self.api.add_route("/version/", self.versions) self.app.add_route("/version/", self.versions)
self.api.add_route("/version/{version_id}", self.versions) self.app.add_route("/version/{version_id}", self.versions)
def test_request_for_incorrect_version(self): def test_request_for_incorrect_version(self):
incorrect_version = 'v2.0' incorrect_version = 'v2.0'
uri = _get_versioned_url(incorrect_version) uri = _get_versioned_url(incorrect_version)
self.simulate_request( res = self.simulate_request(
uri, path=uri,
method='GET', method='GET',
headers={ headers={
'Content-Type': 'application/json' 'Content-Type': 'application/json'
} }
) )
self.assertEqual(falcon.HTTP_400, self.srmock.status) self.assertEqual(falcon.HTTP_400, res.status)
def test_should_return_supported_event_api_version(self): def test_should_return_supported_event_api_version(self):
@ -80,15 +80,14 @@ class TestVersionApi(base.BaseApiTestCase):
for version in supported_versions: for version in supported_versions:
endpoint = '%s/%s' % (version_endpoint, version) endpoint = '%s/%s' % (version_endpoint, version)
res = self.simulate_request( res = self.simulate_request(
endpoint, path=endpoint,
method='GET', method='GET',
headers={ headers={
'Content-Type': 'application/json' 'Content-Type': 'application/json'
}, }
decode='utf-8'
) )
self.assertEqual(falcon.HTTP_200, self.srmock.status) self.assertEqual(falcon.HTTP_200, res.status)
response = json.loads(res) response = res.json
self.assertIn('links', response) self.assertIn('links', response)
_check_global_links(endpoint, response['links']) _check_global_links(endpoint, response['links'])
self.assertIn('elements', response) self.assertIn('elements', response)

View File

@ -3,10 +3,10 @@
# process, which may cause wedges in the gate later. # process, which may cause wedges in the gate later.
pbr!=2.1.0,>=2.0.0 # Apache-2.0 pbr!=2.1.0,>=2.0.0 # Apache-2.0
Paste # MIT Paste>=2.0.2 # MIT
falcon>=1.0.0 # Apache-2.0 falcon>=2.0.0 # Apache-2.0
keystonemiddleware>=4.12.0 # Apache-2.0 keystonemiddleware>=4.12.0 # Apache-2.0
oslo.config!=4.3.0,!=4.4.0 # Apache-2.0 oslo.config>=6.1.0 # Apache-2.0
oslo.context>=2.14.0 # Apache-2.0 oslo.context>=2.14.0 # Apache-2.0
oslo.middleware>=3.27.0 # Apache-2.0 oslo.middleware>=3.27.0 # Apache-2.0
oslo.log>=3.22.0 # Apache-2.0 oslo.log>=3.22.0 # Apache-2.0

View File

@ -12,13 +12,14 @@ fixtures>=3.0.0 # Apache-2.0/BSD
coverage!=4.4,>=4.0 # Apache-2.0 coverage!=4.4,>=4.0 # Apache-2.0
mock>=2.0 # BSD mock>=2.0 # BSD
oslotest>=1.10.0 # Apache-2.0 oslotest>=1.10.0 # Apache-2.0
os-testr>=0.8.0 # Apache-2.0 simplejson>=3.5.1 # MIT
simplejson>=2.2.0 # MIT stestr>=1.0.0 # Apache-2.0
voluptuous>=0.8.9 # BSD License voluptuous>=0.8.9 # BSD License
# documentation # documentation
doc8 # Apache-2.0 doc8>=0.6.0 # Apache-2.0
sphinx>=1.6.2 # BSD sphinx!=1.6.6,!=1.6.7,>=1.6.2,<2.0.0;python_version=='2.7' # BSD
sphinx!=1.6.6,!=1.6.7,>=1.6.2;python_version>='3.4' # BSD
os-api-ref>=1.0.0 # Apache-2.0 os-api-ref>=1.0.0 # Apache-2.0
reno # Apache-2.0 reno>=2.5.0 # Apache-2.0
openstackdocstheme>=1.16.0 # Apache-2.0 openstackdocstheme>=1.16.0 # Apache-2.0

11
tox.ini
View File

@ -26,14 +26,14 @@ description = Runs unit test using Python2.7
basepython = python2.7 basepython = python2.7
commands = commands =
{[testenv]commands} {[testenv]commands}
ostestr {posargs} stestr run {posargs}
[testenv:py36] [testenv:py36]
description = Runs unit test using Python3.6 description = Runs unit test using Python3.6
basepython = python3.6 basepython = python3.6
commands = commands =
{[testenv]commands} {[testenv]commands}
ostestr {posargs} stestr run {posargs}
[testenv:cover] [testenv:cover]
basepython = python3 basepython = python3
@ -158,4 +158,11 @@ enable-extensions = H203,H106
ignore = D100,D104 ignore = D100,D104
import-order-style = pep8 import-order-style = pep8
[testenv:lower-constraints]
basepython = python3
deps =
-c{toxinidir}/lower-constraints.txt
-r{toxinidir}/test-requirements.txt
-r{toxinidir}/requirements.txt
[hacking] [hacking]