Files
edx-platform/lms/djangoapps/verify_student/tests/test_api.py
Isaac Lee a3871cda22 API functions for the new generic VerificationAttempt model in the verify_student app (#35338)
* 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>
2024-09-09 13:16:29 -04:00

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,
)