* feat: add VerificationAttempt model to verify_student application This commits adds a VerificationAttempt model to store implementation and provider agnostic information about identity verification attempts in the platform. * feat: add api for VerificationAttempt model * fix: error handling for update - added tests accordingly - also took care of some nits * chore: lint * chore: lint for equals spaces * feat: using generic update function instead - can now update name, status, and exp. date on generic attempts - changed tests accordingly - a few nits * chore: fix docstring args * fix: corrected status validation - reverted to old status validation method - fixed tests accordingly * fix: datetime, status, and annotation fixes - expiration_datetime can be updated to None - VerificationAttemptStatus is now StrEnum - Added type annotations for api functions --------- Co-authored-by: michaelroytman <mroytman@edx.org>
187 lines
6.6 KiB
Python
187 lines
6.6 KiB
Python
"""
|
|
Tests of API module.
|
|
"""
|
|
from unittest.mock import patch
|
|
|
|
from datetime import datetime, timezone
|
|
import ddt
|
|
from django.conf import settings
|
|
from django.core import mail
|
|
from django.test import TestCase
|
|
|
|
from common.djangoapps.student.tests.factories import UserFactory
|
|
from lms.djangoapps.verify_student.api import (
|
|
create_verification_attempt,
|
|
send_approval_email,
|
|
update_verification_attempt,
|
|
)
|
|
from lms.djangoapps.verify_student.exceptions import VerificationAttemptInvalidStatus
|
|
from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification, VerificationAttempt
|
|
from lms.djangoapps.verify_student.statuses import VerificationAttemptStatus
|
|
|
|
|
|
@ddt.ddt
|
|
class TestSendApprovalEmail(TestCase):
|
|
"""
|
|
Test cases for the send_approval_email API method.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
self.user = UserFactory.create()
|
|
self.attempt = SoftwareSecurePhotoVerification(
|
|
status="submitted",
|
|
user=self.user
|
|
)
|
|
self.attempt.save()
|
|
|
|
def _assert_verification_approved_email(self, expiration_date):
|
|
"""Check that a verification approved email was sent."""
|
|
assert len(mail.outbox) == 1
|
|
email = mail.outbox[0]
|
|
assert email.subject == 'Your édX ID verification was approved!'
|
|
assert 'Your édX ID verification photos have been approved' in email.body
|
|
assert expiration_date.strftime("%m/%d/%Y") in email.body
|
|
|
|
@ddt.data(True, False)
|
|
def test_send_approval(self, use_ace):
|
|
with patch.dict(settings.VERIFY_STUDENT, {'USE_DJANGO_MAIL': use_ace}):
|
|
send_approval_email(self.attempt)
|
|
self._assert_verification_approved_email(self.attempt.expiration_datetime)
|
|
|
|
|
|
@ddt.ddt
|
|
class CreateVerificationAttempt(TestCase):
|
|
"""
|
|
Test cases for the create_verification_attempt API method.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
self.user = UserFactory.create()
|
|
self.attempt = VerificationAttempt(
|
|
user=self.user,
|
|
name='Tester McTest',
|
|
status=VerificationAttemptStatus.CREATED,
|
|
expiration_datetime=datetime(2024, 12, 31, tzinfo=timezone.utc)
|
|
)
|
|
self.attempt.save()
|
|
|
|
def test_create_verification_attempt(self):
|
|
expected_id = 2
|
|
self.assertEqual(
|
|
create_verification_attempt(
|
|
user=self.user,
|
|
name='Tester McTest',
|
|
status=VerificationAttemptStatus.CREATED,
|
|
expiration_datetime=datetime(2024, 12, 31, tzinfo=timezone.utc)
|
|
),
|
|
expected_id
|
|
)
|
|
verification_attempt = VerificationAttempt.objects.get(id=expected_id)
|
|
|
|
self.assertEqual(verification_attempt.user, self.user)
|
|
self.assertEqual(verification_attempt.name, 'Tester McTest')
|
|
self.assertEqual(verification_attempt.status, VerificationAttemptStatus.CREATED)
|
|
self.assertEqual(verification_attempt.expiration_datetime, datetime(2024, 12, 31, tzinfo=timezone.utc))
|
|
|
|
def test_create_verification_attempt_no_expiration_datetime(self):
|
|
expected_id = 2
|
|
self.assertEqual(
|
|
create_verification_attempt(
|
|
user=self.user,
|
|
name='Tester McTest',
|
|
status=VerificationAttemptStatus.CREATED,
|
|
),
|
|
expected_id
|
|
)
|
|
verification_attempt = VerificationAttempt.objects.get(id=expected_id)
|
|
|
|
self.assertEqual(verification_attempt.user, self.user)
|
|
self.assertEqual(verification_attempt.name, 'Tester McTest')
|
|
self.assertEqual(verification_attempt.status, VerificationAttemptStatus.CREATED)
|
|
self.assertEqual(verification_attempt.expiration_datetime, None)
|
|
|
|
|
|
@ddt.ddt
|
|
class UpdateVerificationAttempt(TestCase):
|
|
"""
|
|
Test cases for the update_verification_attempt API method.
|
|
"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
|
|
self.user = UserFactory.create()
|
|
self.attempt = VerificationAttempt(
|
|
user=self.user,
|
|
name='Tester McTest',
|
|
status=VerificationAttemptStatus.CREATED,
|
|
expiration_datetime=datetime(2024, 12, 31, tzinfo=timezone.utc)
|
|
)
|
|
self.attempt.save()
|
|
|
|
@ddt.data(
|
|
('Tester McTest', VerificationAttemptStatus.PENDING, datetime(2024, 12, 31, tzinfo=timezone.utc)),
|
|
('Tester McTest2', VerificationAttemptStatus.APPROVED, datetime(2025, 12, 31, tzinfo=timezone.utc)),
|
|
('Tester McTest3', VerificationAttemptStatus.DENIED, datetime(2026, 12, 31, tzinfo=timezone.utc)),
|
|
)
|
|
@ddt.unpack
|
|
def test_update_verification_attempt(self, name, status, expiration_datetime):
|
|
update_verification_attempt(
|
|
attempt_id=self.attempt.id,
|
|
name=name,
|
|
status=status,
|
|
expiration_datetime=expiration_datetime,
|
|
)
|
|
|
|
verification_attempt = VerificationAttempt.objects.get(id=self.attempt.id)
|
|
|
|
# Values should change as a result of this update.
|
|
self.assertEqual(verification_attempt.user, self.user)
|
|
self.assertEqual(verification_attempt.name, name)
|
|
self.assertEqual(verification_attempt.status, status)
|
|
self.assertEqual(verification_attempt.expiration_datetime, expiration_datetime)
|
|
|
|
def test_update_verification_attempt_none_values(self):
|
|
update_verification_attempt(
|
|
attempt_id=self.attempt.id,
|
|
name=None,
|
|
status=None,
|
|
expiration_datetime=None,
|
|
)
|
|
|
|
verification_attempt = VerificationAttempt.objects.get(id=self.attempt.id)
|
|
|
|
# Values should not change as a result of the values passed in being None, except for expiration_datetime.
|
|
self.assertEqual(verification_attempt.user, self.user)
|
|
self.assertEqual(verification_attempt.name, self.attempt.name)
|
|
self.assertEqual(verification_attempt.status, self.attempt.status)
|
|
self.assertEqual(verification_attempt.expiration_datetime, None)
|
|
|
|
def test_update_verification_attempt_not_found(self):
|
|
self.assertRaises(
|
|
VerificationAttempt.DoesNotExist,
|
|
update_verification_attempt,
|
|
attempt_id=999999,
|
|
status=VerificationAttemptStatus.APPROVED,
|
|
)
|
|
|
|
@ddt.data(
|
|
'completed',
|
|
'failed',
|
|
'submitted',
|
|
'expired',
|
|
)
|
|
def test_update_verification_attempt_invalid(self, status):
|
|
self.assertRaises(
|
|
VerificationAttemptInvalidStatus,
|
|
update_verification_attempt,
|
|
attempt_id=self.attempt.id,
|
|
name=None,
|
|
status=status,
|
|
expiration_datetime=None,
|
|
)
|