From 70c9212152d3d1225f79ca30e62591ebd75e1518 Mon Sep 17 00:00:00 2001 From: Akash Chandra Date: Thu, 23 Jan 2025 19:22:34 +0530 Subject: [PATCH] feat: added UT to cover the scenarios --- lib/live_cluster/manage_controller.py | 19 ++- .../live_cluster/test_manage_controller.py | 117 +++++++++++++++++- 2 files changed, 129 insertions(+), 7 deletions(-) diff --git a/lib/live_cluster/manage_controller.py b/lib/live_cluster/manage_controller.py index 18cfd78a..a695224f 100644 --- a/lib/live_cluster/manage_controller.py +++ b/lib/live_cluster/manage_controller.py @@ -2755,8 +2755,15 @@ async def _check_ns_is_strong_consistency(self, ns): """ try: namespace_stats = await self.cluster.info_namespace_statistics(ns, nodes='all') + if isinstance(namespace_stats, Exception): + raise namespace_stats + + if not namespace_stats: + logger.error("namespace {} not found".format(ns)) + return False + namespace_stats = list(namespace_stats.values())[0] if len(namespace_stats.values()) > 0 else None - if not namespace_stats or namespace_stats is None: + if not namespace_stats: logger.error("namespace {} not does not exist".format(ns)) return False @@ -2767,7 +2774,7 @@ async def _check_ns_is_strong_consistency(self, ns): except Exception as e: logger.error("Error while checking namespace strong consistency mode: {}".format(e)) - raise ASInfoError("Error while checking namespace strong consistency mode", e) + raise e return strong_consistency @@ -2814,7 +2821,7 @@ async def _do_default(self, line): # to be run against a SC namespace only ns_strong_consistency = await self._check_ns_is_strong_consistency(ns) - if isinstance(ns_strong_consistency, ASInfoError): + if isinstance(ns_strong_consistency, Exception): return elif not ns_strong_consistency: return @@ -2896,7 +2903,7 @@ async def _do_default(self, line): # to be run against a SC namespace only ns_strong_consistency = await self._check_ns_is_strong_consistency(ns) - if isinstance(ns_strong_consistency, ASInfoError): + if isinstance(ns_strong_consistency, Exception): return elif not ns_strong_consistency: return @@ -2996,7 +3003,7 @@ async def _do_default(self, line): # to be run against a SC namespace only ns_strong_consistency = await self._check_ns_is_strong_consistency(ns) - if isinstance(ns_strong_consistency, ASInfoError): + if isinstance(ns_strong_consistency, Exception): return elif not ns_strong_consistency: return @@ -3061,7 +3068,7 @@ async def _do_default(self, line): # to be run against a SC namespace only ns_strong_consistency = await self._check_ns_is_strong_consistency(ns) - if isinstance(ns_strong_consistency, ASInfoError): + if isinstance(ns_strong_consistency, Exception): return elif not ns_strong_consistency: return diff --git a/test/unit/live_cluster/test_manage_controller.py b/test/unit/live_cluster/test_manage_controller.py index 67d25b38..1f923bce 100644 --- a/test/unit/live_cluster/test_manage_controller.py +++ b/test/unit/live_cluster/test_manage_controller.py @@ -27,7 +27,7 @@ from lib.live_cluster.client import ( ASINFO_RESPONSE_OK, - ASInfoClusterStableError, + ASInfoError, ASInfoResponseError, ASProtocolError, ASResponse, @@ -2712,6 +2712,16 @@ def __init__(self, input, output): self.logger_mock.warning.assert_called_with( "The cluster is unstable. It is advised that you do not manage the roster. Run 'info network' for more information." ) + + self.assertRaises( + ASInfoError, + self.controller._check_and_log_cluster_stable, + { + "1.1.1.1": "ABC", + "2.2.2.2": "ABC", + "3.3.3.3": ASInfoError("", "foo"), + }, + ) def test_check_and_log_nodes_in_observed(self): class test_case: @@ -2813,6 +2823,7 @@ async def test_logs_error_from_roster(self): self.logger_mock.error.assert_called_once_with(error) self.cluster_mock.info_roster_set.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() self.view_mock.print_result.assert_not_called() async def test_logs_error_from_roster_set(self): @@ -2830,8 +2841,91 @@ async def test_logs_error_from_roster_set(self): self.logger_mock.error.assert_called_once_with(error) self.cluster_mock.info_roster_set.assert_called_once() + self.cluster_mock.info_namespace_statistics.assert_called_once() + self.view_mock.print_result.assert_not_called() + + async def test_raises_warn_from_none_ns(self): + line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" + self.cluster_mock.info_namespace_statistics.return_value = None + + await self.controller.execute(line.split()) + + self.logger_mock.error.assert_called() + self.cluster_mock.info_namespace_statistics.assert_called() + self.cluster_mock.info_roster_set.assert_not_called() + self.view_mock.print_result.assert_not_called() + self.logger_mock.error.assert_any_call( + 'namespace test not found' + ) + + async def test_raises_warn_from_invalid_ns(self): + error = Exception("test exception") + line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" + self.cluster_mock.info_roster.return_value = {"1.1.1.1": {}} + self.cluster_mock.info_namespace_statistics.return_value = {"1.1.1.1": {}} + + await self.controller.execute(line.split()) + + self.logger_mock.error.assert_called() + self.cluster_mock.info_namespace_statistics.assert_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() + self.cluster_mock.info_roster_set.assert_not_called() + self.view_mock.print_result.assert_not_called() + self.logger_mock.error.assert_any_call( + 'namespace test not does not exist' + ) + + async def test_raises_warn_from_ap_ns(self): + error = Exception("test exception") + line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" + self.cluster_mock.info_roster.return_value = {"1.1.1.1": {}} + self.cluster_mock.info_namespace_statistics.return_value = { + "1.1.1.1": { "strong-consistency": "false" } + } + + await self.controller.execute(line.split()) + + self.logger_mock.error.assert_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() + self.cluster_mock.info_roster_set.assert_not_called() + self.view_mock.print_result.assert_not_called() + self.logger_mock.error.assert_any_call( + 'namespace test is not in strong consistency mode' + ) + + async def test_raises_warn_from_malformed_ns(self): + error = Exception("test exception") + line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" + self.cluster_mock.info_roster.return_value = {"1.1.1.1": {}} + self.cluster_mock.info_namespace_statistics.return_value = { + "1.1.1.1": { "disable-mrt-writes": "false" } + } + + await self.controller.execute(line.split()) + + self.logger_mock.error.assert_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() + self.cluster_mock.info_roster_set.assert_not_called() + self.view_mock.print_result.assert_not_called() + self.logger_mock.error.assert_any_call( + 'namespace test is not in strong consistency mode' + ) + + async def test_raises_error_from_ns_stats(self): + error = Exception("test exception") + line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" + self.cluster_mock.info_roster.return_value = {"1.1.1.1": error} + self.cluster_mock.info_namespace_statistics.return_value = error + + await test_util.assert_exception_async( + self, Exception, "test exception", self.controller.execute, line.split() + ) + self.logger_mock.error.assert_called() + self.cluster_mock.info_roster_set.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() self.view_mock.print_result.assert_not_called() + async def test_raises_error_from_roster(self): error = Exception("test exception") line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" @@ -2862,6 +2956,7 @@ async def test_raises_error_from_roster_set(self): self, Exception, "test exception", self.controller.execute, line.split() ) self.logger_mock.error.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() self.cluster_mock.info_roster_set.assert_called_once() self.view_mock.print_result.assert_not_called() @@ -2889,6 +2984,7 @@ async def test_warn_returns_false(self): "You are about to set the pending-roster for namespace test to: GHI, ABC@rack1, DEF@rack2" ) self.cluster_mock.info_roster_set.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_warn_returns_true(self): line = "nodes ABC@rack1 DEF@rack2 ns test" @@ -2915,6 +3011,7 @@ async def test_warn_returns_true(self): "You are about to set the pending-roster for namespace test to: GHI, ABC@rack1, DEF@rack2" ) self.cluster_mock.info_roster_set.assert_called_once() + self.cluster_mock.info_namespace_statistics.assert_called_once() @asynctest.fail_on(active_handles=True) @@ -2957,6 +3054,7 @@ async def test_success(self): self.view_mock.print_result.assert_any_call( 'Run "manage recluster" for your changes to take effect.' ) + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_logs_error_from_roster(self): line = "nodes ABC@rack1 DEF@rack2 ns test --no-warn" @@ -2971,6 +3069,7 @@ async def test_logs_error_from_roster(self): self.logger_mock.error.assert_called_once_with(error) self.cluster_mock.info_roster_set.assert_not_called() self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_logs_error_from_roster_set(self): error = ASInfoResponseError("blah", "error::foo") @@ -2991,6 +3090,7 @@ async def test_logs_error_from_roster_set(self): self.logger_mock.error.assert_called_once_with(error) self.cluster_mock.info_roster_set.assert_called_once() self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_logs_error_when_node_not_in_pending(self): line = "nodes GHI ns test" @@ -3014,6 +3114,7 @@ async def test_logs_error_when_node_not_in_pending(self): ) self.cluster_mock.info_roster_set.assert_not_called() self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_raises_error_from_roster(self): error = Exception("test exception") @@ -3027,6 +3128,7 @@ async def test_raises_error_from_roster(self): self, Exception, "test exception", self.controller.execute, line.split() ) self.logger_mock.error.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() self.cluster_mock.info_roster_set.assert_not_called() self.view_mock.print_result.assert_not_called() @@ -3048,6 +3150,7 @@ async def test_raises_error_from_roster_set(self): self, Exception, "test exception", self.controller.execute, line.split() ) self.logger_mock.error.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() self.cluster_mock.info_roster_set.assert_called_once() self.view_mock.print_result.assert_not_called() @@ -3074,6 +3177,7 @@ async def test_warn_returns_false(self): self.check_and_log_cluster_stable_mock.assert_called_once_with( {"1.1.1.1": "ABCDEF"} ) + self.cluster_mock.info_namespace_statistics.assert_called_once() self.cluster_mock.info_roster_set.assert_not_called() async def test_warn_returns_true(self): @@ -3101,6 +3205,7 @@ async def test_warn_returns_true(self): {"1.1.1.1": "ABCDEF"} ) self.cluster_mock.info_roster_set.assert_called_once() + self.cluster_mock.info_namespace_statistics.assert_called_once() @asynctest.fail_on(active_handles=True) @@ -3144,6 +3249,7 @@ async def test_success(self): self.view_mock.print_result.assert_any_call( 'Run "manage recluster" for your changes to take effect.' ) + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_logs_error_from_roster_set(self): line = "ABC@rack1 DEF@rack2 ns test --no-warn" @@ -3159,6 +3265,7 @@ async def test_logs_error_from_roster_set(self): "test", ["ABC@rack1", "DEF@rack2"], nodes="principal" ) + self.cluster_mock.info_namespace_statistics.assert_called_once() self.logger_mock.error.assert_called_once_with(error) self.view_mock.print_result.assert_not_called() @@ -3185,6 +3292,7 @@ async def test_raises_error_from_roster_set(self): ) self.logger_mock.error.assert_not_called() self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_warn_returns_false(self): line = "ABC@rack1 DEF@rack2 ns test" @@ -3212,6 +3320,7 @@ async def test_warn_returns_false(self): [], ["ABC@rack1", "DEF@rack2"] ) self.cluster_mock.info_roster_set.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_warn_returns_true(self): line = "ABC@rack1 DEF@rack2 ns test" @@ -3240,6 +3349,7 @@ async def test_warn_returns_true(self): "You are about to set the pending-roster for namespace test to: ABC@rack1, DEF@rack2" ) self.cluster_mock.info_roster_set.assert_called_once() + self.cluster_mock.info_namespace_statistics.assert_called_once() @asynctest.fail_on(active_handles=True) @@ -3299,6 +3409,7 @@ async def test_logs_error_from_roster(self): self.logger_mock.error.assert_called_once_with(error) self.cluster_mock.info_roster_set.assert_not_called() self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_logs_error_from_roster_set(self): line = "ns test" @@ -3318,6 +3429,7 @@ async def test_logs_error_from_roster_set(self): ) self.logger_mock.error.assert_called_once_with(error) self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_raises_error_from_roster(self): error = Exception("test exception") @@ -3333,6 +3445,7 @@ async def test_raises_error_from_roster(self): self.logger_mock.error.assert_not_called() self.cluster_mock.info_roster_set.assert_not_called() self.view_mock.print_result.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_raises_error_from_roster_set(self): error = Exception("test exception") @@ -3373,6 +3486,7 @@ async def test_warn_returns_false(self): "You are about to set the pending-roster for namespace test to: GHI, ABC@rack1, DEF@rack2" ) self.cluster_mock.info_roster_set.assert_not_called() + self.cluster_mock.info_namespace_statistics.assert_called_once() async def test_warn_returns_true(self): line = "nodes ABC@rack1 DEF@rack2 ns test" @@ -3397,3 +3511,4 @@ async def test_warn_returns_true(self): ) self.cluster_mock.info_roster_set.assert_called_once() self.cluster_mock.info_namespace_statistics.assert_called_once() +