"""Tests for controlflow.""" import unittest from unittest.mock import patch from gam import controlflow class ControlFlowTest(unittest.TestCase): def test_system_error_exit_raises_systemexit_error(self): with self.assertRaises(SystemExit): controlflow.system_error_exit(1, 'exit message') def test_system_error_exit_raises_systemexit_with_return_code(self): with self.assertRaises(SystemExit) as context_manager: controlflow.system_error_exit(100, 'exit message') self.assertEqual(context_manager.exception.code, 100) @patch.object(controlflow.display, 'print_error') def test_system_error_exit_prints_error_before_exiting( self, mock_print_err): with self.assertRaises(SystemExit): controlflow.system_error_exit(100, 'exit message') self.assertIn('exit message', mock_print_err.call_args[0][0]) def test_csv_field_error_exit_raises_systemexit_error(self): with self.assertRaises(SystemExit): controlflow.csv_field_error_exit('aField', ['unusedField1', 'unusedField2']) def test_csv_field_error_exit_exits_code_2(self): with self.assertRaises(SystemExit) as context_manager: controlflow.csv_field_error_exit('aField', ['unusedField1', 'unusedField2']) self.assertEqual(context_manager.exception.code, 2) @patch.object(controlflow.display, 'print_error') def test_csv_field_error_exit_prints_error_details(self, mock_print_err): with self.assertRaises(SystemExit): controlflow.csv_field_error_exit('aField', ['unusedField1', 'unusedField2']) printed_message = mock_print_err.call_args[0][0] self.assertIn('aField', printed_message) self.assertIn('unusedField1', printed_message) self.assertIn('unusedField2', printed_message) def test_invalid_json_exit_raises_systemexit_error(self): with self.assertRaises(SystemExit): controlflow.invalid_json_exit('filename') def test_invalid_json_exit_exit_exits_code_17(self): with self.assertRaises(SystemExit) as context_manager: controlflow.invalid_json_exit('filename') self.assertEqual(context_manager.exception.code, 17) @patch.object(controlflow.display, 'print_error') def test_invalid_json_exit_prints_error_details(self, mock_print_err): with self.assertRaises(SystemExit): controlflow.invalid_json_exit('filename') printed_message = mock_print_err.call_args[0][0] self.assertIn('filename', printed_message) @patch.object(controlflow.time, 'sleep') def test_wait_on_failure_waits_exponentially(self, mock_sleep): controlflow.wait_on_failure(1, 5, 'Backoff attempt #1') controlflow.wait_on_failure(2, 5, 'Backoff attempt #2') controlflow.wait_on_failure(3, 5, 'Backoff attempt #3') sleep_calls = mock_sleep.call_args_list self.assertGreaterEqual(sleep_calls[0][0][0], 2**1) self.assertGreaterEqual(sleep_calls[1][0][0], 2**2) self.assertGreaterEqual(sleep_calls[2][0][0], 2**3) @patch.object(controlflow.time, 'sleep') def test_wait_on_failure_does_not_exceed_60_secs_wait(self, mock_sleep): total_attempts = 20 for attempt in range(1, total_attempts + 1): controlflow.wait_on_failure( attempt, total_attempts, f'Attempt #{attempt}', # Suppress messages while we make a lot of attempts. error_print_threshold=total_attempts + 1) # Wait time may be between 60 and 61 secs, due to rand addition. self.assertLessEqual(mock_sleep.call_args[0][0], 61) # Prevent the system from actually sleeping and thus slowing down the test. @patch.object(controlflow.time, 'sleep') def test_wait_on_failure_prints_errors(self, unused_mock_sleep): message = 'An error message to display' with patch.object(controlflow.sys.stderr, 'write') as mock_stderr_write: controlflow.wait_on_failure(1, 5, message, error_print_threshold=0) self.assertIn(message, mock_stderr_write.call_args[0][0]) @patch.object(controlflow.time, 'sleep') def test_wait_on_failure_only_prints_after_threshold( self, unused_mock_sleep): total_attempts = 5 threshold = 3 with patch.object(controlflow.sys.stderr, 'write') as mock_stderr_write: for attempt in range(1, total_attempts + 1): controlflow.wait_on_failure(attempt, total_attempts, f'Attempt #{attempt}', error_print_threshold=threshold) self.assertEqual(total_attempts - threshold, mock_stderr_write.call_count)