Files
edx-platform/lms/djangoapps/certificates/tests/test_generation.py

230 lines
9.5 KiB
Python

"""
Tests for certificate generation
"""
import ddt
import logging # lint-amnesty, pylint: disable=wrong-import-order
from unittest import mock, skipUnless # lint-amnesty, pylint: disable=wrong-import-order
from common.djangoapps.course_modes.models import CourseMode
from common.djangoapps.student.models import UserProfile
from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory
from common.djangoapps.util.testing import EventTestMixin
from lms.djangoapps.certificates.data import CertificateStatuses
from lms.djangoapps.certificates.generation import generate_course_certificate
from lms.djangoapps.certificates.models import GeneratedCertificate
from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory
from openedx.features.name_affirmation_api.utils import get_name_affirmation_service
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order
from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order
log = logging.getLogger(__name__)
PROFILE_NAME_METHOD = 'common.djangoapps.student.models_api.get_name'
name_affirmation_service = get_name_affirmation_service()
@ddt.ddt
class CertificateTests(EventTestMixin, ModuleStoreTestCase):
"""
Tests for certificate generation
"""
def setUp(self): # pylint: disable=arguments-differ
super().setUp('lms.djangoapps.certificates.utils.tracker')
# Create user, a course run, and an enrollment
self.u = UserFactory()
self.profile = UserProfile.objects.get(user_id=self.u.id)
self.name = self.profile.name
self.cr = CourseFactory()
self.key = self.cr.id # pylint: disable=no-member
CourseEnrollmentFactory(
user=self.u,
course_id=self.key,
is_active=True,
mode=CourseMode.VERIFIED,
)
self.gen_mode = 'batch'
self.grade = '.85'
self.enrollment_mode = CourseMode.VERIFIED
def test_generation(self):
"""
Test certificate generation
"""
certs = GeneratedCertificate.objects.filter(user=self.u, course_id=self.key)
assert len(certs) == 0
generated_cert = generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable,
self.enrollment_mode, self.grade, self.gen_mode)
self.assert_event_emitted(
'edx.certificate.created',
user_id=self.u.id,
course_id=str(self.key),
certificate_id=generated_cert.verify_uuid,
enrollment_mode=generated_cert.mode,
certificate_url='',
generation_mode=self.gen_mode
)
certs = GeneratedCertificate.objects.filter(user=self.u, course_id=self.key)
assert len(certs) == 1
cert = GeneratedCertificate.objects.get(user=self.u, course_id=self.key)
assert cert.status == CertificateStatuses.downloadable
assert cert.mode == self.enrollment_mode
assert cert.grade == self.grade
assert cert.name == self.name
def test_generation_existing_unverified(self):
"""
Test certificate generation when a certificate already exists and we want to mark it as unverified
"""
error_reason = 'Some PDF error'
GeneratedCertificateFactory(
user=self.u,
course_id=self.key,
mode=CourseMode.AUDIT,
status=CertificateStatuses.error,
error_reason=error_reason
)
cert = GeneratedCertificate.objects.get(user=self.u, course_id=self.key)
assert cert.error_reason == error_reason
assert cert.mode == CourseMode.AUDIT
assert cert.status == CertificateStatuses.error
generate_course_certificate(self.u, self.key, CertificateStatuses.unverified, self.enrollment_mode, self.grade,
self.gen_mode)
cert = GeneratedCertificate.objects.get(user=self.u, course_id=self.key)
assert cert.error_reason == ''
assert cert.status == CertificateStatuses.unverified
assert cert.mode == self.enrollment_mode
assert cert.grade == ''
def test_generation_existing_downloadable(self):
"""
Test certificate generation when a certificate already exists and we want to mark it as downloadable
"""
error_reason = 'Some PDF error'
GeneratedCertificateFactory(
user=self.u,
course_id=self.key,
mode=CourseMode.AUDIT,
status=CertificateStatuses.error,
error_reason=error_reason
)
generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable, self.enrollment_mode,
self.grade, self.gen_mode)
cert = GeneratedCertificate.objects.get(user=self.u, course_id=self.key)
assert cert.error_reason == ''
assert cert.status == CertificateStatuses.downloadable
assert cert.mode == self.enrollment_mode
assert cert.grade == self.grade
def test_generation_uuid_persists_through_revocation(self):
"""
Test that the `verify_uuid` value of a certificate does not change when it is revoked and re-awarded.
"""
generated_cert = generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable,
self.enrollment_mode, self.grade, self.gen_mode)
assert generated_cert.status, CertificateStatuses.downloadable
verify_uuid = generated_cert.verify_uuid
generated_cert.invalidate()
assert generated_cert.status, CertificateStatuses.unavailable
assert generated_cert.verify_uuid, verify_uuid
generated_cert = generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable,
self.enrollment_mode, self.grade, self.gen_mode)
assert generated_cert.status, CertificateStatuses.downloadable
assert generated_cert.verify_uuid, verify_uuid
generated_cert.mark_notpassing(mode=generated_cert.mode, grade=50.00)
assert generated_cert.status, CertificateStatuses.notpassing
assert generated_cert.verify_uuid, verify_uuid
generated_cert = generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable,
self.enrollment_mode, self.grade, self.gen_mode)
assert generated_cert.status, CertificateStatuses.downloadable
assert generated_cert.verify_uuid, verify_uuid
def test_generation_creates_verify_uuid_when_needed(self):
"""
Test that ensures we will create a verify_uuid when needed.
"""
GeneratedCertificateFactory(
user=self.u,
course_id=self.key,
mode=CourseMode.VERIFIED,
status=CertificateStatuses.unverified,
verify_uuid=''
)
generated_cert = generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable,
self.enrollment_mode, self.grade, self.gen_mode)
assert generated_cert.status, CertificateStatuses.downloadable
assert generated_cert.verify_uuid != ''
def test_generation_missing_profile(self):
"""
Test certificate generation when the user profile is missing
"""
GeneratedCertificateFactory(
user=self.u,
course_id=self.key,
mode=CourseMode.AUDIT,
status=CertificateStatuses.unverified
)
with mock.patch(PROFILE_NAME_METHOD, return_value=None):
generate_course_certificate(self.u, self.key, CertificateStatuses.downloadable, self.enrollment_mode,
self.grade, self.gen_mode)
cert = GeneratedCertificate.objects.get(user=self.u, course_id=self.key)
assert cert.status == CertificateStatuses.downloadable
assert cert.mode == self.enrollment_mode
assert cert.grade == self.grade
assert cert.name == ''
@skipUnless(name_affirmation_service is not None, 'Requires Name Affirmation')
@ddt.data((True, 'approved'),
(True, 'denied'),
(False, 'pending'))
@ddt.unpack
def test_generation_verified_name(self, should_use_verified_name_for_certs, status):
"""
Test that if the user has their preference set to use
verified name for certificates, their verified name will appear on the certificate rather than
their profile name.
"""
verified_name = 'Jonathan Doe'
name_affirmation_service.create_verified_name(self.u, verified_name, self.name, status=status)
name_affirmation_service.create_verified_name_config(
self.u,
use_verified_name_for_certs=should_use_verified_name_for_certs
)
GeneratedCertificateFactory(
user=self.u,
course_id=self.key,
mode=CourseMode.AUDIT,
status=CertificateStatuses.unverified
)
generate_course_certificate(
self.u, self.key, CertificateStatuses.downloadable, self.enrollment_mode, self.grade, self.gen_mode,
)
cert = GeneratedCertificate.objects.get(user=self.u, course_id=self.key)
if should_use_verified_name_for_certs and status == 'approved':
assert cert.name == verified_name
else:
assert cert.name == self.name