Merge "Improve unit test coverage for dcmanager's APIs (sw_update_options)"

This commit is contained in:
Zuul 2024-04-16 21:10:29 +00:00 committed by Gerrit Code Review
commit 9493673a68
2 changed files with 373 additions and 18 deletions

View File

@ -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')

View File

@ -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"
)