Improve unit test coverage for dcmanager's orchestrator/states/software

Improves unit test coverage for dcmanager's orchestrator/states/software
functionality from 77% to 100%.

Test plan:
  All of the tests were created taking into account the
output of 'tox -c tox.ini -e cover' command

Story: 2010676
Task: 49313

Change-Id: Id3de824406df4d4c6c6504aa07e95dc457e7c2df
Signed-off-by: rlima <Raphael.Lima@windriver.com>
This commit is contained in:
rlima 2023-12-11 18:23:50 -03:00 committed by Raphael Lima
parent f6aa5ac912
commit d937955c19
3 changed files with 146 additions and 25 deletions

View File

@ -198,6 +198,11 @@ class DCManagerTestCase(base.BaseTestCase):
self.mock_sysinv_client = mock_patch.start() self.mock_sysinv_client = mock_patch.start()
self.addCleanup(mock_patch.stop) self.addCleanup(mock_patch.stop)
def _mock_read_from_cache(self, target):
mock_patch = mock.patch.object(target, '_read_from_cache')
self.mock_read_from_cache = mock_patch.start()
self.addCleanup(mock_patch.stop)
def _mock_get_network_address_pool(self): def _mock_get_network_address_pool(self):
"""Mock phased subcloud deploy's get_network_address_pool""" """Mock phased subcloud deploy's get_network_address_pool"""

View File

@ -3,38 +3,66 @@
# #
# SPDX-License-Identifier: Apache-2.0 # SPDX-License-Identifier: Apache-2.0
# #
import mock import mock
from dcmanager.common import consts from dcmanager.common import consts
from dcmanager.orchestrator.states.base import BaseState
from dcmanager.orchestrator.states.software.finish_strategy import \ from dcmanager.orchestrator.states.software.finish_strategy import \
FinishStrategyState FinishStrategyState
from dcmanager.tests.unit.orchestrator.states.software.test_base import \ from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator TestSoftwareOrchestrator
REGION_ONE_RELEASES = {"DC.1": {"sw_version": "20.12", REGION_ONE_RELEASES = {
"state": "committed"}, "DC.1": {
"DC.2": {"sw_version": "20.12", "sw_version": "20.12",
"state": "committed"}, "state": "committed"
"DC.3": {"sw_version": "20.12", },
"state": "committed"}, "DC.2": {
"DC.8": {"sw_version": "20.12", "sw_version": "20.12",
"state": "committed"}} "state": "committed"
},
"DC.3": {
"sw_version": "20.12",
"state": "committed"
},
"DC.8": {
"sw_version": "20.12",
"state": "committed"
}
}
SUBCLOUD_RELEASES = {"DC.1": {"sw_version": "20.12", SUBCLOUD_RELEASES = {
"state": "committed"}, "DC.1": {
"DC.2": {"sw_version": "20.12", "sw_version": "20.12",
"state": "committed"}, "state": "committed"
"DC.3": {"sw_version": "20.12", },
"state": "deployed"}, "DC.2": {
"DC.9": {"sw_version": "20.12", "sw_version": "20.12",
"state": "available"}} "state": "committed"
},
"DC.3": {
"sw_version": "20.12",
"state": "deployed"
},
"DC.9": {
"sw_version": "20.12",
"state": "available"
},
"DC.10": {
"sw_version": "20.12",
"state": "deployed"
}
}
class TestFinishStrategyState(TestSoftwareOrchestrator): class TestFinishStrategyState(TestSoftwareOrchestrator):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self._mock_read_from_cache(FinishStrategyState)
self.on_success_state = consts.STRATEGY_STATE_COMPLETE self.on_success_state = consts.STRATEGY_STATE_COMPLETE
# Add the subcloud being processed by this unit test # Add the subcloud being processed by this unit test
@ -51,10 +79,10 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
self.software_client.commit_patch = mock.MagicMock() self.software_client.commit_patch = mock.MagicMock()
self._read_from_cache = mock.MagicMock() self._read_from_cache = mock.MagicMock()
@mock.patch.object(FinishStrategyState, '_read_from_cache') def test_finish_strategy_success(self):
def test_finish_strategy_success(self, mock_read_from_cache):
"""Test software finish strategy when the API call succeeds.""" """Test software finish strategy when the API call succeeds."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = [SUBCLOUD_RELEASES] self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
@ -71,10 +99,10 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
self.assert_step_updated(self.strategy_step.subcloud_id, self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state) self.on_success_state)
@mock.patch.object(FinishStrategyState, '_read_from_cache') def test_finish_strategy_no_operation_required(self):
def test_finish_strategy_no_operation_required(self, mock_read_from_cache):
"""Test software finish strategy when no operation is required.""" """Test software finish strategy when no operation is required."""
mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = [REGION_ONE_RELEASES] self.software_client.query.side_effect = [REGION_ONE_RELEASES]
@ -88,3 +116,62 @@ class TestFinishStrategyState(TestSoftwareOrchestrator):
# On success, the state should transition to the next state # On success, the state should transition to the next state
self.assert_step_updated(self.strategy_step.subcloud_id, self.assert_step_updated(self.strategy_step.subcloud_id,
self.on_success_state) self.on_success_state)
def test_finish_strategy_fails_when_query_exception(self):
"""Test finish strategy fails when software client query raises exception"""
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = Exception()
self.worker.perform_state_action(self.strategy_step)
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)
def test_finish_strategy_fails_when_delete_exception(self):
"""Test finish strategy fails when software client delete raises exception"""
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
self.software_client.delete.side_effect = Exception()
self.worker.perform_state_action(self.strategy_step)
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)
@mock.patch.object(BaseState, 'stopped')
def test_finish_strategy_fails_when_stopped(self, mock_base_stopped):
"""Test finish strategy fails when stopped"""
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
mock_base_stopped.return_value = True
self.worker.perform_state_action(self.strategy_step)
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)
def test_finish_strategy_fails_when_commit_patch_exception(self):
"""Test finish strategy fails when software client commit_patch
raises exception
"""
self.mock_read_from_cache.return_value = REGION_ONE_RELEASES
self.software_client.query.side_effect = [SUBCLOUD_RELEASES]
self.software_client.commit_patch.side_effect = Exception()
self.worker.perform_state_action(self.strategy_step)
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)

View File

@ -6,7 +6,9 @@
import mock import mock
from dccommon import consts as dccommon_consts
from dcmanager.common import consts from dcmanager.common import consts
from dcmanager.db import api as db_api
from dcmanager.tests.unit.orchestrator.states.software.test_base import \ from dcmanager.tests.unit.orchestrator.states.software.test_base import \
TestSoftwareOrchestrator TestSoftwareOrchestrator
@ -15,6 +17,8 @@ MISSING_LICENSE_RESPONSE = {
"error": "License file not found. A license may not have been installed.", "error": "License file not found. A license may not have been installed.",
} }
GENERIC_ERROR_RESPONSE = {"content": "", "error": "Invalid license"}
LICENSE_VALID_RESPONSE = {"content": "A valid license", "error": ""} LICENSE_VALID_RESPONSE = {"content": "A valid license", "error": ""}
ALTERNATE_LICENSE_RESPONSE = {"content": "A different valid license", "error": ""} ALTERNATE_LICENSE_RESPONSE = {"content": "A different valid license", "error": ""}
@ -69,7 +73,7 @@ class TestInstallLicenseState(TestSoftwareOrchestrator):
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
) )
def test_install_license_success(self): def test_install_license_succeeds(self):
"""Test the install license step succeeds. """Test the install license step succeeds.
The license will be installed on the subcloud when system controller The license will be installed on the subcloud when system controller
@ -147,8 +151,8 @@ class TestInstallLicenseState(TestSoftwareOrchestrator):
self.strategy_step.subcloud_id, self.on_success_state self.strategy_step.subcloud_id, self.on_success_state
) )
def test_install_license_skip_when_no_sys_controller_lic(self): def test_install_license_skips_with_sys_controller_without_license(self):
"""Test license install skipped when no license on system controller.""" """Test license install skips when sys controller doesn't have a license"""
# Only makes one query: to system controller # Only makes one query: to system controller
self.sysinv_client.get_license.return_value = MISSING_LICENSE_RESPONSE self.sysinv_client.get_license.return_value = MISSING_LICENSE_RESPONSE
@ -163,3 +167,28 @@ class TestInstallLicenseState(TestSoftwareOrchestrator):
self.assert_step_updated( self.assert_step_updated(
self.strategy_step.subcloud_id, self.on_success_state self.strategy_step.subcloud_id, self.on_success_state
) )
def test_install_license_fails_with_generic_error_response(self):
"""Test license install fails with generic error response"""
# Only makes one query: to system controller
self.sysinv_client.get_license.return_value = GENERIC_ERROR_RESPONSE
# invoke the strategy state operation on the orch thread
self.worker.perform_state_action(self.strategy_step)
subcloud = db_api.subcloud_get(self.ctx, self.subcloud.id)
self.assertEqual(
subcloud.error_description, "An unexpected error occurred querying the "
f"license {dccommon_consts.SYSTEM_CONTROLLER_NAME}. "
f"Detail: {GENERIC_ERROR_RESPONSE['error']}"
)
# Should skip install_license API call
self.sysinv_client.install_license.assert_not_called()
# Verify it successfully moves to the next step
self.assert_step_updated(
self.strategy_step.subcloud_id, consts.STRATEGY_STATE_FAILED
)