Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add IDV Status to VerifiedName #215

Merged
merged 10 commits into from
Sep 9, 2024
20 changes: 20 additions & 0 deletions edx_name_affirmation/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,16 @@
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

try:
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification
except ImportError:
SoftwareSecurePhotoVerification = None

User = get_user_model()


Expand Down Expand Up @@ -45,6 +51,20 @@ class Meta:
db_table = 'nameaffirmation_verifiedname'
verbose_name = 'verified name'

@property
def verification_attempt_status(self):
"Returns the status associated with its SoftwareSecurePhotoVerification with verification_attempt_id if any."

if not self.verification_attempt_id or not SoftwareSecurePhotoVerification:
return None

try:
verification = SoftwareSecurePhotoVerification.objects.get(id=self.verification_attempt_id)
return verification.status

except ObjectDoesNotExist:
return None


class VerifiedNameConfig(ConfigurationModel):
"""
Expand Down
3 changes: 2 additions & 1 deletion edx_name_affirmation/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ 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.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)

Expand All @@ -30,7 +31,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):
Expand Down
44 changes: 41 additions & 3 deletions edx_name_affirmation/tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
"""
Tests for Name Affirmation models
"""
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
Expand All @@ -16,6 +18,9 @@
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='[email protected]')
self.verified_name = VerifiedName.objects.create(
Expand All @@ -29,11 +34,11 @@
"""
Test the model history is recording records as expected
"""

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
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
Expand All @@ -44,4 +49,37 @@

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
"""

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 = 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})

Check failure on line 85 in edx_name_affirmation/tests/test_models.py

View workflow job for this annotation

GitHub Actions / Tests (ubuntu-20.04, 3.11, 10, django42)

Missing coverage

Missing coverage on line 85
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can omit the Missing coverage warning since this is just a mock for testing. I'm not sure why is this a thing on a test file.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure. I think it's fine to ignore!

22 changes: 22 additions & 0 deletions edx_name_affirmation/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
All tests for edx_name_affirmation views
"""
import json
from unittest.mock import PropertyMock, patch

import ddt

Expand Down Expand Up @@ -357,6 +358,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,
Expand All @@ -379,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(
Expand Down Expand Up @@ -456,6 +477,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,
rijuma marked this conversation as resolved.
Show resolved Hide resolved
'proctored_exam_attempt_id': verified_name_obj.proctored_exam_attempt_id,
'status': verified_name_obj.status
}
Expand Down
Loading