2022-07-24 00:12:40 +00:00
|
|
|
from unittest import mock
|
2022-07-24 01:17:07 +00:00
|
|
|
|
2018-12-03 00:28:36 +00:00
|
|
|
from redis import Redis
|
2019-10-22 01:34:47 +00:00
|
|
|
|
2012-07-23 06:25:31 +00:00
|
|
|
from rq.decorators import job
|
2020-08-14 00:17:03 +00:00
|
|
|
from rq.job import Job, Retry
|
2017-01-23 15:37:47 +00:00
|
|
|
from rq.queue import Queue
|
2019-10-22 01:34:47 +00:00
|
|
|
from rq.worker import DEFAULT_RESULT_TTL
|
2014-05-06 13:56:09 +00:00
|
|
|
from tests import RQTestCase
|
2014-05-05 08:49:29 +00:00
|
|
|
from tests.fixtures import decorated_job
|
|
|
|
|
|
|
|
|
2012-07-23 06:25:31 +00:00
|
|
|
class TestDecorator(RQTestCase):
|
|
|
|
|
|
|
|
def setUp(self):
|
2022-07-24 01:17:07 +00:00
|
|
|
super().setUp()
|
2012-07-23 06:25:31 +00:00
|
|
|
|
|
|
|
def test_decorator_preserves_functionality(self):
|
2012-07-24 09:15:23 +00:00
|
|
|
"""Ensure that a decorated function's functionality is still preserved.
|
2012-07-23 06:25:31 +00:00
|
|
|
"""
|
|
|
|
self.assertEqual(decorated_job(1, 2), 3)
|
|
|
|
|
|
|
|
def test_decorator_adds_delay_attr(self):
|
2012-07-24 09:15:23 +00:00
|
|
|
"""Ensure that decorator adds a delay attribute to function that returns
|
2012-07-23 06:25:31 +00:00
|
|
|
a Job instance when called.
|
|
|
|
"""
|
|
|
|
self.assertTrue(hasattr(decorated_job, 'delay'))
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertTrue(isinstance(result, Job))
|
|
|
|
# Ensure that job returns the right result when performed
|
|
|
|
self.assertEqual(result.perform(), 3)
|
|
|
|
|
2012-07-24 09:03:49 +00:00
|
|
|
def test_decorator_accepts_queue_name_as_argument(self):
|
2012-07-24 09:15:23 +00:00
|
|
|
"""Ensure that passing in queue name to the decorator puts the job in
|
|
|
|
the right queue.
|
2012-07-24 09:03:49 +00:00
|
|
|
"""
|
|
|
|
@job(queue='queue_name')
|
|
|
|
def hello():
|
|
|
|
return 'Hi'
|
|
|
|
result = hello.delay()
|
|
|
|
self.assertEqual(result.origin, 'queue_name')
|
2012-09-13 16:07:52 +00:00
|
|
|
|
|
|
|
def test_decorator_accepts_result_ttl_as_argument(self):
|
|
|
|
"""Ensure that passing in result_ttl to the decorator sets the
|
|
|
|
result_ttl on the job
|
|
|
|
"""
|
2014-05-05 08:49:43 +00:00
|
|
|
# Ensure default
|
2012-09-13 16:07:52 +00:00
|
|
|
result = decorated_job.delay(1, 2)
|
2012-09-14 07:56:10 +00:00
|
|
|
self.assertEqual(result.result_ttl, DEFAULT_RESULT_TTL)
|
2012-09-13 16:07:52 +00:00
|
|
|
|
|
|
|
@job('default', result_ttl=10)
|
|
|
|
def hello():
|
|
|
|
return 'Why hello'
|
|
|
|
result = hello.delay()
|
|
|
|
self.assertEqual(result.result_ttl, 10)
|
2014-04-27 17:02:33 +00:00
|
|
|
|
2016-02-16 14:15:49 +00:00
|
|
|
def test_decorator_accepts_ttl_as_argument(self):
|
|
|
|
"""Ensure that passing in ttl to the decorator sets the ttl on the job
|
|
|
|
"""
|
|
|
|
# Ensure default
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertEqual(result.ttl, None)
|
|
|
|
|
|
|
|
@job('default', ttl=30)
|
|
|
|
def hello():
|
|
|
|
return 'Hello'
|
|
|
|
result = hello.delay()
|
|
|
|
self.assertEqual(result.ttl, 30)
|
|
|
|
|
2017-12-06 14:34:26 +00:00
|
|
|
def test_decorator_accepts_meta_as_argument(self):
|
|
|
|
"""Ensure that passing in meta to the decorator sets the meta on the job
|
|
|
|
"""
|
|
|
|
# Ensure default
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertEqual(result.meta, {})
|
|
|
|
|
|
|
|
test_meta = {
|
|
|
|
'metaKey1': 1,
|
|
|
|
'metaKey2': 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
@job('default', meta=test_meta)
|
|
|
|
def hello():
|
|
|
|
return 'Hello'
|
|
|
|
result = hello.delay()
|
|
|
|
self.assertEqual(result.meta, test_meta)
|
|
|
|
|
2014-04-27 17:02:33 +00:00
|
|
|
def test_decorator_accepts_result_depends_on_as_argument(self):
|
|
|
|
"""Ensure that passing in depends_on to the decorator sets the
|
|
|
|
correct dependency on the job
|
|
|
|
"""
|
2017-12-06 14:34:26 +00:00
|
|
|
# Ensure default
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertEqual(result.dependency, None)
|
|
|
|
self.assertEqual(result._dependency_id, None)
|
2014-04-27 17:02:33 +00:00
|
|
|
|
|
|
|
@job(queue='queue_name')
|
|
|
|
def foo():
|
|
|
|
return 'Firstly'
|
|
|
|
|
2017-12-06 14:34:26 +00:00
|
|
|
foo_job = foo.delay()
|
|
|
|
|
|
|
|
@job(queue='queue_name', depends_on=foo_job)
|
2014-04-27 17:02:33 +00:00
|
|
|
def bar():
|
|
|
|
return 'Secondly'
|
|
|
|
|
2017-12-06 14:34:26 +00:00
|
|
|
bar_job = bar.delay()
|
2014-04-27 17:02:33 +00:00
|
|
|
|
2022-08-06 23:48:00 +00:00
|
|
|
self.assertEqual(foo_job._dependency_ids, [])
|
2014-04-27 17:02:33 +00:00
|
|
|
self.assertIsNone(foo_job._dependency_id)
|
|
|
|
|
2019-10-22 01:34:47 +00:00
|
|
|
self.assertEqual(foo_job.dependency, None)
|
2014-04-27 17:02:33 +00:00
|
|
|
self.assertEqual(bar_job.dependency, foo_job)
|
2019-10-22 01:34:47 +00:00
|
|
|
self.assertEqual(bar_job.dependency.id, foo_job.id)
|
2014-05-04 12:34:42 +00:00
|
|
|
|
2017-12-06 14:34:26 +00:00
|
|
|
def test_decorator_delay_accepts_depends_on_as_argument(self):
|
|
|
|
"""Ensure that passing in depends_on to the delay method of
|
|
|
|
a decorated function overrides the depends_on set in the
|
|
|
|
constructor.
|
|
|
|
"""
|
|
|
|
# Ensure default
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertEqual(result.dependency, None)
|
|
|
|
self.assertEqual(result._dependency_id, None)
|
|
|
|
|
|
|
|
@job(queue='queue_name')
|
|
|
|
def foo():
|
|
|
|
return 'Firstly'
|
|
|
|
|
|
|
|
@job(queue='queue_name')
|
|
|
|
def bar():
|
|
|
|
return 'Firstly'
|
|
|
|
|
|
|
|
foo_job = foo.delay()
|
|
|
|
bar_job = bar.delay()
|
|
|
|
|
|
|
|
@job(queue='queue_name', depends_on=foo_job)
|
|
|
|
def baz():
|
|
|
|
return 'Secondly'
|
|
|
|
|
|
|
|
baz_job = bar.delay(depends_on=bar_job)
|
|
|
|
|
|
|
|
self.assertIsNone(foo_job._dependency_id)
|
|
|
|
self.assertIsNone(bar_job._dependency_id)
|
|
|
|
|
2022-08-06 23:48:00 +00:00
|
|
|
self.assertEqual(foo_job._dependency_ids, [])
|
|
|
|
self.assertEqual(bar_job._dependency_ids, [])
|
2017-12-06 14:34:26 +00:00
|
|
|
self.assertEqual(baz_job._dependency_id, bar_job.id)
|
2019-10-22 01:34:47 +00:00
|
|
|
self.assertEqual(baz_job.dependency, bar_job)
|
|
|
|
self.assertEqual(baz_job.dependency.id, bar_job.id)
|
2017-12-06 14:34:26 +00:00
|
|
|
|
2022-03-02 01:20:30 +00:00
|
|
|
def test_decorator_accepts_on_failure_function_as_argument(self):
|
|
|
|
"""Ensure that passing in on_failure function to the decorator sets the
|
|
|
|
correct on_failure function on the job.
|
2022-08-06 23:48:00 +00:00
|
|
|
"""
|
2022-03-02 01:20:30 +00:00
|
|
|
# Only functions and builtins are supported as callback
|
|
|
|
@job('default', on_failure=Job.fetch)
|
|
|
|
def foo():
|
|
|
|
return 'Foo'
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
result = foo.delay()
|
|
|
|
|
|
|
|
@job('default', on_failure=print)
|
|
|
|
def hello():
|
|
|
|
return 'Hello'
|
|
|
|
result = hello.delay()
|
|
|
|
result_job = Job.fetch(id=result.id, connection=self.testconn)
|
|
|
|
self.assertEqual(result_job.failure_callback, print)
|
|
|
|
|
|
|
|
def test_decorator_accepts_on_success_function_as_argument(self):
|
|
|
|
"""Ensure that passing in on_failure function to the decorator sets the
|
|
|
|
correct on_success function on the job.
|
|
|
|
"""
|
|
|
|
# Only functions and builtins are supported as callback
|
|
|
|
@job('default', on_failure=Job.fetch)
|
|
|
|
def foo():
|
|
|
|
return 'Foo'
|
|
|
|
with self.assertRaises(ValueError):
|
|
|
|
result = foo.delay()
|
2022-08-06 23:48:00 +00:00
|
|
|
|
2022-03-02 01:20:30 +00:00
|
|
|
@job('default', on_success=print)
|
|
|
|
def hello():
|
|
|
|
return 'Hello'
|
|
|
|
result = hello.delay()
|
|
|
|
result_job = Job.fetch(id=result.id, connection=self.testconn)
|
|
|
|
self.assertEqual(result_job.success_callback, print)
|
|
|
|
|
2014-05-04 12:40:12 +00:00
|
|
|
@mock.patch('rq.queue.resolve_connection')
|
2014-05-04 12:34:42 +00:00
|
|
|
def test_decorator_connection_laziness(self, resolve_connection):
|
|
|
|
"""Ensure that job decorator resolve connection in `lazy` way """
|
|
|
|
|
2018-12-03 00:28:36 +00:00
|
|
|
resolve_connection.return_value = Redis()
|
2014-05-04 12:34:42 +00:00
|
|
|
|
|
|
|
@job(queue='queue_name')
|
|
|
|
def foo():
|
|
|
|
return 'do something'
|
|
|
|
|
|
|
|
self.assertEqual(resolve_connection.call_count, 0)
|
|
|
|
|
|
|
|
foo()
|
|
|
|
|
|
|
|
self.assertEqual(resolve_connection.call_count, 0)
|
|
|
|
|
|
|
|
foo.delay()
|
|
|
|
|
|
|
|
self.assertEqual(resolve_connection.call_count, 1)
|
2017-01-23 15:37:47 +00:00
|
|
|
|
|
|
|
def test_decorator_custom_queue_class(self):
|
|
|
|
"""Ensure that a custom queue class can be passed to the job decorator"""
|
|
|
|
class CustomQueue(Queue):
|
|
|
|
pass
|
|
|
|
CustomQueue.enqueue_call = mock.MagicMock(
|
|
|
|
spec=lambda *args, **kwargs: None,
|
|
|
|
name='enqueue_call'
|
|
|
|
)
|
|
|
|
|
|
|
|
custom_decorator = job(queue='default', queue_class=CustomQueue)
|
|
|
|
self.assertIs(custom_decorator.queue_class, CustomQueue)
|
|
|
|
|
|
|
|
@custom_decorator
|
|
|
|
def custom_queue_class_job(x, y):
|
|
|
|
return x + y
|
|
|
|
|
|
|
|
custom_queue_class_job.delay(1, 2)
|
|
|
|
self.assertEqual(CustomQueue.enqueue_call.call_count, 1)
|
|
|
|
|
|
|
|
def test_decorate_custom_queue(self):
|
|
|
|
"""Ensure that a custom queue instance can be passed to the job decorator"""
|
|
|
|
class CustomQueue(Queue):
|
|
|
|
pass
|
|
|
|
CustomQueue.enqueue_call = mock.MagicMock(
|
|
|
|
spec=lambda *args, **kwargs: None,
|
|
|
|
name='enqueue_call'
|
|
|
|
)
|
|
|
|
queue = CustomQueue()
|
|
|
|
|
|
|
|
@job(queue=queue)
|
|
|
|
def custom_queue_job(x, y):
|
|
|
|
return x + y
|
|
|
|
|
|
|
|
custom_queue_job.delay(1, 2)
|
|
|
|
self.assertEqual(queue.enqueue_call.call_count, 1)
|
2019-09-08 11:04:43 +00:00
|
|
|
|
|
|
|
def test_decorator_custom_failure_ttl(self):
|
|
|
|
"""Ensure that passing in failure_ttl to the decorator sets the
|
|
|
|
failure_ttl on the job
|
|
|
|
"""
|
|
|
|
# Ensure default
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertEqual(result.failure_ttl, None)
|
|
|
|
|
|
|
|
@job('default', failure_ttl=10)
|
|
|
|
def hello():
|
|
|
|
return 'Why hello'
|
|
|
|
result = hello.delay()
|
|
|
|
self.assertEqual(result.failure_ttl, 10)
|
2020-08-14 00:17:03 +00:00
|
|
|
|
|
|
|
def test_decorator_custom_retry(self):
|
|
|
|
""" Ensure that passing in retry to the decorator sets the
|
|
|
|
retry on the job
|
|
|
|
"""
|
|
|
|
# Ensure default
|
|
|
|
result = decorated_job.delay(1, 2)
|
|
|
|
self.assertEqual(result.retries_left, None)
|
|
|
|
self.assertEqual(result.retry_intervals, None)
|
|
|
|
|
|
|
|
@job('default', retry=Retry(3, [2]))
|
|
|
|
def hello():
|
|
|
|
return 'Why hello'
|
|
|
|
result = hello.delay()
|
|
|
|
self.assertEqual(result.retries_left, 3)
|
|
|
|
self.assertEqual(result.retry_intervals, [2])
|