From 15ff78c678ed71b50e6d56c948352774c49fe283 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 28 Aug 2024 11:55:25 -0300 Subject: [PATCH 01/10] feat: Adding IDV Status to VerifiedName --- edx_name_affirmation/serializers.py | 18 +++++++++++++++++- edx_name_affirmation/tests/test_views.py | 7 +++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/edx_name_affirmation/serializers.py b/edx_name_affirmation/serializers.py index b67bc88..8ca7b20 100644 --- a/edx_name_affirmation/serializers.py +++ b/edx_name_affirmation/serializers.py @@ -19,9 +19,25 @@ class VerifiedNameSerializer(serializers.ModelSerializer): verified_name = serializers.CharField(required=True) profile_name = serializers.CharField(required=True) verification_attempt_id = serializers.IntegerField(required=False, allow_null=True) + verification_attempt_status = serializers.SerializerMethodField('get_verification_attempt_status') proctored_exam_attempt_id = serializers.IntegerField(required=False, allow_null=True) status = serializers.CharField(required=False, allow_null=True) + def get_verification_attempt_status(self, obj): + try: + from lms.djangoapps.verify_student.services import IDVerificationService # pylint: disable=import-error,import-outside-toplevel + except ImportError: + return None + + idv_attempt_id = obj.verification_attempt_id + + if not idv_attempt_id: + return None + + verification = IDVerificationService.get_verification_details_by_id(idv_attempt_id) + + return verification.status if verification else None + class Meta: """ Meta Class @@ -30,7 +46,7 @@ class Meta: fields = ( "id", "created", "username", "verified_name", "profile_name", "verification_attempt_id", - "proctored_exam_attempt_id", "status" + "verification_attempt_status", "proctored_exam_attempt_id", "status" ) def validate_verified_name(self, verified_name): diff --git a/edx_name_affirmation/tests/test_views.py b/edx_name_affirmation/tests/test_views.py index 650643d..827a5dc 100644 --- a/edx_name_affirmation/tests/test_views.py +++ b/edx_name_affirmation/tests/test_views.py @@ -114,6 +114,7 @@ def test_post_200(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'verification_attempt_id': self.ATTEMPT_ID, + 'verification_attempt_status': None, } response = self.client.post( reverse('edx_name_affirmation:verified_name'), @@ -136,7 +137,9 @@ def test_post_200_if_staff(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'proctored_exam_attempt_id': self.ATTEMPT_ID, + 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, + } response = self.client.post( reverse('edx_name_affirmation:verified_name'), @@ -156,6 +159,7 @@ def test_post_403_non_staff(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'verification_attempt_id': self.ATTEMPT_ID, + 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } response = self.client.post( @@ -171,6 +175,7 @@ def test_post_400_invalid_name(self, verified_name): 'profile_name': self.PROFILE_NAME, 'verified_name': verified_name, 'verification_attempt_id': self.ATTEMPT_ID, + 'verification_attempt_status': None, 'status': VerifiedNameStatus.SUBMITTED.value, } response = self.client.post( @@ -185,6 +190,7 @@ def test_post_400_invalid_serializer(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'verification_attempt_id': 'xxyz', + 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } response = self.client.post( @@ -357,6 +363,7 @@ def _get_expected_data( 'verified_name': verified_name_obj.verified_name, 'profile_name': verified_name_obj.profile_name, 'verification_attempt_id': verified_name_obj.verification_attempt_id, + 'verification_attempt_status': None, 'proctored_exam_attempt_id': verified_name_obj.proctored_exam_attempt_id, 'status': verified_name_obj.status, 'use_verified_name_for_certs': use_verified_name_for_certs, From 86d6ddf08a01e612b816d3323a6e95d1062ded9c Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 30 Aug 2024 11:09:18 -0300 Subject: [PATCH 02/10] fix: Moved attempt status getter from Serialized to Model --- edx_name_affirmation/models.py | 14 ++++++++++++++ edx_name_affirmation/serializers.py | 17 +---------------- edx_name_affirmation/tests/test_models.py | 5 +++++ edx_name_affirmation/tests/test_views.py | 1 - 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/edx_name_affirmation/models.py b/edx_name_affirmation/models.py index 14ef24b..0dc2056 100644 --- a/edx_name_affirmation/models.py +++ b/edx_name_affirmation/models.py @@ -45,6 +45,20 @@ class Meta: db_table = 'nameaffirmation_verifiedname' verbose_name = 'verified name' + @property + def verification_attempt_status(self): + if not self.verification_attempt_id: + return None + + try: + from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification + except ImportError: + return None + + verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id) + + return verification.status if verification else None + class VerifiedNameConfig(ConfigurationModel): """ diff --git a/edx_name_affirmation/serializers.py b/edx_name_affirmation/serializers.py index 8ca7b20..464519d 100644 --- a/edx_name_affirmation/serializers.py +++ b/edx_name_affirmation/serializers.py @@ -19,25 +19,10 @@ class VerifiedNameSerializer(serializers.ModelSerializer): verified_name = serializers.CharField(required=True) profile_name = serializers.CharField(required=True) verification_attempt_id = serializers.IntegerField(required=False, allow_null=True) - verification_attempt_status = serializers.SerializerMethodField('get_verification_attempt_status') + verification_attempt_status = serializers.CharField(required=False, allow_null=True) proctored_exam_attempt_id = serializers.IntegerField(required=False, allow_null=True) status = serializers.CharField(required=False, allow_null=True) - def get_verification_attempt_status(self, obj): - try: - from lms.djangoapps.verify_student.services import IDVerificationService # pylint: disable=import-error,import-outside-toplevel - except ImportError: - return None - - idv_attempt_id = obj.verification_attempt_id - - if not idv_attempt_id: - return None - - verification = IDVerificationService.get_verification_details_by_id(idv_attempt_id) - - return verification.status if verification else None - class Meta: """ Meta Class diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index 1f22e35..043c1bf 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -8,6 +8,11 @@ from edx_name_affirmation.models import VerifiedName from edx_name_affirmation.statuses import VerifiedNameStatus +try: + from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification +except ImportError: + pass + User = get_user_model() diff --git a/edx_name_affirmation/tests/test_views.py b/edx_name_affirmation/tests/test_views.py index 827a5dc..5ff7063 100644 --- a/edx_name_affirmation/tests/test_views.py +++ b/edx_name_affirmation/tests/test_views.py @@ -139,7 +139,6 @@ def test_post_200_if_staff(self): 'proctored_exam_attempt_id': self.ATTEMPT_ID, 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, - } response = self.client.post( reverse('edx_name_affirmation:verified_name'), From 839fcc0dd30ddf31cea66273086b1424fcd4210d Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 30 Aug 2024 11:28:32 -0300 Subject: [PATCH 03/10] fix: Reverted Views unit tests --- edx_name_affirmation/tests/test_views.py | 29 +++++++++++++----------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/edx_name_affirmation/tests/test_views.py b/edx_name_affirmation/tests/test_views.py index 5ff7063..af74c2d 100644 --- a/edx_name_affirmation/tests/test_views.py +++ b/edx_name_affirmation/tests/test_views.py @@ -8,6 +8,7 @@ from django.contrib.auth import get_user_model from django.core.cache import cache from django.urls import reverse +from functools import partial from edx_name_affirmation.api import ( create_verified_name, @@ -37,6 +38,7 @@ def setUp(self): # Create fresh configs with default values VerifiedNameConfig.objects.create(user=self.user) VerifiedNameConfig.objects.create(user=self.other_user) + self.json_post = partial(self.client.post, content_type="application/json") def tearDown(self): super().tearDown() @@ -116,7 +118,7 @@ def test_post_200(self): 'verification_attempt_id': self.ATTEMPT_ID, 'verification_attempt_status': None, } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -140,9 +142,10 @@ def test_post_200_if_staff(self): 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name'), - verified_name_data + verified_name_data, + content_type='json' ) self.assertEqual(response.status_code, 200) @@ -161,7 +164,7 @@ def test_post_403_non_staff(self): 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -177,7 +180,7 @@ def test_post_400_invalid_name(self, verified_name): 'verification_attempt_status': None, 'status': VerifiedNameStatus.SUBMITTED.value, } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -192,7 +195,7 @@ def test_post_400_invalid_serializer(self): 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -206,7 +209,7 @@ def test_post_400_two_attempt_ids(self): 'verification_attempt_id': self.ATTEMPT_ID, 'proctored_exam_attempt_id': self.ATTEMPT_ID } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -480,7 +483,7 @@ def test_post_201(self): 'username': self.user.username, 'use_verified_name_for_certs': True } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name_config'), config_data ) @@ -496,11 +499,11 @@ def test_post_201_missing_field(self): } config_data_missing_field = {'username': self.user.username} - first_response = self.client.post( + first_response = self.json_post( reverse('edx_name_affirmation:verified_name_config'), initial_config_data ) - second_response = self.client.post( + second_response = self.json_post( reverse('edx_name_affirmation:verified_name_config'), config_data_missing_field ) @@ -520,7 +523,7 @@ def test_post_201_if_staff(self): 'username': self.other_user.username, 'use_verified_name_for_certs': True } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name_config'), config_data ) @@ -534,7 +537,7 @@ def test_post_403_non_staff(self): 'username': self.other_user.username, 'use_verified_name_for_certs': True } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name_config'), config_data ) @@ -545,7 +548,7 @@ def test_post_400_invalid_serializer(self): 'username': self.user.username, 'use_verified_name_for_certs': 'not a boolean' } - response = self.client.post( + response = self.json_post( reverse('edx_name_affirmation:verified_name_config'), config_data ) From 734a8674b428dd489cdb5472b7bbe80f159e5894 Mon Sep 17 00:00:00 2001 From: Marcos Date: Fri, 30 Aug 2024 11:37:57 -0300 Subject: [PATCH 04/10] fix: Updated tests --- edx_name_affirmation/tests/test_views.py | 35 ++++++++++-------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/edx_name_affirmation/tests/test_views.py b/edx_name_affirmation/tests/test_views.py index af74c2d..c4dd425 100644 --- a/edx_name_affirmation/tests/test_views.py +++ b/edx_name_affirmation/tests/test_views.py @@ -8,7 +8,6 @@ from django.contrib.auth import get_user_model from django.core.cache import cache from django.urls import reverse -from functools import partial from edx_name_affirmation.api import ( create_verified_name, @@ -38,7 +37,6 @@ def setUp(self): # Create fresh configs with default values VerifiedNameConfig.objects.create(user=self.user) VerifiedNameConfig.objects.create(user=self.other_user) - self.json_post = partial(self.client.post, content_type="application/json") def tearDown(self): super().tearDown() @@ -116,9 +114,8 @@ def test_post_200(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'verification_attempt_id': self.ATTEMPT_ID, - 'verification_attempt_status': None, } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -139,13 +136,11 @@ def test_post_200_if_staff(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'proctored_exam_attempt_id': self.ATTEMPT_ID, - 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name'), - verified_name_data, - content_type='json' + verified_name_data ) self.assertEqual(response.status_code, 200) @@ -161,10 +156,9 @@ def test_post_403_non_staff(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'verification_attempt_id': self.ATTEMPT_ID, - 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -177,10 +171,9 @@ def test_post_400_invalid_name(self, verified_name): 'profile_name': self.PROFILE_NAME, 'verified_name': verified_name, 'verification_attempt_id': self.ATTEMPT_ID, - 'verification_attempt_status': None, 'status': VerifiedNameStatus.SUBMITTED.value, } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -192,10 +185,9 @@ def test_post_400_invalid_serializer(self): 'profile_name': self.PROFILE_NAME, 'verified_name': self.VERIFIED_NAME, 'verification_attempt_id': 'xxyz', - 'verification_attempt_status': None, 'status': VerifiedNameStatus.APPROVED.value, } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -209,7 +201,7 @@ def test_post_400_two_attempt_ids(self): 'verification_attempt_id': self.ATTEMPT_ID, 'proctored_exam_attempt_id': self.ATTEMPT_ID } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name'), verified_name_data ) @@ -465,6 +457,7 @@ def _get_expected_response( 'verified_name': verified_name_obj.verified_name, 'profile_name': verified_name_obj.profile_name, 'verification_attempt_id': verified_name_obj.verification_attempt_id, + 'verification_attempt_status': None, 'proctored_exam_attempt_id': verified_name_obj.proctored_exam_attempt_id, 'status': verified_name_obj.status } @@ -483,7 +476,7 @@ def test_post_201(self): 'username': self.user.username, 'use_verified_name_for_certs': True } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name_config'), config_data ) @@ -499,11 +492,11 @@ def test_post_201_missing_field(self): } config_data_missing_field = {'username': self.user.username} - first_response = self.json_post( + first_response = self.client.post( reverse('edx_name_affirmation:verified_name_config'), initial_config_data ) - second_response = self.json_post( + second_response = self.client.post( reverse('edx_name_affirmation:verified_name_config'), config_data_missing_field ) @@ -523,7 +516,7 @@ def test_post_201_if_staff(self): 'username': self.other_user.username, 'use_verified_name_for_certs': True } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name_config'), config_data ) @@ -537,7 +530,7 @@ def test_post_403_non_staff(self): 'username': self.other_user.username, 'use_verified_name_for_certs': True } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name_config'), config_data ) @@ -548,7 +541,7 @@ def test_post_400_invalid_serializer(self): 'username': self.user.username, 'use_verified_name_for_certs': 'not a boolean' } - response = self.json_post( + response = self.client.post( reverse('edx_name_affirmation:verified_name_config'), config_data ) From 1067184273642052c6a7ae214b2d2be23087cebf Mon Sep 17 00:00:00 2001 From: Marcos Date: Tue, 3 Sep 2024 12:29:53 -0300 Subject: [PATCH 05/10] chore: Added proper tests to model --- edx_name_affirmation/models.py | 14 ++++++++----- edx_name_affirmation/tests/test_models.py | 25 ++++++++++++++++------- 2 files changed, 27 insertions(+), 12 deletions(-) diff --git a/edx_name_affirmation/models.py b/edx_name_affirmation/models.py index 0dc2056..f0e0728 100644 --- a/edx_name_affirmation/models.py +++ b/edx_name_affirmation/models.py @@ -11,6 +11,11 @@ from edx_name_affirmation.statuses import VerifiedNameStatus +try: + from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification +except ImportError: + SoftwareSecurePhotoVerification = None + User = get_user_model() @@ -47,14 +52,13 @@ class Meta: @property def verification_attempt_status(self): - if not self.verification_attempt_id: - return None + "Returns the status associated with its SoftwareSecurePhotoVerification with verification_attempt_id if any." - try: - from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification - except ImportError: + if not self.verification_attempt_id or not SoftwareSecurePhotoVerification: return None + # breakpoint() + verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id) return verification.status if verification else None diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index 043c1bf..eaf344a 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -1,6 +1,7 @@ """ Tests for Name Affirmation models """ +from unittest.mock import patch from django.contrib.auth import get_user_model from django.test import TestCase @@ -8,14 +9,15 @@ from edx_name_affirmation.models import VerifiedName from edx_name_affirmation.statuses import VerifiedNameStatus -try: - from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification -except ImportError: - pass - User = get_user_model() +def _obj(dictionary): + "Helper method to turn a dict into an object. Used to mock below." + + return type('obj', (object,), dictionary) + + class VerifiedNameModelTests(TestCase): """ Test suite for the VerifiedName models @@ -30,15 +32,24 @@ def setUp(self): ) return super().setUp() - def test_histories(self): + @patch('edx_name_affirmation.models.SoftwareSecurePhotoVerification') + def test_histories(self, sspv_mock): """ Test the model history is recording records as expected """ + idv_attempt_id = 34455 + idv_attempt_status = 'submitted' + + sspv_mock.objects.get = lambda id: \ + _obj({'status': idv_attempt_status}) if id == idv_attempt_id \ + else _obj({'status': None}) + verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 1 - idv_attempt_id = 34455 self.verified_name.status = VerifiedNameStatus.APPROVED self.verified_name.verification_attempt_id = idv_attempt_id + + assert self.verified_name.verification_attempt_status is idv_attempt_status self.verified_name.save() verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 2 From 8bcfee555ecb8789030829e61567473df411dfea Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 4 Sep 2024 12:50:56 -0300 Subject: [PATCH 06/10] fix: Added catch to not found exception --- edx_name_affirmation/models.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/edx_name_affirmation/models.py b/edx_name_affirmation/models.py index f0e0728..9a4c3a0 100644 --- a/edx_name_affirmation/models.py +++ b/edx_name_affirmation/models.py @@ -57,12 +57,12 @@ def verification_attempt_status(self): if not self.verification_attempt_id or not SoftwareSecurePhotoVerification: return None - # breakpoint() - - verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id) - - return verification.status if verification else None + try: + verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id) + return verification.status if verification else None + except SoftwareSecurePhotoVerification.DoesNotExist: + return None class VerifiedNameConfig(ConfigurationModel): """ From 8d568b2355887065c5f807b319317fe62a732e23 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 4 Sep 2024 13:08:07 -0300 Subject: [PATCH 07/10] chore: Updated tests --- edx_name_affirmation/tests/test_models.py | 28 +++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index eaf344a..5325a9c 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -32,24 +32,16 @@ def setUp(self): ) return super().setUp() - @patch('edx_name_affirmation.models.SoftwareSecurePhotoVerification') - def test_histories(self, sspv_mock): + def test_histories(self): """ Test the model history is recording records as expected """ idv_attempt_id = 34455 - idv_attempt_status = 'submitted' - - sspv_mock.objects.get = lambda id: \ - _obj({'status': idv_attempt_status}) if id == idv_attempt_id \ - else _obj({'status': None}) verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 1 self.verified_name.status = VerifiedNameStatus.APPROVED self.verified_name.verification_attempt_id = idv_attempt_id - - assert self.verified_name.verification_attempt_status is idv_attempt_status self.verified_name.save() verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 2 @@ -61,3 +53,21 @@ def test_histories(self, sspv_mock): second_history_record = verified_name_history[1] assert second_history_record.status == VerifiedNameStatus.APPROVED assert second_history_record.verification_attempt_id == idv_attempt_id + + @patch('edx_name_affirmation.models.SoftwareSecurePhotoVerification') + def test_verification_status(self, sspv_mock): + """ + Test the model history is recording records as expected + """ + idv_attempt_id = 34455 + idv_attempt_status = 'submitted' + + sspv_mock.objects.get = lambda id: \ + _obj({'status': idv_attempt_status}) if id == idv_attempt_id \ + else _obj({'status': None}) + + self.verified_name.verification_attempt_id = 12345 + assert self.verified_name.verification_attempt_status is None + + self.verified_name.verification_attempt_id = idv_attempt_id + assert self.verified_name.verification_attempt_status is idv_attempt_status From d54fa932380962ce37b09a98c7cdb0ee57e8ea67 Mon Sep 17 00:00:00 2001 From: Marcos Date: Wed, 4 Sep 2024 13:45:23 -0300 Subject: [PATCH 08/10] fix: Updated tests --- edx_name_affirmation/models.py | 4 ++- edx_name_affirmation/tests/test_models.py | 30 ++++++++++++++++------- edx_name_affirmation/tests/test_views.py | 20 +++++++++++++++ 3 files changed, 44 insertions(+), 10 deletions(-) diff --git a/edx_name_affirmation/models.py b/edx_name_affirmation/models.py index 9a4c3a0..bd61db7 100644 --- a/edx_name_affirmation/models.py +++ b/edx_name_affirmation/models.py @@ -7,6 +7,7 @@ from simple_history.models import HistoricalRecords from django.contrib.auth import get_user_model +from django.core.exceptions import ObjectDoesNotExist from django.db import models from edx_name_affirmation.statuses import VerifiedNameStatus @@ -61,9 +62,10 @@ def verification_attempt_status(self): verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id) return verification.status if verification else None - except SoftwareSecurePhotoVerification.DoesNotExist: + except ObjectDoesNotExist: return None + class VerifiedNameConfig(ConfigurationModel): """ This model provides various configuration fields for users regarding their diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index 5325a9c..5a030f1 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -4,6 +4,7 @@ from unittest.mock import patch from django.contrib.auth import get_user_model +from django.core.exceptions import ObjectDoesNotExist from django.test import TestCase from edx_name_affirmation.models import VerifiedName @@ -11,6 +12,12 @@ User = get_user_model() +idv_attempt_id = 34455 +idv_attempt_status = 'submitted' + +idv_attempt_id_notfound = 404 +idv_attempt_id_notfound_status = None + def _obj(dictionary): "Helper method to turn a dict into an object. Used to mock below." @@ -18,6 +25,17 @@ def _obj(dictionary): return type('obj', (object,), dictionary) +def _mocked_model_get(id): # pylint: disable=redefined-builtin + "Helper method to mock the behavior of SoftwareSecurePhotoVerification model. Used to mock below." + if id == idv_attempt_id_notfound: + raise ObjectDoesNotExist + + if id == idv_attempt_id: + return _obj({'status': idv_attempt_status}) + + return _obj({'status': None}) + + class VerifiedNameModelTests(TestCase): """ Test suite for the VerifiedName models @@ -36,7 +54,6 @@ def test_histories(self): """ Test the model history is recording records as expected """ - idv_attempt_id = 34455 verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 1 @@ -59,15 +76,10 @@ def test_verification_status(self, sspv_mock): """ Test the model history is recording records as expected """ - idv_attempt_id = 34455 - idv_attempt_status = 'submitted' - - sspv_mock.objects.get = lambda id: \ - _obj({'status': idv_attempt_status}) if id == idv_attempt_id \ - else _obj({'status': None}) + sspv_mock.objects.get = _mocked_model_get - self.verified_name.verification_attempt_id = 12345 - assert self.verified_name.verification_attempt_status is None + self.verified_name.verification_attempt_id = idv_attempt_id_notfound + assert self.verified_name.verification_attempt_status is idv_attempt_id_notfound_status self.verified_name.verification_attempt_id = idv_attempt_id assert self.verified_name.verification_attempt_status is idv_attempt_status diff --git a/edx_name_affirmation/tests/test_views.py b/edx_name_affirmation/tests/test_views.py index c4dd425..eb1eb86 100644 --- a/edx_name_affirmation/tests/test_views.py +++ b/edx_name_affirmation/tests/test_views.py @@ -2,6 +2,7 @@ All tests for edx_name_affirmation views """ import json +from unittest.mock import PropertyMock, patch import ddt @@ -380,6 +381,25 @@ def test_get(self): data = json.loads(response.content.decode('utf-8')) self.assertEqual(data, expected_response) + @patch('edx_name_affirmation.api.VerifiedName.verification_attempt_status', new_callable=PropertyMock) + def test_get_with_idv_status(self, verification_attempt_status_mock): + mocked_idv_status = 'approved' + + verification_attempt_status_mock.return_value = mocked_idv_status + verified_name_history = self._create_verified_name_history(self.user) + expected_response = self._get_expected_response(self.user, verified_name_history) + + # replacing the expected response results with the mocked status + for row in expected_response['results']: + row['verification_attempt_status'] = mocked_idv_status + + response = self.client.get(reverse('edx_name_affirmation:verified_name_history')) + + self.assertEqual(response.status_code, 200) + data = json.loads(response.content.decode('utf-8')) + + self.assertEqual(data, expected_response) + def test_get_bools(self): verified_name_history = self._create_verified_name_history(self.user) expected_response = self._get_expected_response( From b8069b7b571e5d51caf7e0d3f4c12b56bd69d100 Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 9 Sep 2024 15:07:18 -0300 Subject: [PATCH 09/10] chore: Moved test variables to be more localized --- edx_name_affirmation/models.py | 2 +- edx_name_affirmation/tests/test_models.py | 59 +++++++++++------------ 2 files changed, 30 insertions(+), 31 deletions(-) diff --git a/edx_name_affirmation/models.py b/edx_name_affirmation/models.py index bd61db7..08e109b 100644 --- a/edx_name_affirmation/models.py +++ b/edx_name_affirmation/models.py @@ -60,7 +60,7 @@ def verification_attempt_status(self): try: verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id) - return verification.status if verification else None + return verification.status except ObjectDoesNotExist: return None diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index 5a030f1..fcef806 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -12,35 +12,14 @@ User = get_user_model() -idv_attempt_id = 34455 -idv_attempt_status = 'submitted' - -idv_attempt_id_notfound = 404 -idv_attempt_id_notfound_status = None - - -def _obj(dictionary): - "Helper method to turn a dict into an object. Used to mock below." - - return type('obj', (object,), dictionary) - - -def _mocked_model_get(id): # pylint: disable=redefined-builtin - "Helper method to mock the behavior of SoftwareSecurePhotoVerification model. Used to mock below." - if id == idv_attempt_id_notfound: - raise ObjectDoesNotExist - - if id == idv_attempt_id: - return _obj({'status': idv_attempt_status}) - - return _obj({'status': None}) - - class VerifiedNameModelTests(TestCase): """ Test suite for the VerifiedName models """ def setUp(self): + self.idv_attempt_id = 34455 + self.idv_attempt_status = 'submitted' + self.idv_attempt_id_notfound = 404 self.verified_name = 'Test Tester' self.user = User.objects.create(username='modelTester', email='model@tester.com') self.verified_name = VerifiedName.objects.create( @@ -58,7 +37,7 @@ def test_histories(self): verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 1 self.verified_name.status = VerifiedNameStatus.APPROVED - self.verified_name.verification_attempt_id = idv_attempt_id + self.verified_name.verification_attempt_id = self.idv_attempt_id self.verified_name.save() verified_name_history = self.verified_name.history.all().order_by('history_date') assert len(verified_name_history) == 2 @@ -69,17 +48,37 @@ def test_histories(self): second_history_record = verified_name_history[1] assert second_history_record.status == VerifiedNameStatus.APPROVED - assert second_history_record.verification_attempt_id == idv_attempt_id + assert second_history_record.verification_attempt_id == self.idv_attempt_id @patch('edx_name_affirmation.models.SoftwareSecurePhotoVerification') def test_verification_status(self, sspv_mock): """ Test the model history is recording records as expected """ - sspv_mock.objects.get = _mocked_model_get - self.verified_name.verification_attempt_id = idv_attempt_id_notfound + idv_attempt_id_notfound_status = None + + sspv_mock.objects.get = self._mocked_model_get + + self.verified_name.verification_attempt_id = self.idv_attempt_id_notfound assert self.verified_name.verification_attempt_status is idv_attempt_id_notfound_status - self.verified_name.verification_attempt_id = idv_attempt_id - assert self.verified_name.verification_attempt_status is idv_attempt_status + self.verified_name.verification_attempt_id = self.idv_attempt_id + assert self.verified_name.verification_attempt_status is self.idv_attempt_status + + # Helper methods + + def _obj(self, dictionary): + "Helper method to turn a dict into an object. Used to mock below." + + return type('obj', (object,), dictionary) + + def _mocked_model_get(self, id): # pylint: disable=redefined-builtin + "Helper method to mock the behavior of SoftwareSecurePhotoVerification model. Used to mock below." + if id == self.idv_attempt_id_notfound: + raise ObjectDoesNotExist + + if id == self.idv_attempt_id: + return self._obj({'status': self.idv_attempt_status}) + + return self._obj({'status': None}) From cc9d1160741d7b7e17ce1af8d0f7a64003dca2a0 Mon Sep 17 00:00:00 2001 From: Marcos Date: Mon, 9 Sep 2024 15:07:18 -0300 Subject: [PATCH 10/10] chore: Moved test variables to be more localized --- edx_name_affirmation/tests/test_models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/edx_name_affirmation/tests/test_models.py b/edx_name_affirmation/tests/test_models.py index fcef806..dc2aac5 100644 --- a/edx_name_affirmation/tests/test_models.py +++ b/edx_name_affirmation/tests/test_models.py @@ -12,6 +12,7 @@ User = get_user_model() + class VerifiedNameModelTests(TestCase): """ Test suite for the VerifiedName models