diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b03a94f..0f23c69 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -14,6 +14,10 @@ Change Log Unreleased ~~~~~~~~~~ +[1.0.2] - 2021-09-29 +~~~~~~~~~~~~~~~~~~~~~ +* Add automatic retry logic to celery tasks. + [1.0.1] - 2021-09-28 ~~~~~~~~~~~~~~~~~~~~~ * Move toggle check out of tasks diff --git a/edx_name_affirmation/__init__.py b/edx_name_affirmation/__init__.py index bf3ca02..cb65737 100644 --- a/edx_name_affirmation/__init__.py +++ b/edx_name_affirmation/__init__.py @@ -2,6 +2,6 @@ Django app housing name affirmation logic. """ -__version__ = '1.0.1' +__version__ = '1.0.2' default_app_config = 'edx_name_affirmation.apps.EdxNameAffirmationConfig' # pylint: disable=invalid-name diff --git a/edx_name_affirmation/tasks.py b/edx_name_affirmation/tasks.py index 6b67163..3be6848 100644 --- a/edx_name_affirmation/tasks.py +++ b/edx_name_affirmation/tasks.py @@ -1,4 +1,4 @@ -# pylint: disable=logging-format-interpolation +# pylint: disable=logging-format-interpolation, unused-argument """ Name affirmation celery tasks """ @@ -17,14 +17,18 @@ log = logging.getLogger(__name__) +DEFAULT_RETRY_SECONDS = 30 +MAX_RETRIES = 3 -@shared_task + +@shared_task( + bind=True, autoretry_for=(Exception,), default_retry_delay=DEFAULT_RETRY_SECONDS, max_retries=MAX_RETRIES, +) @set_code_owner_attribute -def idv_update_verified_name(attempt_id, user_id, status, photo_id_name, full_name): +def idv_update_verified_name(self, attempt_id, user_id, status, photo_id_name, full_name): """ Celery task for updating a verified name based on an IDV attempt """ - trigger_status = VerifiedNameStatus.trigger_state_change_from_idv(status) verified_names = VerifiedName.objects.filter(user__id=user_id, verified_name=photo_id_name).order_by('-created') if verified_names: @@ -84,9 +88,12 @@ def idv_update_verified_name(attempt_id, user_id, status, photo_id_name, full_na ) -@shared_task +@shared_task( + bind=True, autoretry_for=(Exception,), default_retry_delay=DEFAULT_RETRY_SECONDS, max_retries=MAX_RETRIES, +) @set_code_owner_attribute def proctoring_update_verified_name( + self, attempt_id, user_id, status, diff --git a/edx_name_affirmation/tests/test_tasks.py b/edx_name_affirmation/tests/test_tasks.py new file mode 100644 index 0000000..89a9dcf --- /dev/null +++ b/edx_name_affirmation/tests/test_tasks.py @@ -0,0 +1,56 @@ +""" +Tests for Name Affirmation tasks +""" + +from mock import patch + +from django.contrib.auth import get_user_model +from django.test import TestCase + +from edx_name_affirmation.models import VerifiedName +from edx_name_affirmation.statuses import VerifiedNameStatus +from edx_name_affirmation.tasks import idv_update_verified_name, proctoring_update_verified_name + +User = get_user_model() + + +class TaskTests(TestCase): + """ + Tests for tasks.py + """ + def setUp(self): # pylint: disable=super-method-not-called + self.user = User(username='tester', email='tester@test.com') + self.user.save() + self.verified_name_obj = VerifiedName( + user=self.user, verified_name='Jonathan Doe', profile_name='Jon Doe', + ) + self.verified_name_obj.save() + self.idv_attempt_id = 1111111 + self.proctoring_attempt_id = 2222222 + + @patch('edx_name_affirmation.tasks.idv_update_verified_name.retry') + def test_idv_retry(self, mock_retry): + idv_update_verified_name.delay( + self.idv_attempt_id, + # force an error with an invalid user ID + 99999, + VerifiedNameStatus.SUBMITTED, + self.verified_name_obj.verified_name, + self.verified_name_obj.profile_name, + ) + mock_retry.assert_called() + + @patch('edx_name_affirmation.tasks.proctoring_update_verified_name.retry') + def test_proctoring_retry(self, mock_retry): + proctoring_update_verified_name.delay( + self.proctoring_attempt_id, + # force an error with an invalid user ID + 99999, + VerifiedNameStatus.PENDING, + self.verified_name_obj.verified_name, + self.verified_name_obj.profile_name, + True, + True, + True, + ) + mock_retry.assert_called()