diff --git a/nova/tests/unit/fake_requests.py b/nova/tests/unit/fake_requests.py new file mode 100644 index 000000000000..da265220154c --- /dev/null +++ b/nova/tests/unit/fake_requests.py @@ -0,0 +1,39 @@ +# 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. +"""Fakes relating to the `requests` module.""" + +import requests + + +class FakeResponse(requests.Response): + def __init__(self, status_code, content=None, headers=None): + """A requests.Response that can be used as a mock return_value. + + A key feature is that the instance will evaluate to True or False like + a real Response, based on the status_code. + + Properties like ok, status_code, text, and content, and methods like + json(), work as expected based on the inputs. + + :param status_code: Integer HTTP response code (200, 404, etc.) + :param content: String supplying the payload content of the response. + Using a json-encoded string will make the json() method + behave as expected. + :param headers: Dict of HTTP header values to set. + """ + super(FakeResponse, self).__init__() + self.status_code = status_code + if content: + self._content = content.encode('utf-8') + self.encoding = 'utf-8' + if headers: + self.headers = headers diff --git a/nova/tests/unit/scheduler/client/test_report.py b/nova/tests/unit/scheduler/client/test_report.py index 5bf3108bef51..60960cadb86a 100644 --- a/nova/tests/unit/scheduler/client/test_report.py +++ b/nova/tests/unit/scheduler/client/test_report.py @@ -14,7 +14,6 @@ import time from keystoneauth1 import exceptions as ks_exc import mock -import requests from six.moves.urllib import parse import nova.conf @@ -25,6 +24,7 @@ from nova import rc_fields as fields from nova.scheduler.client import report from nova.scheduler import utils as scheduler_utils from nova import test +from nova.tests.unit import fake_requests from nova.tests import uuidsentinel as uuids CONF = nova.conf.CONF @@ -1925,12 +1925,9 @@ class TestProviderOperations(SchedulerReportClientTestCase): # record. uuid = uuids.compute_node name = 'computehost' - resp_mock = requests.Response() - resp_mock.status_code = 409 - resp_mock.headers = {'x-openstack-request-id': uuids.request_id} - resp_mock._content = 'not a name conflict'.encode('utf-8') - resp_mock.encoding = 'utf-8' - self.ks_adap_mock.post.return_value = resp_mock + self.ks_adap_mock.post.return_value = fake_requests.FakeResponse( + 409, content='not a name conflict', + headers={'x-openstack-request-id': uuids.request_id}) get_rp_mock.return_value = mock.sentinel.get_rp @@ -1955,13 +1952,9 @@ class TestProviderOperations(SchedulerReportClientTestCase): def test_create_resource_provider_name_conflict(self): # When the API call to create the resource provider fails 409 with a # name conflict, we raise an exception. - resp_mock = requests.Response() - resp_mock.status_code = 409 - resp_mock._content = ( - 'Conflicting resource provider name: ' - 'foo already exists.').encode('utf-8') - resp_mock.encoding = 'utf-8' - self.ks_adap_mock.post.return_value = resp_mock + self.ks_adap_mock.post.return_value = fake_requests.FakeResponse( + 409, content='Conflicting resource provider name: foo ' + 'already exists.') self.assertRaises( exception.ResourceProviderCreationFailed, @@ -1975,11 +1968,8 @@ class TestProviderOperations(SchedulerReportClientTestCase): # deal with uuid = uuids.compute_node name = 'computehost' - resp_mock = requests.Response() - resp_mock.status_code = 503 - self.ks_adap_mock.post.return_value = resp_mock - self.ks_adap_mock.post.return_value.headers = { - 'x-openstack-request-id': uuids.request_id} + self.ks_adap_mock.post.return_value = fake_requests.FakeResponse( + 503, headers={'x-openstack-request-id': uuids.request_id}) self.assertRaises( exception.ResourceProviderCreationFailed, @@ -2011,7 +2001,7 @@ class TestProviderOperations(SchedulerReportClientTestCase): url, json=[], raise_exc=False, microversion=None, headers={}) def test_delete_provider(self): - delete_mock = requests.Response() + delete_mock = fake_requests.FakeResponse(None) self.ks_adap_mock.delete.return_value = delete_mock for status_code in (204, 404): @@ -2032,7 +2022,7 @@ class TestProviderOperations(SchedulerReportClientTestCase): self.ks_adap_mock.delete.reset_mock() def test_delete_provider_fail(self): - delete_mock = requests.Response() + delete_mock = fake_requests.FakeResponse(None) self.ks_adap_mock.delete.return_value = delete_mock resp_exc_map = {409: exception.ResourceProviderInUse, 503: exception.ResourceProviderDeletionFailed} @@ -2232,9 +2222,7 @@ class TestTraits(SchedulerReportClientTestCase): get_mock = mock.Mock(status_code=200) get_mock.json.return_value = {'traits': []} self.ks_adap_mock.get.return_value = get_mock - put_mock = requests.Response() - put_mock.status_code = 400 - self.ks_adap_mock.put.return_value = put_mock + self.ks_adap_mock.put.return_value = fake_requests.FakeResponse(400) self.assertRaises(exception.TraitCreationFailed, self.client._ensure_traits, @@ -2925,13 +2913,8 @@ class TestInventory(SchedulerReportClientTestCase): 'resource_provider_generation': 42, 'inventories': {}, } - try: - mock_put.return_value.__nonzero__.return_value = False - except AttributeError: - # Thanks py3 - mock_put.return_value.__bool__.return_value = False - mock_put.return_value.headers = {'x-openstack-request-id': - uuids.request_id} + mock_put.return_value = fake_requests.FakeResponse( + 400, headers={'x-openstack-request-id': uuids.request_id}) inv_data = report._compute_node_to_inventory_dict(compute_node) result = self.client._update_inventory_attempt( @@ -3334,11 +3317,7 @@ class TestAllocations(SchedulerReportClientTestCase): cn = objects.ComputeNode(uuid=uuids.cn) inst = objects.Instance(uuid=uuids.inst, project_id=uuids.project, user_id=uuids.user) - try: - mock_put.return_value.__nonzero__.return_value = False - except AttributeError: - # NOTE(danms): LOL @ py3 - mock_put.return_value.__bool__.return_value = False + mock_put.return_value = fake_requests.FakeResponse(400) self.client.update_instance_allocation(self.context, cn, inst, 1) self.assertTrue(mock_warn.called) @@ -3359,11 +3338,7 @@ class TestAllocations(SchedulerReportClientTestCase): mock_delete): cn = objects.ComputeNode(uuid=uuids.cn) inst = objects.Instance(uuid=uuids.inst) - try: - mock_delete.return_value.__nonzero__.return_value = False - except AttributeError: - # NOTE(danms): LOL @ py3 - mock_delete.return_value.__bool__.return_value = False + mock_delete.return_value = fake_requests.FakeResponse(400) self.client.update_instance_allocation(self.context, cn, inst, -1) self.assertTrue(mock_warn.called) @@ -3375,13 +3350,7 @@ class TestAllocations(SchedulerReportClientTestCase): """Tests that we don't log a warning on a 404 response when trying to delete an allocation record. """ - mock_response = mock.MagicMock(status_code=404) - try: - mock_response.__nonzero__.return_value = False - except AttributeError: - # py3 uses __bool__ - mock_response.__bool__.return_value = False - mock_delete.return_value = mock_response + mock_delete.return_value = fake_requests.FakeResponse(404) self.client.delete_allocation_for_instance(self.context, uuids.rp_uuid) # make sure we didn't screw up the logic or the mock mock_log.info.assert_not_called() @@ -3443,12 +3412,7 @@ class TestAllocations(SchedulerReportClientTestCase): self.client._provider_tree.new_root(uuids.cn, uuids.cn, 1) cn = objects.ComputeNode(uuid=uuids.cn, host="fake_host", hypervisor_hostname="fake_hostname", ) - resp_mock = mock.MagicMock(status_code=204) - try: - resp_mock.__nonzero__.return_value = True - except AttributeError: - # py3 uses __bool__ - resp_mock.__bool__.return_value = True + resp_mock = fake_requests.FakeResponse(204) mock_delete.return_value = resp_mock self.client.delete_resource_provider(self.context, cn) # With a 204, only the info should be called @@ -3458,11 +3422,6 @@ class TestAllocations(SchedulerReportClientTestCase): # Now check a 404 response mock_log.reset_mock() resp_mock.status_code = 404 - try: - resp_mock.__nonzero__.return_value = False - except AttributeError: - # py3 uses __bool__ - resp_mock.__bool__.return_value = False self.client.delete_resource_provider(self.context, cn) # With a 404, neither log message should be called self.assertEqual(0, mock_log.info.call_count) @@ -3500,9 +3459,7 @@ class TestResourceClass(SchedulerReportClientTestCase): self.mock_put.assert_not_called() def test_ensure_resource_classes_put_fail(self): - resp = requests.Response() - resp.status_code = 503 - self.mock_put.return_value = resp + self.mock_put.return_value = fake_requests.FakeResponse(503) rcs = ['VCPU', 'MEMORY_MB', 'CUSTOM_BAD'] self.assertRaises( exception.InvalidResourceClass, diff --git a/nova/tests/unit/test_identity.py b/nova/tests/unit/test_identity.py index d97929f3b251..76d79bfeff54 100644 --- a/nova/tests/unit/test_identity.py +++ b/nova/tests/unit/test_identity.py @@ -21,30 +21,7 @@ import webob from nova.api.openstack import identity from nova import test - - -class FakeResponse(object): - """A basic response constainer that simulates requests.Response. - - One of the critical things is that a success error code makes the - object return true. - - """ - def __init__(self, status_code, content=""): - self.status_code = status_code - self.content = content - - def __bool__(self): - # python 3 - return self.__nonzero__() - - def __nonzero__(self): - # python 2 - return self.status_code < 400 - - @property - def text(self): - return self.content +from nova.tests.unit import fake_requests class IdentityValidationTest(test.NoDBTestCase): @@ -89,7 +66,7 @@ class IdentityValidationTest(test.NoDBTestCase): found the project exists. """ - self.mock_adap.get.return_value = FakeResponse(200) + self.mock_adap.get.return_value = fake_requests.FakeResponse(200) self.assertTrue(identity.verify_project_id(mock.MagicMock(), "foo")) self.validate_common() @@ -100,7 +77,7 @@ class IdentityValidationTest(test.NoDBTestCase): definitively found the project does not exist. """ - self.mock_adap.get.return_value = FakeResponse(404) + self.mock_adap.get.return_value = fake_requests.FakeResponse(404) self.assertRaises(webob.exc.HTTPBadRequest, identity.verify_project_id, mock.MagicMock(), "foo") @@ -113,7 +90,7 @@ class IdentityValidationTest(test.NoDBTestCase): and assume the project exists. """ - self.mock_adap.get.return_value = FakeResponse(403) + self.mock_adap.get.return_value = fake_requests.FakeResponse(403) self.assertTrue(identity.verify_project_id(mock.MagicMock(), "foo")) self.validate_common() @@ -124,7 +101,8 @@ class IdentityValidationTest(test.NoDBTestCase): side. We don't want to fail on our side. """ - self.mock_adap.get.return_value = FakeResponse(500, "Oh noes!") + self.mock_adap.get.return_value = fake_requests.FakeResponse( + 500, content="Oh noes!") self.assertTrue(identity.verify_project_id(mock.MagicMock(), "foo")) self.validate_common() diff --git a/nova/tests/unit/test_metadata.py b/nova/tests/unit/test_metadata.py index ba03a9d00ce0..b94999aff573 100644 --- a/nova/tests/unit/test_metadata.py +++ b/nova/tests/unit/test_metadata.py @@ -54,7 +54,7 @@ from nova import test from nova.tests.unit.api.openstack import fakes from nova.tests.unit import fake_block_device from nova.tests.unit import fake_network -from nova.tests.unit import test_identity +from nova.tests.unit import fake_requests from nova.tests import uuidsentinel as uuids from nova import utils from nova.virt import netutils @@ -856,9 +856,11 @@ class OpenStackMetadataTestCase(test.TestCase): def _test_vendordata2_response_inner(self, request_mock, response_code, include_rest_result=True): - fake_response = test_identity.FakeResponse(response_code) + content = None if include_rest_result: - fake_response.content = '{"color": "blue"}' + content = '{"color": "blue"}' + fake_response = fake_requests.FakeResponse(response_code, + content=content) request_mock.return_value = fake_response with utils.tempdir() as tmpdir: