def test_can_generate_auto_disabled(self): """ Test handling when automatic generation is disabled """ with override_waffle_switch(AUTO_GENERATION_SWITCH, active=False): assert not _can_generate_allowlist_certificate( self.user, self.course_run_key)
def test_uncached_with_storage(self): with override_waffle_switch(waffle_switch(STORAGE_BACKING_FOR_CACHE), active=True): self.store.add(self.block_structure) self.mock_cache.map.clear() stored_value = self.store.get( self.block_structure.root_block_usage_key) self.assert_block_structure(stored_value, self.children_map)
def test_override(self): switch = WaffleSwitch("test_namespace.test_switch", module_name="testmodule") self.assertFalse(switch.is_enabled()) with override_waffle_switch(switch, active=True): self.assertTrue(switch.is_enabled()) self.assertFalse(switch.is_enabled())
def test_read_zero(self, assume_zero_enabled, create_if_needed): with override_waffle_switch(waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=assume_zero_enabled): grade_factory = CourseGradeFactory() course_grade = grade_factory.read(self.request.user, self.course, create_if_needed=create_if_needed) if create_if_needed or assume_zero_enabled: self._assert_zero_grade(course_grade, ZeroCourseGrade if assume_zero_enabled else CourseGrade) else: assert course_grade is None
def configure_waffle_namespace(feature_enabled): """ Context manager to configure the certs flags """ namespace = certs_waffle.waffle() auto_certificate_generation_switch = LegacyWaffleSwitch(namespace, certs_waffle.AUTO_CERTIFICATE_GENERATION) # pylint: disable=toggle-missing-annotation with override_waffle_switch(auto_certificate_generation_switch, active=feature_enabled): yield
def test_cache_invalidation(self, invalidate_cache_enabled, mock_bs_manager_clear): test_display_name = "Jedi 101" with override_waffle_switch(INVALIDATE_CACHE_ON_PUBLISH, active=invalidate_cache_enabled): self.course.display_name = test_display_name self.store.update_item(self.course, self.user.id) self.assertEqual(mock_bs_manager_clear.called, invalidate_cache_enabled)
def test_view_enabled(self, method, url): """ Ensure CourseEnrollmentAdmin views can be enabled with the waffle switch. """ with override_waffle_switch(COURSE_ENROLLMENT_ADMIN_SWITCH, active=True): response = getattr(self.client, method)(url) self.assertEqual(response.status_code, 200)
def test_add_and_get(self, with_storage_backing): with override_waffle_switch(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): self.store.add(self.block_structure) stored_value = self.store.get( self.block_structure.root_block_usage_key) assert stored_value is not None self.assert_block_structure(stored_value, self.children_map)
def test_query_counts_cached(self, store_type, with_storage_backing): with override_waffle_switch(waffle_switch(STORAGE_BACKING_FOR_CACHE), active=with_storage_backing): course = self._create_course(store_type) self._get_blocks( course, expected_mongo_queries=0, expected_sql_queries=11 if with_storage_backing else 10, )
def test_ungating_when_fulfilled(self, earned, max_possible, result): self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): answer_problem(self.course, self.request, self.gating_prob1, earned, max_possible) self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=result) self.assert_access_to_gated_content(self.non_staff_user)
def test_override(self): switch = WaffleSwitch( # lint-amnesty, pylint: disable=toggle-missing-annotation "test_namespace.test_switch", module_name="testmodule" ) self.assertFalse(switch.is_enabled()) with override_waffle_switch(switch, active=True): self.assertTrue(switch.is_enabled()) self.assertFalse(switch.is_enabled())
def test_display_maintenance_warning_switch(self, display_warning): """ Tests the `DISPLAY_MAINTENANCE_WARNING` switch is working as expected. Checks if the decorated request from `get_decorated_request` has a warning or not. """ with override_waffle_switch(DISPLAY_MAINTENANCE_WARNING, active=display_warning): banner_added, _ = self.add_maintenance_banner() assert display_warning == banner_added
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 override_waffle_switch(self, override): """ Override the setting of the ENABLE_COMPLETION_TRACKING waffle switch for the course of the test. Parameters: override (bool): True if tracking should be enabled. """ _waffle_overrider = override_waffle_switch( waffle.ENABLE_COMPLETION_TRACKING_SWITCH, override) _waffle_overrider.__enter__() self.addCleanup(_waffle_overrider.__exit__, None, None, None)
def test_admin_login_redirect(self): with override_waffle_switch(ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY, True): response = self.client.get(reverse('admin:login')) assert response.url == '/login?next=/admin' assert response.status_code == 302 with override_waffle_flag(ADMIN_AUTH_REDIRECT_TO_LMS, True): response = self.client.get(reverse('admin:login')) assert response.url == '/login?next=/admin' assert response.status_code == 302 with override_waffle_switch(ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY, False): response = self.client.get(reverse('admin:login')) assert response.template_name == ['admin/login.html'] with override_waffle_flag(ADMIN_AUTH_REDIRECT_TO_LMS, False): response = self.client.get(reverse('admin:login')) assert response.template_name == ['admin/login.html']
def test_cert_generation_on_photo_verification(self): with mock.patch( 'lms.djangoapps.certificates.signals.generate_certificate_task', return_value=None) as mock_cert_task: with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True): attempt = SoftwareSecurePhotoVerification.objects.create( user=self.user_two, status='submitted') attempt.approve() mock_cert_task.assert_called_with(self.user_two, self.course_two.id)
def test_enable_completion_tracking(self): """ Test response when the waffle switch is disabled (default). """ with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, False): response = self.client.post(self.url, {'username': self.ENROLLED_USERNAME}, format='json') assert response.data == \ { 'detail': 'BlockCompletion.objects.submit_batch_completion' ' should not be called when the feature is disabled.' } assert response.status_code == 400
def test_fire_task_allowlist_auto_disabled(self): """ Test that the allowlist generation is not invoked if automatic generation is disabled """ with mock.patch( 'lms.djangoapps.certificates.signals.generate_allowlist_certificate_task', return_value=None) as mock_generate_allowlist_task: with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=False): CertificateAllowlistFactory(user=self.user, course_id=self.ip_course.id) mock_generate_allowlist_task.assert_not_called()
def test_query_counts_uncached(self, store_type, expected_mongo_queries, with_storage_backing, num_sql_queries): with override_waffle_switch(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): course = self._create_course(store_type) clear_course_from_cache(course.id) self._get_blocks( course, expected_mongo_queries, expected_sql_queries=num_sql_queries, )
def test_id_verification_allowlist(self): # User is not on the allowlist with mock.patch( 'lms.djangoapps.certificates.signals.generate_allowlist_certificate_task', return_value=None ) as mock_allowlist_task: with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True): attempt = SoftwareSecurePhotoVerification.objects.create( user=self.user_two, status='submitted' ) attempt.approve() mock_allowlist_task.assert_not_called() # User is on the allowlist with mock.patch( 'lms.djangoapps.certificates.signals.generate_allowlist_certificate_task', return_value=None ) as mock_allowlist_task: with override_waffle_switch(AUTO_CERTIFICATE_GENERATION, active=True): u = UserFactory.create() c = CourseFactory() course_key = c.id # pylint: disable=no-member CourseEnrollmentFactory( user=u, course_id=course_key, is_active=True, mode='verified' ) CertificateAllowlistFactory( user=u, course_id=course_key ) attempt = SoftwareSecurePhotoVerification.objects.create( user=u, status='submitted' ) attempt.approve() mock_allowlist_task.assert_called_with(u, course_key)
def test_update_collected_if_needed(self, with_storage_backing): with override_waffle_switch(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): with mock_registered_transformers(self.registered_transformers): assert TestTransformer1.collect_call_count == 0 self.bs_manager.update_collected_if_needed() assert TestTransformer1.collect_call_count == 1 self.bs_manager.update_collected_if_needed() expected_count = 1 if with_storage_backing else 2 assert TestTransformer1.collect_call_count == expected_count self.collect_and_verify(expect_modulestore_called=False, expect_cache_updated=False)
def test_cert_generation_on_whitelist_append_self_paced(self): """ Verify that signal is sent, received, and fires task based on 'AUTO_CERTIFICATE_GENERATION' flag """ 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=False): CertificateWhitelistFactory(user=self.user, course_id=self.course.id) mock_generate_certificate_apply_async.assert_not_called() with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): CertificateWhitelistFactory(user=self.user, course_id=self.course.id) mock_generate_certificate_apply_async.assert_called_with( countdown=CERTIFICATE_DELAY_SECONDS, kwargs={ 'student': six.text_type(self.user.id), 'course_key': six.text_type(self.course.id), })
def test_cert_generation_on_allowlist_append_self_paced_auto_cert_generation_disabled( self): """ Verify that signal is sent, received, and fires task based on 'AUTO_CERTIFICATE_GENERATION' flag """ 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=False): CertificateWhitelistFactory(user=self.user, course_id=self.course.id) mock_generate_certificate_apply_async.assert_not_called()
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_zero_null_scores(self, assume_zero_enabled): """ Creates a zero course grade and ensures that null scores aren't included in the section problem scores. """ with override_waffle_switch(waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=assume_zero_enabled): with patch('lms.djangoapps.grades.subsection_grade.get_score', return_value=None): course_data = CourseData(self.request.user, structure=self.course_structure) chapter_grades = ZeroCourseGrade(self.request.user, course_data).chapter_grades for chapter in chapter_grades: assert {} != chapter_grades[chapter]['sections'] for section in chapter_grades[chapter]['sections']: assert {} == section.problem_scores
def test_zero(self, assume_zero_enabled): """ Creates a ZeroCourseGrade and ensures it's empty. """ with override_waffle_switch(waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=assume_zero_enabled): course_data = CourseData(self.request.user, structure=self.course_structure) chapter_grades = ZeroCourseGrade(self.request.user, course_data).chapter_grades for chapter in chapter_grades: for section in chapter_grades[chapter]['sections']: for score in six.itervalues(section.problem_scores): self.assertEqual(score.earned, 0) self.assertEqual(score.first_attempted, None) self.assertEqual(section.all_total.earned, 0)
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': 'approved' })
def test_save_invalid_course_id(self): """ Send an invalid course ID instead of "org.0/course_0/Run_0" when saving, and verify that it fails. """ data = { 'user': str(self.course_enrollment.user.id), 'course': 'invalid-course-id', 'is_active': 'true', 'mode': self.course_enrollment.mode, } with override_waffle_switch(COURSE_ENROLLMENT_ADMIN_SWITCH, active=True): with pytest.raises(ValidationError): self.client.post( reverse('admin:student_courseenrollment_change', args=(self.course_enrollment.id, )), data=data, )
def test_gated_content_always_in_grades(self): with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): # start with a grade from a non-gated subsection answer_problem(self.course, self.request, self.prob3, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=False) self.assert_access_to_gated_content(self.non_staff_user) self.assert_course_grade(self.non_staff_user, .33) # fulfill the gated requirements answer_problem(self.course, self.request, self.gating_prob1, 10, 10) # verify gated status and overall course grade percentage self.assert_user_has_prereq_milestone(self.non_staff_user, expected_has_milestone=True) self.assert_access_to_gated_content(self.non_staff_user) self.assert_course_grade(self.non_staff_user, .67)
def test_complete_student_attempt_split_test(self, mock_submit): """ Asserts complete_student_attempt correctly publishes completion when a split test is involved This test case exists because we ran into a bug about the user_service not existing when a split_test existed inside of a subsection. Associated with this change was adding in the user state into the module before attempting completion and this ensures that is working properly. """ partition = UserPartition( 0, 'first_partition', 'First Partition', [ Group(0, 'alpha'), Group(1, 'beta') ] ) course = CourseFactory.create(user_partitions=[partition]) section = ItemFactory.create(parent=course, category='chapter') subsection = ItemFactory.create(parent=section, category='sequential') c0_url = course.id.make_usage_key('vertical', 'split_test_cond0') c1_url = course.id.make_usage_key('vertical', 'split_test_cond1') split_test = ItemFactory.create( parent=subsection, category='split_test', user_partition_id=0, group_id_to_child={'0': c0_url, '1': c1_url}, ) cond0vert = ItemFactory.create(parent=split_test, category='vertical', location=c0_url) ItemFactory.create(parent=cond0vert, category='video') ItemFactory.create(parent=cond0vert, category='problem') cond1vert = ItemFactory.create(parent=split_test, category='vertical', location=c1_url) ItemFactory.create(parent=cond1vert, category='video') ItemFactory.create(parent=cond1vert, category='html') with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): self.service.complete_student_attempt(self.student.username, str(subsection.location)) # Only the group the user was assigned to should have completion published. # Either cond0vert's children or cond1vert's children assert mock_submit.call_count == 2