def test_passing_grade_allowlist(self): with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True): # User who is not on the allowlist GeneratedCertificateFactory(user=self.user, course_id=self.course.id, status=CertificateStatuses.error) with mock_passing_grade(): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate_task', return_value=None) as mock_cert_task: CourseGradeFactory().update(self.user, self.course) mock_cert_task.assert_called_with(self.user, self.course.id) # User who is on the allowlist u = UserFactory.create() c = CourseFactory() course_key = c.id # pylint: disable=no-member CertificateAllowlistFactory(user=u, course_id=course_key) GeneratedCertificateFactory(user=u, course_id=course_key, status=CertificateStatuses.error) with mock_passing_grade(): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate_task', return_value=None) as mock_cert_task: CourseGradeFactory().update(u, c) mock_cert_task.assert_called_with(u, course_key)
def test_ineligible_cert_whitelisted(self, disable_audit_cert, status): """ Test that audit mode students receive a certificate if DISABLE_AUDIT_CERTIFICATES feature is set to false """ # Enroll as audit CourseEnrollmentFactory(user=self.user_2, course_id=self.course.id, is_active=True, mode='audit') # Whitelist student CertificateWhitelistFactory(course_id=self.course.id, user=self.user_2) features = settings.FEATURES features['DISABLE_AUDIT_CERTIFICATES'] = disable_audit_cert with override_settings(FEATURES=features) and mock_passing_grade(): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: mock_send.return_value = (0, None) self.xqueue.add_cert(self.user_2, self.course.id) certificate = GeneratedCertificate.certificate_for_student( self.user_2, self.course.id) self.assertIsNotNone(certificate) self.assertEqual(certificate.mode, 'audit') self.assertEqual(certificate.status, status)
def test_with_downloadable_web_cert(self): CourseEnrollment.enroll(self.student, self.course.id, mode='honor') self._setup_course_certificate() with mock_passing_grade(): certs_api.generate_user_certificates(self.student, self.course.id) cert_status = certificate_status_for_student(self.student, self.course.id) self.assertEqual( certs_api.certificate_downloadable_status(self.student, self.course.id), { 'is_downloadable': True, 'is_generating': False, 'is_unverified': False, 'download_url': '/certificates/user/{user_id}/course/{course_id}'.format( user_id=self.student.id, # pylint: disable=no-member course_id=self.course.id, ), 'uuid': cert_status['uuid'] })
def test_web_certificate(self): CourseMode.objects.create( course_id=self.course.id, mode_display_name="Honor", mode_slug=CourseMode.HONOR, ) self.login_and_enroll() certificates = [{ 'id': 1, 'name': 'Test Certificate Name', 'description': 'Test Certificate Description', 'course_title': 'tes_course_title', 'signatories': [], 'version': 1, 'is_active': True }] self.course.certificates = {'certificates': certificates} self.course.cert_html_view_enabled = True self.store.update_item(self.course, self.user.id) with mock_passing_grade(): generate_user_certificates(self.user, self.course.id) response = self.api_response() certificate_data = response.data[0]['certificate'] self.assertRegexpMatches( certificate_data['url'], r'http.*/certificates/user/{user_id}/course/{course_id}'.format( user_id=self.user.id, course_id=self.course.id, ))
def test_regen_audit_certs_eligibility(self, status, created_delta, grade, expected_status): """ Test that existing audit certificates remain eligible even if cert generation is re-run. """ # Create an existing audit enrollment and certificate CourseEnrollmentFactory( user=self.user_2, course_id=self.course.id, is_active=True, mode=CourseMode.AUDIT, ) created_date = datetime.now(pytz.UTC) + created_delta with freezegun.freeze_time(created_date): GeneratedCertificateFactory( user=self.user_2, course_id=self.course.id, grade='1.0', status=status, mode=GeneratedCertificate.MODES.audit, ) # Run grading/cert generation again with mock_passing_grade(letter_grade=grade): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: mock_send.return_value = (0, None) self.xqueue.add_cert(self.user_2, self.course.id) self.assertEqual( GeneratedCertificate.objects.get(user=self.user_2, course_id=self.course.id).status, expected_status)
def test_course_grade_results(self, mock_get_programs): grade_percent = .8 with mock_passing_grade(percent=grade_percent): course_run_key = generate_course_run_key() data = [ ProgramFactory( courses=[ CourseFactory(course_runs=[ CourseRunFactory(key=course_run_key), ]), ] ) ] mock_get_programs.return_value = data self._create_enrollments(course_run_key) meter = ProgramProgressMeter(self.site, self.user) program = data[0] expected = [ ProgressFactory( uuid=program['uuid'], completed=[], in_progress=[program['courses'][0]], not_started=[], grades={course_run_key: grade_percent}, ) ] self.assertEqual(meter.progress(count_only=False), expected)
def test_regen_audit_certs_eligibility(self, status, created_date, grade, expected_status): """ Test that existing audit certificates remain eligible even if cert generation is re-run. """ # Create an existing audit enrollment and certificate CourseEnrollmentFactory( user=self.user_2, course_id=self.course.id, is_active=True, mode=CourseMode.AUDIT, ) with freezegun.freeze_time(created_date): GeneratedCertificateFactory( user=self.user_2, course_id=self.course.id, grade='1.0', status=status, mode=GeneratedCertificate.MODES.audit, ) # Run grading/cert generation again with mock_passing_grade(grade_pass=grade): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: mock_send.return_value = (0, None) self.xqueue.add_cert(self.user_2, self.course.id) self.assertEqual( GeneratedCertificate.objects.get(user=self.user_2, course_id=self.course.id).status, # pylint: disable=no-member expected_status )
def test_xqueue_submit_task_error(self): with mock_passing_grade(): with self._mock_queue(is_successful=False): certs_api.generate_user_certificates(self.student, self.course.id) # Verify that the certificate has been marked with status error cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id) self.assertEqual(cert.status, 'error') self.assertIn(self.ERROR_REASON, cert.error_reason)
def test_send_grade_queries_grade(self, mock_is_course_run_in_a_program, mock_send_grade_to_credentials, _mock_is_learner_issuance_enabled): mock_is_course_run_in_a_program.return_value = True with mock_passing_grade('B', 0.81): tasks.send_grade_if_interesting(self.user, self.key, 'verified', 'downloadable', None, None) assert mock_send_grade_to_credentials.delay.called assert mock_send_grade_to_credentials.delay.call_args[0] == (self.user.username, str(self.key), True, 'B', 0.81) mock_send_grade_to_credentials.delay.reset_mock()
def test_send_grade_queries_grade(self, mock_is_course_run_in_a_program, mock_send_grade_to_credentials): mock_is_course_run_in_a_program.return_value = True with mock_passing_grade('B', 0.81): send_grade_if_interesting(self.user, self.key, 'verified', 'downloadable', None, None) self.assertTrue(mock_send_grade_to_credentials.delay.called) self.assertEqual(mock_send_grade_to_credentials.delay.call_args[0], (self.user.username, str(self.key), True, 'B', 0.81)) mock_send_grade_to_credentials.delay.reset_mock()
def test_without_cert(self): with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate_task', return_value=None) as mock_cert_task: grade_factory = CourseGradeFactory() with mock_passing_grade(): grade_factory.update(self.user, self.course) mock_cert_task.assert_called_with(self.user, self.course_key)
def test_new_cert_request_for_html_certificate(self): """ Test generate_user_certificates with HTML certificates """ self._setup_course_certificate() with mock_passing_grade(): generate_user_certificates(self.student, self.course.id) cert = GeneratedCertificate.eligible_certificates.get( user=self.student, course_id=self.course.id) assert cert.status == CertificateStatuses.downloadable
def test_new_cert_requests_returns_generating_for_html_certificate(self): """ Test no message sent to Xqueue if HTML certificate view is enabled """ self._setup_course_certificate() with mock_passing_grade(): certs_api.generate_user_certificates(self.student, self.course.id) # Verify that the certificate has status 'downloadable' cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id) self.assertEqual(cert.status, CertificateStatuses.downloadable)
def test_add_cert_callback_url(self): with mock_passing_grade(): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: mock_send.return_value = (0, None) self.xqueue.add_cert(self.user, self.course.id) # Verify that the task was sent to the queue with the correct callback URL self.assertTrue(mock_send.called) __, kwargs = mock_send.call_args_list[0] actual_header = json.loads(kwargs['header']) self.assertIn('https://edx.org/update_certificate?key=', actual_header['lms_callback_url'])
def test_no_create_action_in_queue_for_html_view_certs(self): """ Tests there is no certificate create message in the queue if generate_pdf is False """ with mock_passing_grade(): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: self.xqueue.add_cert(self.user, self.course.id, generate_pdf=False) # Verify that add_cert method does not add message to queue self.assertFalse(mock_send.called) certificate = GeneratedCertificate.eligible_certificates.get(user=self.user, course_id=self.course.id) self.assertEqual(certificate.status, CertificateStatuses.downloadable) self.assertIsNotNone(certificate.verify_uuid)
def test_grade(self, grade): """ Test that the user gets her grade in case she answered tests with an insufficient score. """ with mock_passing_grade(letter_grade=grade['letter_grade'], percent=grade['percent']): resp = self.client.get(self.get_url(self.student.username)) self.assertEqual(resp.status_code, status.HTTP_200_OK) expected_data = { 'username': self.student.username, 'course_key': str(self.course_key), } expected_data.update(grade) self.assertEqual(resp.data, [expected_data])
def test_cert_already_generated_unverified(self): with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True): GeneratedCertificateFactory(user=self.user, course_id=self.course.id, status=CertificateStatuses.unverified) with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate_task', return_value=None) as mock_cert_task: grade_factory = CourseGradeFactory() with mock_passing_grade(): grade_factory.update(self.user, self.course) mock_cert_task.assert_called_with(self.user, self.course_key)
def test_ungenerated_certificate(self, mock_send_to_queue): """ Given that I have ended course If I run ungenerated certs command Then certificates should be generated for all users who passed course """ mock_send_to_queue.return_value = (0, "Successfully queued") key = self.course.location.course_key self._create_cert(key, self.user, CertificateStatuses.unavailable) with mock_passing_grade(): self._run_command(course=unicode(key), noop=False, insecure=True, force=False) self.assertTrue(mock_send_to_queue.called) certificate = GeneratedCertificate.eligible_certificates.get(user=self.user, course_id=key) self.assertEqual(certificate.status, CertificateStatuses.generating)
def test_cert_already_generated(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', return_value=None) as mock_generate_certificate_apply_async: grade_factory = CourseGradeFactory() # Create the certificate GeneratedCertificateFactory( user=self.user, course_id=self.course.id, status=CertificateStatuses.downloadable) # Certs are not re-fired after passing with mock_passing_grade(): grade_factory.update(self.user, self.course) mock_generate_certificate_apply_async.assert_not_called()
def test_cert_generation_on_passing_self_paced(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', return_value=None) as mock_generate_certificate_apply_async: with waffle.waffle().override(waffle.SELF_PACED_ONLY, active=True): grade_factory = CourseGradeFactory() # Not passing grade_factory.update(self.user, self.course) mock_generate_certificate_apply_async.assert_not_called() # Certs fired after passing with mock_passing_grade(): grade_factory.update(self.user, self.course) mock_generate_certificate_apply_async.assert_called_with( student=self.user, course_key=self.course.id)
def test_with_downloadable_web_cert(self): CourseEnrollment.enroll(self.student, self.course.id, mode='honor') self._setup_course_certificate() with mock_passing_grade(): generate_user_certificates(self.student, self.course.id) cert_status = certificate_status_for_student(self.student, self.course.id) assert certificate_downloadable_status(self.student, self.course.id) ==\ {'is_downloadable': True, 'is_generating': False, 'is_unverified': False, 'download_url': f'/certificates/{cert_status["uuid"]}', 'is_pdf_certificate': False, 'uuid': cert_status['uuid']}
def test_cert_generation_on_photo_verification_self_paced(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', return_value=None) as mock_generate_certificate_apply_async: with mock_passing_grade(): grade_factory = CourseGradeFactory() grade_factory.update(self.user_one, self.course_one) with waffle.waffle().override(waffle.SELF_PACED_ONLY, active=True): mock_generate_certificate_apply_async.assert_not_called() attempt = SoftwareSecurePhotoVerification.objects.create( user=self.user_one, status='submitted') attempt.approve() mock_generate_certificate_apply_async.assert_called_with( student=self.user_one, course_key=self.course_one.id)
def test_regenerate_certificate_for_honor_mode(self, mock_generate_cert): """Test web certificate regeneration for the users who have earned the certificate in honor mode """ self.cert.mode = 'honor' self.cert.download_url = '' self.cert.save() with mock_passing_grade(percent=0.75): with patch('common.djangoapps.course_modes.models.CourseMode.mode_for_course') as mock_mode_for_course: mock_mode_for_course.return_value = 'honor' regenerate_user_certificates(self.student, self.course_key, course=self.course) mock_generate_cert.assert_called()
def test_cert_already_generated(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', return_value=None ) as mock_generate_certificate_apply_async: grade_factory = CourseGradeFactory() # Create the certificate GeneratedCertificate.eligible_certificates.create( user=self.user, course_id=self.course.id, status=CertificateStatuses.downloadable ) # Certs are not re-fired after passing with mock_passing_grade(): grade_factory.update(self.user, self.course) mock_generate_certificate_apply_async.assert_not_called()
def test_generate_user_certificates_with_unverified_cert_status(self): """ Generate user certificate will not raise exception in case of certificate is None. """ # generate certificate with unverified status. GeneratedCertificateFactory.create( user=self.student, course_id=self.course.id, status=CertificateStatuses.unverified, mode='verified' ) with mock_passing_grade(): with self._mock_queue(is_successful=False): status = certs_api.generate_user_certificates(self.student, self.course.id) self.assertEqual(status, None)
def test_new_cert_requests_into_xqueue_returns_generating(self): with mock_passing_grade(): with self._mock_queue(): certs_api.generate_user_certificates(self.student, self.course.id) # Verify that the certificate has status 'generating' cert = GeneratedCertificate.eligible_certificates.get(user=self.student, course_id=self.course.id) self.assertEqual(cert.status, CertificateStatuses.generating) self.assert_event_emitted( 'edx.certificate.created', user_id=self.student.id, course_id=unicode(self.course.id), certificate_url=certs_api.get_certificate_url(self.student.id, self.course.id), certificate_id=cert.verify_uuid, enrollment_mode=cert.mode, generation_mode='batch' )
def test_cert_api_return(self, self_paced, cert_avail_delta, cert_downloadable_status, earned_but_not_available): """ Test 'downloadable status' """ cert_avail_date = datetime.now(pytz.UTC) + cert_avail_delta self.course.self_paced = self_paced self.course.certificate_available_date = cert_avail_date self.course.save() CourseEnrollment.enroll(self.student, self.course.id, mode='honor') self._setup_course_certificate() with mock_passing_grade(): certs_api.generate_user_certificates(self.student, self.course.id) downloadable_status = certs_api.certificate_downloadable_status(self.student, self.course.id) self.assertEqual(downloadable_status['is_downloadable'], cert_downloadable_status) self.assertEqual(downloadable_status.get('earned_but_not_available'), earned_but_not_available)
def test_web_certificate(self): CourseMode.objects.create( course_id=self.course.id, mode_display_name="Honor", mode_slug=CourseMode.HONOR, ) self.login_and_enroll() self.course.cert_html_view_enabled = True self.store.update_item(self.course, self.user.id) with mock_passing_grade(): generate_user_certificates(self.user, self.course.id) response = self.api_response() certificate_data = response.data[0]['certificate'] self.assertRegex(certificate_data['url'], r'http.*/certificates/[0-9a-f]{32}')
def test_cert_api_return(self, self_paced, cert_avail_date, cert_downloadable_status): """ Test 'downloadable status' """ self.course.self_paced = self_paced self.course.certificate_available_date = cert_avail_date self.course.save() CourseEnrollment.enroll(self.student, self.course.id, mode='honor') self._setup_course_certificate() with mock_passing_grade(): certs_api.generate_user_certificates(self.student, self.course.id) self.assertEqual( certs_api.certificate_downloadable_status(self.student, self.course.id)['is_downloadable'], cert_downloadable_status )
def test_cert_api_return(self, self_paced, cert_avail_date, cert_downloadable_status): """ Test 'downloadable status' """ self.course.self_paced = self_paced self.course.certificate_available_date = cert_avail_date self.course.save() CourseEnrollment.enroll(self.student, self.course.id, mode='honor') self._setup_course_certificate() with mock_passing_grade(): certs_api.generate_user_certificates(self.student, self.course.id) self.assertEqual( certs_api.certificate_downloadable_status( self.student, self.course.id)['is_downloadable'], cert_downloadable_status)
def add_cert_to_queue(self, mode): """ Dry method for course enrollment and adding request to queue. Returns a mock object containing information about the `XQueueInterface.send_to_queue` method, which can be used in other assertions. """ CourseEnrollmentFactory( user=self.user_2, course_id=self.course.id, is_active=True, mode=mode, ) with mock_passing_grade(): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: mock_send.return_value = (0, None) self.xqueue.add_cert(self.user_2, self.course.id) return mock_send
def test_generate_user_certificates_with_unverified_cert_status(self): """ Generate user certificate when the certificate is unverified will trigger an update to the certificate if the user has since verified. """ # generate certificate with unverified status. GeneratedCertificateFactory.create( user=self.student, course_id=self.course.id, status=CertificateStatuses.unverified, mode='verified' ) with mock_passing_grade(): with self._mock_queue(): status = certs_api.generate_user_certificates(self.student, self.course.id) self.assertEqual(status, 'generating')
def test_ungenerated_certificate(self, mock_send_to_queue): """ Given that I have ended course If I run ungenerated certs command Then certificates should be generated for all users who passed course """ mock_send_to_queue.return_value = (0, "Successfully queued") key = self.course.location.course_key self._create_cert(key, self.user, CertificateStatuses.unavailable) with mock_passing_grade(): self._run_command(course=unicode(key), noop=False, insecure=True, force=False) self.assertTrue(mock_send_to_queue.called) certificate = GeneratedCertificate.eligible_certificates.get( user=self.user, course_id=key) self.assertEqual(certificate.status, CertificateStatuses.generating)
def test_ungenerated_certificate(self, mock_send_to_queue): """ Given that I have ended course If I run ungenerated certs command Then certificates should be generated for all users who passed course """ mock_send_to_queue.return_value = (0, "Successfully queued") key = self.course.location.course_key self._create_cert(key, self.user, CertificateStatuses.unavailable) with mock_passing_grade(): args = u'-c {} --insecure'.format(text_type(key)) call_command(self.command, *args.split(' ')) self.assertTrue(mock_send_to_queue.called) certificate = GeneratedCertificate.eligible_certificates.get( user=self.user, course_id=key) self.assertEqual(certificate.status, CertificateStatuses.generating)
def test_web_certificate(self): CourseMode.objects.create(course_id=self.course.id, mode_display_name="Honor", mode_slug=CourseMode.HONOR) self.login_and_enroll() self.course.cert_html_view_enabled = True self.store.update_item(self.course, self.user.id) with mock_passing_grade(): generate_user_certificates(self.user, self.course.id) response = self.api_response() certificate_data = response.data[0]["certificate"] self.assertRegexpMatches( certificate_data["url"], r"http.*/certificates/user/{user_id}/course/{course_id}".format( user_id=self.user.id, course_id=self.course.id ), )
def test_cert_generation_on_passing_instructor_paced(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', return_value=None ) as mock_generate_certificate_apply_async: with waffle.waffle().override(waffle.AUTO_CERTIFICATE_GENERATION, active=True): grade_factory = CourseGradeFactory() # Not passing grade_factory.update(self.user, self.ip_course) mock_generate_certificate_apply_async.assert_not_called() # Certs fired after passing with mock_passing_grade(): grade_factory.update(self.user, self.ip_course) mock_generate_certificate_apply_async.assert_called_with( countdown=CERTIFICATE_DELAY_SECONDS, kwargs={ 'student': unicode(self.user.id), 'course_key': unicode(self.ip_course.id), } )
def test_with_downloadable_web_cert(self): CourseEnrollment.enroll(self.student, self.course.id, mode='honor') self._setup_course_certificate() with mock_passing_grade(): certs_api.generate_user_certificates(self.student, self.course.id) cert_status = certificate_status_for_student(self.student, self.course.id) self.assertEqual( certs_api.certificate_downloadable_status(self.student, self.course.id), { 'is_downloadable': True, 'is_generating': False, 'is_unverified': False, 'download_url': '/certificates/user/{user_id}/course/{course_id}'.format( user_id=self.student.id, # pylint: disable=no-member course_id=self.course.id, ), 'uuid': cert_status['uuid'] } )
def test_ungenerated_certificate(self, mock_send_to_queue): """ Given that I have ended course If I run ungenerated certs command Then certificates should be generated for all users who passed course """ mock_send_to_queue.return_value = (0, "Successfully queued") key = self.course.location.course_key self._create_cert(key, self.user, CertificateStatuses.unavailable) with mock_passing_grade(): args = '-c {} --insecure'.format(text_type(key)) call_command(self.command, *args.split(' ')) self.assertTrue(mock_send_to_queue.called) certificate = GeneratedCertificate.eligible_certificates.get( user=self.user, course_id=key ) self.assertEqual(certificate.status, CertificateStatuses.generating)
def setUp(self): super(LearnerTrackChangeCertsTest, self).setUp() self.course_one = CourseFactory.create(self_paced=True) self.user_one = UserFactory.create() self.enrollment_one = CourseEnrollmentFactory( user=self.user_one, course_id=self.course_one.id, is_active=True, mode='verified', ) self.user_two = UserFactory.create() self.course_two = CourseFactory.create(self_paced=False) self.enrollment_two = CourseEnrollmentFactory( user=self.user_two, course_id=self.course_two.id, is_active=True, mode='verified' ) with mock_passing_grade(): grade_factory = CourseGradeFactory() grade_factory.update(self.user_one, self.course_one) grade_factory.update(self.user_two, self.course_two)
def test_ineligible_cert_whitelisted(self): """Test that audit mode students can receive a certificate if they are whitelisted.""" # Enroll as audit CourseEnrollmentFactory( user=self.user_2, course_id=self.course.id, is_active=True, mode='audit' ) # Whitelist student CertificateWhitelistFactory(course_id=self.course.id, user=self.user_2) # Generate certs with mock_passing_grade(): with patch.object(XQueueInterface, 'send_to_queue') as mock_send: mock_send.return_value = (0, None) self.xqueue.add_cert(self.user_2, self.course.id) # Assert cert generated correctly self.assertTrue(mock_send.called) certificate = GeneratedCertificate.certificate_for_student(self.user_2, self.course.id) self.assertIsNotNone(certificate) self.assertEqual(certificate.mode, 'audit')