diff --git a/queue_job/models/queue_job_function.py b/queue_job/models/queue_job_function.py index f593b6520a..d839708dcf 100644 --- a/queue_job/models/queue_job_function.py +++ b/queue_job/models/queue_job_function.py @@ -149,10 +149,12 @@ def _parse_retry_pattern(self): try: # as json can't have integers as keys and the field is stored # as json, convert back to int - retry_pattern = { - int(try_count): postpone_seconds - for try_count, postpone_seconds in self.retry_pattern.items() - } + retry_pattern = {} + for try_count, postpone_value in self.retry_pattern.items(): + if isinstance(postpone_value, int): + retry_pattern[int(try_count)] = postpone_value + else: + retry_pattern[int(try_count)] = tuple(postpone_value) except ValueError: _logger.error( "Invalid retry pattern for job function %s," @@ -181,8 +183,9 @@ def job_config(self, name): def _retry_pattern_format_error_message(self): return _( "Unexpected format of Retry Pattern for {}.\n" - "Example of valid format:\n" - "{{1: 300, 5: 600, 10: 1200, 15: 3000}}" + "Example of valid formats:\n" + "{{1: 300, 5: 600, 10: 1200, 15: 3000}}\n" + "{{1: (1, 10), 5: (11, 20), 10: (21, 30), 15: (100, 300)}}" ).format(self.name) @api.constrains("retry_pattern") @@ -195,12 +198,20 @@ def _check_retry_pattern(self): all_values = list(retry_pattern) + list(retry_pattern.values()) for value in all_values: try: - int(value) + self._retry_value_type_check(value) except ValueError: raise exceptions.UserError( record._retry_pattern_format_error_message() ) + def _retry_value_type_check(self, value): + if isinstance(value, (tuple, list)): + if len(value) != 2: + raise ValueError + [self._retry_value_type_check(element) for element in value] + return + int(value) + def _related_action_format_error_message(self): return _( "Unexpected format of Related Action for {}.\n" diff --git a/test_queue_job/tests/__init__.py b/test_queue_job/tests/__init__.py index dc59429e71..0405022ce0 100644 --- a/test_queue_job/tests/__init__.py +++ b/test_queue_job/tests/__init__.py @@ -4,5 +4,6 @@ from . import test_job from . import test_job_auto_delay from . import test_job_channels +from . import test_job_function from . import test_related_actions from . import test_delay_mocks diff --git a/test_queue_job/tests/test_job_function.py b/test_queue_job/tests/test_job_function.py new file mode 100644 index 0000000000..17781ac475 --- /dev/null +++ b/test_queue_job/tests/test_job_function.py @@ -0,0 +1,35 @@ +import odoo.tests.common as common +from odoo import exceptions + + +class TestJobFunction(common.TransactionCase): + def setUp(self): + super(TestJobFunction, self).setUp() + self.test_function_model = self.env.ref( + "queue_job.job_function_queue_job__test_job" + ) + + def test_check_retry_pattern_randomized_case(self): + randomized_pattern = "{1: (10, 20), 2: (20, 40)}" + self.test_function_model.edit_retry_pattern = randomized_pattern + self.assertEqual( + self.test_function_model.edit_retry_pattern, randomized_pattern + ) + + def test_check_retry_pattern_fixed_case(self): + fixed_pattern = "{1: 10, 2: 20}" + self.test_function_model.edit_retry_pattern = fixed_pattern + self.assertEqual(self.test_function_model.edit_retry_pattern, fixed_pattern) + + def test_check_retry_pattern_invalid_cases(self): + invalid_time_value_pattern = "{1: a, 2: 20}" + with self.assertRaises(exceptions.UserError): + self.test_function_model.edit_retry_pattern = invalid_time_value_pattern + + invalid_retry_count_pattern = "{a: 10, 2: 20}" + with self.assertRaises(exceptions.UserError): + self.test_function_model.edit_retry_pattern = invalid_retry_count_pattern + + invalid_randomized_pattern = "{1: (1, 2, 3), 2: 20}" + with self.assertRaises(exceptions.UserError): + self.test_function_model.edit_retry_pattern = invalid_randomized_pattern