Merge "Improve unit test coverage for dcmanager's APIs (sw_update_options)"
This commit is contained in:
commit
9493673a68
@ -158,47 +158,55 @@ class DCManagerTestCase(base.BaseTestCase):
|
||||
self.ctx = utils.dummy_context()
|
||||
self._mock_pecan()
|
||||
|
||||
# TODO(rlima): update the mock creation in the methods below
|
||||
def _mock_object(self, target, attribute, name, wraps=None):
|
||||
"""Mock a specified target's attribute and save it in a variable"""
|
||||
|
||||
mock_patch_object = mock.patch.object(target, attribute, wraps=wraps)
|
||||
self.__dict__[name] = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_pecan(self):
|
||||
"""Mock pecan's abort"""
|
||||
|
||||
mock_patch = mock.patch.object(pecan, 'abort', wraps=pecan.abort)
|
||||
self.mock_pecan_abort = mock_patch.start()
|
||||
self.addCleanup(mock_patch.stop)
|
||||
mock_patch_object = mock.patch.object(pecan, 'abort', wraps=pecan.abort)
|
||||
self.mock_pecan_abort = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_audit_rpc_client(self):
|
||||
"""Mock rpc's manager audit client"""
|
||||
|
||||
mock_patch = mock.patch.object(audit_rpc_client, 'ManagerAuditClient')
|
||||
self.mock_audit_rpc_client = mock_patch.start()
|
||||
self.addCleanup(mock_patch.stop)
|
||||
mock_patch_object = mock.patch.object(audit_rpc_client, 'ManagerAuditClient')
|
||||
self.mock_audit_rpc_client = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_rpc_client(self):
|
||||
"""Mock rpc's manager client"""
|
||||
|
||||
mock_patch = mock.patch.object(rpc_client, 'ManagerClient')
|
||||
self.mock_rpc_client = mock_patch.start()
|
||||
self.addCleanup(mock_patch.stop)
|
||||
mock_patch_object = mock.patch.object(rpc_client, 'ManagerClient')
|
||||
self.mock_rpc_client = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_rpc_subcloud_state_client(self):
|
||||
"""Mock rpc's subcloud state client"""
|
||||
|
||||
mock_patch = mock.patch.object(rpc_client, 'SubcloudStateClient')
|
||||
self.mock_rpc_subcloud_state_client = mock_patch.start()
|
||||
self.addCleanup(mock_patch.stop)
|
||||
mock_patch_object = mock.patch.object(rpc_client, 'SubcloudStateClient')
|
||||
self.mock_rpc_subcloud_state_client = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_openstack_driver(self, target):
|
||||
"""Mock the target's OpenStackDriver"""
|
||||
|
||||
mock_patch = mock.patch.object(target, 'OpenStackDriver')
|
||||
self.mock_openstack_driver = mock_patch.start()
|
||||
self.addCleanup(mock_patch.stop)
|
||||
mock_patch_object = mock.patch.object(target, 'OpenStackDriver')
|
||||
self.mock_openstack_driver = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_sysinv_client(self, target):
|
||||
"""Mock the target's SysinvClient"""
|
||||
|
||||
mock_patch = mock.patch.object(target, 'SysinvClient')
|
||||
self.mock_sysinv_client = mock_patch.start()
|
||||
self.addCleanup(mock_patch.stop)
|
||||
mock_patch_object = mock.patch.object(target, 'SysinvClient')
|
||||
self.mock_sysinv_client = mock_patch_object.start()
|
||||
self.addCleanup(mock_patch_object.stop)
|
||||
|
||||
def _mock_read_from_cache(self, target):
|
||||
mock_patch = mock.patch.object(target, '_read_from_cache')
|
||||
|
@ -0,0 +1,347 @@
|
||||
# Copyright (c) 2024 Wind River Systems, Inc.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
import copy
|
||||
import http.client
|
||||
|
||||
import mock
|
||||
|
||||
from dccommon import consts as dccommon_consts
|
||||
from dccommon.drivers.openstack.vim import SW_UPDATE_OPTS_CONST_DEFAULT
|
||||
from dcmanager.db import api as db_api
|
||||
from dcmanager.tests.base import FakeException
|
||||
from dcmanager.tests.unit.api.test_root_controller import DCManagerApiTest
|
||||
from dcmanager.tests.unit.common import fake_subcloud
|
||||
|
||||
|
||||
class SwUpdateOptionsMixin(object):
|
||||
"""Specifies common test cases between the different methods"""
|
||||
|
||||
def test_method_succeeds_with_subcloud_ref_as_default_region_name(self):
|
||||
"""Test method succeeds with subcloud ref as default region name"""
|
||||
|
||||
self.url = f"{self.url}/{dccommon_consts.DEFAULT_REGION_NAME}"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
self.mock_sw_update_opts_default.assert_called_once()
|
||||
|
||||
def test_method_succeeds_with_subcloud_ref_as_subcloud_id(self):
|
||||
"""Test method succeeds with subcloud ref as subcloud id"""
|
||||
|
||||
self.url = f"{self.url}/{self.subcloud.id}"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
self.mock_sw_update_opts.assert_called_once()
|
||||
|
||||
def test_method_fails_with_subcloud_id_not_found(self):
|
||||
"""Test method fails with subcloud id not found"""
|
||||
|
||||
self.url = f"{self.url}/9999"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_pecan_and_response(
|
||||
response, http.client.NOT_FOUND, "Subcloud not found"
|
||||
)
|
||||
self.mock_sw_update_opts.assert_not_called()
|
||||
|
||||
def test_method_succeeds_with_subcloud_ref_as_subcloud_name(self):
|
||||
"""Test method succeeds with subcloud ref as subcloud name"""
|
||||
|
||||
self.url = f"{self.url}/{self.subcloud.name}"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
self.mock_sw_update_opts.assert_called_once()
|
||||
|
||||
def test_method_fails_with_subcloud_name_not_found(self):
|
||||
"""Test method fails with subcloud name not found"""
|
||||
|
||||
self.url = f"{self.url}/fake"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_pecan_and_response(
|
||||
response, http.client.NOT_FOUND, "Subcloud not found"
|
||||
)
|
||||
self.mock_sw_update_opts.assert_not_called()
|
||||
|
||||
|
||||
class BaseTestSwUpdateOptionsController(DCManagerApiTest):
|
||||
"""Base class for testing the SwUpdateOptionsController"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.url = "/v1.0/sw-update-options"
|
||||
|
||||
self.subcloud = fake_subcloud.create_fake_subcloud(self.ctx)
|
||||
|
||||
self._mock_rpc_client()
|
||||
|
||||
def _get_sw_update_opts(self):
|
||||
return {
|
||||
"storage_apply_type": SW_UPDATE_OPTS_CONST_DEFAULT["storage-apply-type"],
|
||||
"worker_apply_type": SW_UPDATE_OPTS_CONST_DEFAULT["worker-apply-type"],
|
||||
"max_parallel_workers":
|
||||
SW_UPDATE_OPTS_CONST_DEFAULT["max-parallel-workers"],
|
||||
"alarm_restriction_type":
|
||||
SW_UPDATE_OPTS_CONST_DEFAULT["alarm-restriction-type"],
|
||||
"default_instance_action":
|
||||
SW_UPDATE_OPTS_CONST_DEFAULT["default-instance-action"]
|
||||
}
|
||||
|
||||
def _create_sw_update_opts(self):
|
||||
"""Creates a sw update options"""
|
||||
|
||||
db_api.sw_update_opts_create(
|
||||
self.ctx, self.subcloud.id, **self._get_sw_update_opts()
|
||||
)
|
||||
|
||||
def _create_sw_update_opts_default(self):
|
||||
"""Creates a sw update options for the default region name"""
|
||||
|
||||
db_api.sw_update_opts_default_create(self.ctx, **self._get_sw_update_opts())
|
||||
|
||||
|
||||
class TestSwUpdateOptionsController(BaseTestSwUpdateOptionsController):
|
||||
"""Test class for SwUpdateOptionsController"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def test_unmapped_method(self):
|
||||
"""Test requesting an unmapped method results in success with null content"""
|
||||
|
||||
self.method = self.app.put
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
self.assertEqual(response.text, "null")
|
||||
|
||||
|
||||
class TestSwUpdateOptionsGet(
|
||||
BaseTestSwUpdateOptionsController, SwUpdateOptionsMixin
|
||||
):
|
||||
"""Test class for get requests"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.method = self.app.get
|
||||
|
||||
self._create_sw_update_opts()
|
||||
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_get', 'mock_sw_update_opts',
|
||||
db_api.sw_update_opts_get
|
||||
)
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_default_get', 'mock_sw_update_opts_default',
|
||||
db_api.sw_update_opts_default_get
|
||||
)
|
||||
|
||||
def test_get_succeeds_without_subcloud_ref(self):
|
||||
"""Test get succeeds without subcloud ref"""
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
|
||||
def test_get_succeeds_without_subcloud_ref_and_sw_update_opts(self):
|
||||
"""Test get succeeds without subcloud ref and sw update opts"""
|
||||
|
||||
db_api.sw_update_opts_destroy(self.ctx, self.subcloud.id)
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
|
||||
def test_get_fails_with_generic_exception_without_sw_update_options(self):
|
||||
"""Test get fails with generic exception without sw update options
|
||||
|
||||
When performing a get request with subcloud ref and without sw update
|
||||
options, a SubcloudPatchOptsNotFound exception is raised, resulting in a
|
||||
generic exception
|
||||
"""
|
||||
|
||||
self.url = f"{self.url}/{self.subcloud.name}"
|
||||
|
||||
db_api.sw_update_opts_destroy(self.ctx, self.subcloud.id)
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_pecan_and_response(
|
||||
response, http.client.NOT_FOUND, "No options found for Subcloud with id "
|
||||
f"{self.subcloud.id}, defaults will be used."
|
||||
)
|
||||
|
||||
|
||||
class BaseTestSwUpdateOptionsPost(BaseTestSwUpdateOptionsController):
|
||||
"""Base test class for post requests"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.method = self.app.post_json
|
||||
self.params = copy.copy(SW_UPDATE_OPTS_CONST_DEFAULT)
|
||||
del self.params["created-at"]
|
||||
del self.params["updated-at"]
|
||||
|
||||
|
||||
class TestSwUpdateOptionsPost(BaseTestSwUpdateOptionsPost):
|
||||
"""Test class for post requests"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
def test_post_fails_without_payload(self):
|
||||
"""Test post fails without payload"""
|
||||
|
||||
self.params = {}
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_pecan_and_response(
|
||||
response, http.client.BAD_REQUEST, "Body required"
|
||||
)
|
||||
|
||||
|
||||
class TestSwUpdateOptionsPostUpdate(
|
||||
BaseTestSwUpdateOptionsPost, SwUpdateOptionsMixin
|
||||
):
|
||||
"""Test class for post requests to update sw_update_opts
|
||||
|
||||
When a post request is performed for an existing sw_update_opts, it's updated.
|
||||
Otherwise, a new one is created.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self._create_sw_update_opts()
|
||||
self._create_sw_update_opts_default()
|
||||
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_update', 'mock_sw_update_opts',
|
||||
db_api.sw_update_opts_update
|
||||
)
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_default_update', 'mock_sw_update_opts_default',
|
||||
db_api.sw_update_opts_default_update
|
||||
)
|
||||
|
||||
@mock.patch.object(db_api, "sw_update_opts_default_update")
|
||||
def test_post_update_fails_in_default_region_with_db_api_generic_exception(
|
||||
self, mock_db_api
|
||||
):
|
||||
"""Test post update fails in default region with db api generic exception"""
|
||||
|
||||
self.url = f"{self.url}/{dccommon_consts.DEFAULT_REGION_NAME}"
|
||||
|
||||
mock_db_api.side_effect = FakeException()
|
||||
|
||||
self.assertRaises(
|
||||
FakeException, self.method, self.url, headers=self.headers,
|
||||
params=self.params
|
||||
)
|
||||
|
||||
|
||||
class TestSwUpdateOptionsPostCreate(
|
||||
BaseTestSwUpdateOptionsPost, SwUpdateOptionsMixin
|
||||
):
|
||||
"""Test class for post requests to create sw_update_opts
|
||||
|
||||
When a post request is performed for an existing sw_update_opts, it's updated.
|
||||
Otherwise, a new one is created.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_create', 'mock_sw_update_opts',
|
||||
db_api.sw_update_opts_create
|
||||
)
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_default_create', 'mock_sw_update_opts_default',
|
||||
db_api.sw_update_opts_default_create
|
||||
)
|
||||
|
||||
@mock.patch.object(db_api, "sw_update_opts_default_create")
|
||||
def test_post_create_fails_in_default_region_with_db_api_generic_exception(
|
||||
self, mock_db_api
|
||||
):
|
||||
"""Test post create fails in default region with db api generic exception"""
|
||||
|
||||
if db_api.sw_update_opts_default_get(self.ctx) is not None:
|
||||
db_api.sw_update_opts_default_destroy(self.ctx)
|
||||
|
||||
self.url = f"{self.url}/{dccommon_consts.DEFAULT_REGION_NAME}"
|
||||
|
||||
mock_db_api.side_effect = FakeException()
|
||||
|
||||
self.assertRaises(
|
||||
FakeException, self.method, self.url, headers=self.headers,
|
||||
params=self.params
|
||||
)
|
||||
|
||||
|
||||
class TestSwUpdateOptionsDelete(
|
||||
BaseTestSwUpdateOptionsController, SwUpdateOptionsMixin
|
||||
):
|
||||
"""Test class for delete requests"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
|
||||
self.method = self.app.delete
|
||||
|
||||
self._create_sw_update_opts()
|
||||
self._create_sw_update_opts_default()
|
||||
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_destroy', 'mock_sw_update_opts',
|
||||
db_api.sw_update_opts_destroy
|
||||
)
|
||||
self._mock_object(
|
||||
db_api, 'sw_update_opts_default_destroy', 'mock_sw_update_opts_default',
|
||||
db_api.sw_update_opts_default_destroy
|
||||
)
|
||||
|
||||
def test_delete_succeeds_with_generic_exception_for_default_region_name(self):
|
||||
"""Test delete succeeds with generic exception for default region name
|
||||
|
||||
When a delete request is made for the default region name and there isn't a
|
||||
sw_update_opts_default object in the database, a generic exception is catched
|
||||
and the execution returnns
|
||||
"""
|
||||
|
||||
db_api.sw_update_opts_default_destroy(self.ctx)
|
||||
|
||||
self.url = f"{self.url}/{dccommon_consts.DEFAULT_REGION_NAME}"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_response(response)
|
||||
|
||||
def test_delete_fails_without_sw_update_opts_to_delete(self):
|
||||
"""Test delete fails without sw update opts to delete"""
|
||||
|
||||
db_api.sw_update_opts_destroy(self.ctx, self.subcloud.id)
|
||||
|
||||
self.url = f"{self.url}/{self.subcloud.id}"
|
||||
|
||||
response = self._send_request()
|
||||
|
||||
self._assert_pecan_and_response(
|
||||
response, http.client.NOT_FOUND, "Subcloud patch options not found"
|
||||
)
|
Loading…
Reference in New Issue
Block a user