diff --git a/lms/djangoapps/certificates/tests/test_signals.py b/lms/djangoapps/certificates/tests/test_signals.py index 7c3cfda88a..df453dddd5 100644 --- a/lms/djangoapps/certificates/tests/test_signals.py +++ b/lms/djangoapps/certificates/tests/test_signals.py @@ -23,7 +23,7 @@ from lms.djangoapps.certificates.tasks import CERTIFICATE_DELAY_SECONDS from lms.djangoapps.certificates.tests.factories import CertificateAllowlistFactory, GeneratedCertificateFactory from lms.djangoapps.grades.course_grade_factory import CourseGradeFactory from lms.djangoapps.grades.tests.utils import mock_passing_grade -from lms.djangoapps.verify_student.models import IDVerificationAttempt, SoftwareSecurePhotoVerification +from lms.djangoapps.verify_student.models import SoftwareSecurePhotoVerification from openedx.core.djangoapps.certificates.config import waffle from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -147,28 +147,7 @@ class PassingGradeCertsTest(ModuleStoreTestCase): ) attempt.approve() - 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 override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, 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( - countdown=CERTIFICATE_DELAY_SECONDS, - kwargs={ - 'student': str(self.user.id), - 'course_key': str(self.course.id), - } - ) - - def test_cert_generation_on_passing_instructor_paced(self): + def test_cert_generation_on_passing_v1(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', return_value=None @@ -377,9 +356,9 @@ class FailingGradeCertsTest(ModuleStoreTestCase): assert cert.status == CertificateStatuses.downloadable -class LearnerTrackChangeCertsTest(ModuleStoreTestCase): +class LearnerIdVerificationTest(ModuleStoreTestCase): """ - Tests for certificate generation task firing on learner verification + Tests for certificate generation task firing on learner id verification """ def setUp(self): @@ -405,45 +384,37 @@ class LearnerTrackChangeCertsTest(ModuleStoreTestCase): grade_factory.update(self.user_one, self.course_one) grade_factory.update(self.user_two, self.course_two) - def test_cert_generation_on_photo_verification_self_paced(self): + @override_waffle_flag(CERTIFICATES_USE_UPDATED, active=True) + def test_cert_generation_on_photo_verification(self): with mock.patch( - 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', + 'lms.djangoapps.certificates.signals.generate_certificate_task', return_value=None - ) as mock_generate_certificate_apply_async: + ) as mock_cert_task: with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, 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( - countdown=CERTIFICATE_DELAY_SECONDS, - kwargs={ - 'student': str(self.user_one.id), - 'course_key': str(self.course_one.id), - 'expected_verification_status': IDVerificationAttempt.STATUS.approved, - } - ) - - def test_cert_generation_on_photo_verification_instructor_paced(self): - with mock.patch( - 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', - return_value=None - ) as mock_generate_certificate_apply_async: - with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): - mock_generate_certificate_apply_async.assert_not_called() attempt = SoftwareSecurePhotoVerification.objects.create( user=self.user_two, status='submitted' ) attempt.approve() - mock_generate_certificate_apply_async.assert_called_with( + mock_cert_task.assert_called_with(self.user_two, self.course_two.id) + + def test_cert_generation_on_photo_verification_v1(self): + with mock.patch( + 'lms.djangoapps.certificates.signals.generate_certificate.apply_async', + return_value=None + ) as mock_cert_task: + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): + attempt = SoftwareSecurePhotoVerification.objects.create( + user=self.user_two, + status='submitted' + ) + attempt.approve() + mock_cert_task.assert_called_with( countdown=CERTIFICATE_DELAY_SECONDS, kwargs={ 'student': str(self.user_two.id), 'course_key': str(self.course_two.id), - 'expected_verification_status': IDVerificationAttempt.STATUS.approved, + 'expected_verification_status': 'approved' } ) diff --git a/lms/djangoapps/instructor/tests/test_api.py b/lms/djangoapps/instructor/tests/test_api.py index f96c1664a3..7c7923ac9a 100644 --- a/lms/djangoapps/instructor/tests/test_api.py +++ b/lms/djangoapps/instructor/tests/test_api.py @@ -27,7 +27,6 @@ from edx_toggles.toggles.testutils import override_waffle_flag from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.locator import UsageKey from pytz import UTC -from testfixtures import LogCapture from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.course_modes.tests.factories import CourseModeFactory @@ -59,7 +58,6 @@ from common.djangoapps.student.tests.factories import InstructorFactory from common.djangoapps.student.tests.factories import StaffFactory from common.djangoapps.student.tests.factories import UserFactory from lms.djangoapps.bulk_email.models import BulkEmailFlag, CourseEmail, CourseEmailTemplate -from lms.djangoapps.certificates.api import generate_user_certificates from lms.djangoapps.certificates.data import CertificateStatuses from lms.djangoapps.certificates.tests.factories import ( GeneratedCertificateFactory @@ -1904,20 +1902,6 @@ class TestInstructorAPIBulkBetaEnrollment(SharedModuleStoreTestCase, LoginEnroll # (comment because pylint C0103(invalid-name)) # self.maxDiff = None - def test_beta_tester_must_not_earn_cert(self): - """ - Test to ensure that beta tester must not earn certificate in a course - in which he/she is a beta-tester. - """ - with LogCapture() as capture: - message = ( - f'Canceling Certificate Generation task for user {self.beta_tester.id} : {self.course.id}. User is a ' - 'Beta Tester.' - ) - - generate_user_certificates(self.beta_tester, self.course.id) - capture.check_present(('lms.djangoapps.certificates.generation_handler', 'INFO', message)) - def test_missing_params(self): """ Test missing all query parameters. """ url = reverse('bulk_beta_modify_access', kwargs={'course_id': str(self.course.id)}) diff --git a/lms/djangoapps/instructor/tests/test_certificates.py b/lms/djangoapps/instructor/tests/test_certificates.py index 7d4bd23efd..33cd3dc290 100644 --- a/lms/djangoapps/instructor/tests/test_certificates.py +++ b/lms/djangoapps/instructor/tests/test_certificates.py @@ -4,12 +4,10 @@ import contextlib import io import json -from datetime import datetime, timedelta from unittest import mock import ddt import pytest -import pytz from config_models.models import cache from django.conf import settings from django.core.exceptions import ObjectDoesNotExist @@ -17,8 +15,6 @@ from django.core.files.uploadedfile import SimpleUploadedFile from django.test.utils import override_settings from django.urls import reverse -from capa.xqueue_interface import XQueueInterface -from common.djangoapps.course_modes.models import CourseMode from common.djangoapps.student.models import CourseEnrollment from common.djangoapps.student.tests.factories import GlobalStaffFactory from common.djangoapps.student.tests.factories import InstructorFactory @@ -35,9 +31,6 @@ from lms.djangoapps.certificates.tests.factories import ( CertificateInvalidationFactory, GeneratedCertificateFactory ) -from lms.djangoapps.grades.tests.utils import mock_passing_grade -from lms.djangoapps.verify_student.services import IDVerificationService -from lms.djangoapps.verify_student.tests.factories import SoftwareSecurePhotoVerificationFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory @@ -47,7 +40,7 @@ class CertificatesInstructorDashTest(SharedModuleStoreTestCase): """Tests for the certificate panel of the instructor dash. """ ERROR_REASON = "An error occurred!" - DOWNLOAD_URL = "http://www.example.com/abcd123/cert.pdf" + DOWNLOAD_URL = "https://www.example.com/abcd123/cert.pdf" @classmethod def setUpClass(cls): @@ -359,77 +352,6 @@ class CertificatesInstructorApiTest(SharedModuleStoreTestCase): 'Certificate regeneration task has been started.' \ ' You can view the status of the generation task in the "Pending Tasks" section.' - @override_settings(AUDIT_CERT_CUTOFF_DATE=datetime.now(pytz.UTC) - timedelta(days=1)) - @ddt.data( - (CertificateStatuses.generating, 'ID Verified', 'approved'), - (CertificateStatuses.unverified, 'Not ID Verified', 'denied'), - ) - @ddt.unpack - def test_verified_users_with_audit_certs(self, expected_cert_status, verification_output, id_verification_status): - """ - Test certificate regeneration for verified users with audit certificates. - - Scenario: - Enroll user in a course in audit mode, - User passed the course and now he has `audit_passing` certificate status, - User switched to verified mode and is ID verified, - Regenerate certificate for it, - Modified certificate status is `generating` if user is ID verified otherwise `unverified`. - """ - # Check that user is enrolled in audit mode. - enrollment = CourseEnrollment.get_enrollment(self.user, self.course.id) - assert enrollment.mode == CourseMode.AUDIT - - with mock_passing_grade(): - # Generate certificate for user and check that user has a audit passing certificate. - cert_status = certs_api.generate_user_certificates(student=self.user, course_key=self.course.id) - - # Check that certificate status is 'audit_passing'. - assert cert_status == CertificateStatuses.audit_passing - - # Update user enrollment mode to verified mode. - enrollment.update_enrollment(mode=CourseMode.VERIFIED) - assert enrollment.mode == CourseMode.VERIFIED - - # Create and assert user's ID verification record. - SoftwareSecurePhotoVerificationFactory.create(user=self.user, status=id_verification_status) - actual_verification_status = IDVerificationService.verification_status_for_user( - self.user, - enrollment.mode - ) - assert actual_verification_status == verification_output - - # Login the client and access the url with 'audit_passing' status. - self.client.login(username=self.global_staff.username, password='test') - url = reverse( - 'start_certificate_regeneration', - kwargs={'course_id': str(self.course.id)} - ) - - with mock.patch.object(XQueueInterface, 'send_to_queue') as mock_send: - mock_send.return_value = (0, None) - response = self.client.post( - url, - {'certificate_statuses': [CertificateStatuses.audit_passing]} - ) - - # Assert 200 status code in response - assert response.status_code == 200 - res_json = json.loads(response.content.decode('utf-8')) - - # Assert request is successful - assert res_json['success'] - - # Assert success message - assert res_json['message'] ==\ - 'Certificate regeneration task has been started.' \ - ' You can view the status of the generation task in the "Pending Tasks" section.' - - # Now, check whether user has audit certificate. - cert = certs_api.get_certificate_for_user(self.user.username, self.course.id) - assert cert['status'] != CertificateStatuses.audit_passing - assert cert['status'] == expected_cert_status - def test_certificate_regeneration_error(self): """ Test certificate regeneration errors out when accessed with either empty list of 'certificate_statuses' or diff --git a/lms/djangoapps/mobile_api/users/tests.py b/lms/djangoapps/mobile_api/users/tests.py index 700b72567c..cabfbbdc12 100644 --- a/lms/djangoapps/mobile_api/users/tests.py +++ b/lms/djangoapps/mobile_api/users/tests.py @@ -22,11 +22,9 @@ from common.djangoapps.student.models import CourseEnrollment from common.djangoapps.student.tests.factories import CourseEnrollmentFactory from common.djangoapps.util.milestones_helpers import set_prerequisite_courses from common.djangoapps.util.testing import UrlResetMixin -from lms.djangoapps.certificates.api import generate_user_certificates from lms.djangoapps.certificates.data import CertificateStatuses from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory from lms.djangoapps.courseware.access_response import MilestoneAccessError, StartDateError, VisibilityError -from lms.djangoapps.grades.tests.utils import mock_passing_grade from lms.djangoapps.mobile_api.testutils import ( MobileAPITestCase, MobileAuthTestMixin, @@ -309,10 +307,10 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest ) @ddt.unpack def test_enrollment_with_gating(self, api_version, expired, num_courses_returned): - ''' + """ Test that expired courses are only returned in v1 of API when waffle flag enabled, and un-expired courses always returned - ''' + """ CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime.datetime(2015, 1, 1)) courses = self._get_enrollment_data(api_version, expired) self._assert_enrollment_results(api_version, courses, num_courses_returned, True) @@ -325,10 +323,10 @@ class TestUserEnrollmentApi(UrlResetMixin, MobileAPITestCase, MobileAuthUserTest ) @ddt.unpack def test_enrollment_no_gating(self, api_version, expired, num_courses_returned): - ''' - Test that expired and non-expired courses returned if waffle flag is disabled - regarless of version of API - ''' + """ + Test that expired and non-expired courses are returned if the waffle flag is disabled, + regardless of the API version + """ CourseDurationLimitConfig.objects.create(enabled=False) courses = self._get_enrollment_data(api_version, expired) self._assert_enrollment_results(api_version, courses, num_courses_returned, False) @@ -349,7 +347,7 @@ class TestUserEnrollmentCertificates(UrlResetMixin, MobileAPITestCase, Milestone """ self.login_and_enroll() - certificate_url = "http://test_certificate_url" + certificate_url = "https://test_certificate_url" GeneratedCertificateFactory.create( user=self.user, course_id=self.course.id, @@ -386,17 +384,13 @@ class TestUserEnrollmentCertificates(UrlResetMixin, MobileAPITestCase, Milestone @patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': True, 'ENABLE_MKTG_SITE': True}) 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) + GeneratedCertificateFactory.create( + user=self.user, + course_id=self.course.id, + status=CertificateStatuses.downloadable + ) response = self.api_response() certificate_data = response.data[0]['certificate'] @@ -559,9 +553,9 @@ class TestCourseEnrollmentSerializer(MobileAPITestCase, MilestonesTestCaseMixin) self.request.user = self.user def get_serialized_data(self, api_version): - ''' + """ Return data from CourseEnrollmentSerializer - ''' + """ if api_version == API_V05: serializer = CourseEnrollmentSerializerv05 else: @@ -573,10 +567,10 @@ class TestCourseEnrollmentSerializer(MobileAPITestCase, MilestonesTestCaseMixin) ).data def _expiration_in_response(self, response, api_version): - ''' + """ Assert that audit_access_expires field in present in response based on version of api being used - ''' + """ if api_version != API_V05: assert 'audit_access_expires' in response else: