Files
krkn/tests/test_time_actions_scenario_plugin.py
T
Nitesh SinghandPaige Patton 53a81e8e90 feat: Add exec_with_shell_fallback method and fix failing unit tests (#1225)
- Add exec_with_shell_fallback method with retry logic and shell fallback
- Add unit tests for the new method with proper mocking
- All tests now pass as expected

Signed-off-by: NITESH SINGH <niteshkumar121411@gmail.com>
Co-authored-by: Paige Patton <64206430+paigerube14@users.noreply.github.com>
2026-06-04 16:38:15 -04:00

171 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""
Test suite for TimeActionsScenarioPlugin class
Usage:
python -m coverage run -a -m unittest tests/test_time_actions_scenario_plugin.py -v
Assisted By: Claude Code
"""
import unittest
from unittest.mock import MagicMock, patch
from krkn_lib.k8s import KrknKubernetes
from krkn_lib.telemetry.ocp import KrknTelemetryOpenshift
from krkn.scenario_plugins.time_actions.time_actions_scenario_plugin import TimeActionsScenarioPlugin
class TestTimeActionsScenarioPlugin(unittest.TestCase):
def setUp(self):
"""
Set up test fixtures for TimeActionsScenarioPlugin
"""
self.plugin = TimeActionsScenarioPlugin()
def tearDown(self):
"""Clean up after each test to prevent state leakage"""
self.plugin = None
def test_get_scenario_types(self):
"""
Test get_scenario_types returns correct scenario type
"""
result = self.plugin.get_scenario_types()
self.assertEqual(result, ["time_scenarios"])
self.assertEqual(len(result), 1)
@patch("krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.logging")
@patch("builtins.open", side_effect=RuntimeError("disk quota exceeded"))
def test_exception_variable_bound_in_except_handler(self, mock_open, mock_logging):
"""run() must bind exception variable so logging shows actual error, not NameError"""
result = self.plugin.run(
run_uuid="test-uuid",
scenario="fake_scenario.yaml",
lib_telemetry=MagicMock(),
scenario_telemetry=MagicMock(),
)
self.assertEqual(result, 1)
logged_msg = mock_logging.error.call_args[0][0]
self.assertIn("disk quota exceeded", logged_msg)
self.assertNotIn("NameError", logged_msg)
@unittest.mock.patch('builtins.open', create=True)
@unittest.mock.patch('yaml.safe_load')
@unittest.mock.patch('logging.error')
def test_run_exception_handling_with_variable(self, mock_logging_error, mock_yaml, mock_open):
"""
Test that run() properly captures exception variable and logs it
This tests the fix for the undefined variable 'e' bug
"""
# Setup mock to raise exception
mock_yaml.side_effect = RuntimeError("Test exception message")
mock_lib_telemetry = MagicMock()
mock_scenario_telemetry = MagicMock()
# Execute the run method
result = self.plugin.run(
run_uuid="test-uuid",
scenario="test_scenario.yaml",
lib_telemetry=mock_lib_telemetry,
scenario_telemetry=mock_scenario_telemetry
)
# Assert failure is returned
self.assertEqual(result, 1)
# Assert logging.error was called with the exception message
mock_logging_error.assert_called_once()
error_call_args = str(mock_logging_error.call_args)
self.assertIn("Test exception message", error_call_args)
self.assertIn("TimeActionsScenarioPlugin", error_call_args)
@unittest.mock.patch('builtins.open', create=True)
@unittest.mock.patch('yaml.safe_load')
def test_run_with_skew_time_exception(self, mock_yaml, mock_open):
"""
Test that run() handles exceptions from skew_time method
"""
# Setup mock scenario config
mock_yaml.return_value = {
"time_scenarios": [
{
"action": "skew_time",
"object_type": "node",
"object_name": ["test-node"]
}
]
}
mock_lib_telemetry = MagicMock()
mock_kubecli = MagicMock()
mock_lib_telemetry.get_lib_kubernetes.return_value = mock_kubecli
# Make skew_time raise an exception
with unittest.mock.patch.object(self.plugin, 'skew_time', side_effect=Exception("Skew failed")):
mock_scenario_telemetry = MagicMock()
# Execute the run method
result = self.plugin.run(
run_uuid="test-uuid",
scenario="test_scenario.yaml",
lib_telemetry=mock_lib_telemetry,
scenario_telemetry=mock_scenario_telemetry
)
# Assert failure is returned
self.assertEqual(result, 1)
@patch('krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.KrknKubernetes')
def test_exec_with_shell_fallback_detects_persistent_shell_error(self, mock_kubecli_class):
"""Test that exec_with_shell_fallback returns False when both direct and shell execution fail persistently"""
mock_kubecli = MagicMock()
mock_kubecli_class.return_value = mock_kubecli
mock_kubecli.exec_cmd_in_pod.side_effect = Exception("Command failed")
result = self.plugin.exec_with_shell_fallback(
"test", "test-pod", "default", "test-container", mock_kubecli
)
self.assertFalse(result)
@patch('krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.KrknKubernetes')
def test_exec_with_shell_fallback_fails_after_max_retries(self, mock_kubecli_class):
"""Test that exec_with_shell_fallback returns False after exhausting all retries"""
mock_kubecli = MagicMock()
mock_kubecli_class.return_value = mock_kubecli
mock_kubecli.exec_cmd_in_pod.side_effect = Exception("Command failed")
result = self.plugin.exec_with_shell_fallback(
"test", "test-pod", "default", "test-container", mock_kubecli, max_retries=2
)
self.assertFalse(result)
@patch('krkn.scenario_plugins.time_actions.time_actions_scenario_plugin.KrknKubernetes')
def test_exec_with_shell_fallback_retries_on_error(self, mock_kubecli_class):
"""Test that exec_with_shell_fallback succeeds after retrying"""
mock_kubecli = MagicMock()
mock_kubecli_class.return_value = mock_kubecli
# First two calls fail, third succeeds
mock_kubecli.exec_cmd_in_pod.side_effect = [
Exception("First failure"),
Exception("Second failure"),
"success"
]
result = self.plugin.exec_with_shell_fallback(
"test", "test-pod", "default", "test-container", mock_kubecli, max_retries=3
)
self.assertEqual(result, "success")
if __name__ == "__main__":
unittest.main()