diff --git a/cms/djangoapps/contentstore/views/tests/test_item.py b/cms/djangoapps/contentstore/views/tests/test_item.py index 74ed07989f5a0ce5385e98a04d935b09c11d3eb4..4d7216eb711502146a902d1fc213a9488deb9891 100644 --- a/cms/djangoapps/contentstore/views/tests/test_item.py +++ b/cms/djangoapps/contentstore/views/tests/test_item.py @@ -35,6 +35,7 @@ from cms.djangoapps.contentstore.tests.utils import CourseTestCase from cms.djangoapps.contentstore.utils import reverse_course_url, reverse_usage_url from cms.djangoapps.contentstore.views import item as item_module from lms_xblock.mixin import NONSENSICAL_ACCESS_RESTRICTION +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from student.tests.factories import UserFactory from xblock_django.models import XBlockConfiguration, XBlockStudioConfiguration, XBlockStudioConfigurationFlag from xblock_django.user_service import DjangoXBlockUserService @@ -2666,7 +2667,7 @@ class TestXBlockInfo(ItemTest): self.course.highlights_enabled_for_messaging = True self.store.update_item(self.course, None) chapter = self.store.get_item(self.chapter.location) - with highlights_setting.override(): + with override_waffle_switch(highlights_setting, active=True): chapter_xblock_info = create_xblock_info(chapter) course_xblock_info = create_xblock_info(self.course) self.assertTrue(chapter_xblock_info['highlights_enabled']) diff --git a/cms/djangoapps/contentstore/views/tests/test_videos.py b/cms/djangoapps/contentstore/views/tests/test_videos.py index 6cf01927b4128d04c8f7b487b6a0808bc68e2c7a..8e88f96eb6734e59be6e84c3d5e3c31b87c6252c 100644 --- a/cms/djangoapps/contentstore/views/tests/test_videos.py +++ b/cms/djangoapps/contentstore/views/tests/test_videos.py @@ -41,7 +41,9 @@ from openedx.core.djangoapps.video_pipeline.config.waffle import ( waffle_flags ) from openedx.core.djangoapps.video_pipeline.models import VEMPipelineIntegration +from openedx.core.djangoapps.waffle_utils import WaffleSwitch from openedx.core.djangoapps.waffle_utils.models import WaffleFlagCourseOverrideModel +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from xmodule.modulestore.tests.factories import CourseFactory from ..videos import ( @@ -56,23 +58,7 @@ from ..videos import ( convert_video_status ) - -def override_switch(switch, active): - """ - Overrides the given waffle switch to `active` boolean. - - Arguments: - switch(str): switch name - active(bool): A boolean representing (to be overridden) value - """ - def decorate(function): - @wraps(function) - def inner(*args, **kwargs): - with WAFFLE_SWITCHES.override(switch, active=active): - function(*args, **kwargs) - return inner - - return decorate +VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH = WaffleSwitch(WAFFLE_SWITCHES, VIDEO_IMAGE_UPLOAD_ENABLED) class VideoUploadTestBase(object): @@ -908,7 +894,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): self.assertIn('error', response) self.assertEqual(response['error'], error_message) - @override_switch(VIDEO_IMAGE_UPLOAD_ENABLED, False) + @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, False) def test_video_image_upload_disabled(self): """ Tests the video image upload when the feature is disabled. @@ -917,7 +903,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): response = self.client.post(video_image_upload_url, {'file': 'dummy_file'}, format='multipart') self.assertEqual(response.status_code, 404) - @override_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True) + @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True) def test_video_image(self): """ Test video image is saved. @@ -939,7 +925,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): self.assertNotEqual(image_url1, image_url2) - @override_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True) + @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True) def test_video_image_no_file(self): """ Test that an error error message is returned if upload request is incorrect. @@ -948,7 +934,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): response = self.client.post(video_image_upload_url, {}) self.verify_error_message(response, 'An image file is required.') - @override_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True) + @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True) def test_no_video_image(self): """ Test image url is set to None if no video image. @@ -1130,7 +1116,7 @@ class VideoImageTestCase(VideoUploadTestBase, CourseTestCase): ) ) @ddt.unpack - @override_switch(VIDEO_IMAGE_UPLOAD_ENABLED, True) + @override_waffle_switch(VIDEO_IMAGE_UPLOAD_ENABLED_SWITCH, True) def test_video_image_validation_message(self, image_data, error_message): """ Test video image validation gives proper error message. diff --git a/common/djangoapps/student/tests/test_admin_views.py b/common/djangoapps/student/tests/test_admin_views.py index d67b429ba1f16f236bcc0342859871839dbef37f..02f125179d6248b1ee7d4261cefb354bb1a700c9 100644 --- a/common/djangoapps/student/tests/test_admin_views.py +++ b/common/djangoapps/student/tests/test_admin_views.py @@ -18,13 +18,14 @@ from django.utils.timezone import now from mock import Mock from pytz import UTC -from student.admin import AllowedAuthUserForm, COURSE_ENROLLMENT_ADMIN_SWITCH, UserAdmin, CourseEnrollmentForm +from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory +from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch +from student.admin import COURSE_ENROLLMENT_ADMIN_SWITCH, AllowedAuthUserForm, CourseEnrollmentForm, UserAdmin from student.models import AllowedAuthUser, CourseEnrollment, LoginFailures from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory -from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory -from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin class AdminCourseRolesPageTest(SharedModuleStoreTestCase): @@ -243,7 +244,7 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase): """ Ensure CourseEnrollmentAdmin views can be enabled with the waffle switch. """ - with COURSE_ENROLLMENT_ADMIN_SWITCH.override(active=True): + with override_waffle_switch(COURSE_ENROLLMENT_ADMIN_SWITCH, active=True): response = getattr(self.client, method)(url) self.assertEqual(response.status_code, 200) @@ -257,7 +258,7 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase): course_id=self.course.id, # pylint: disable=no-member ) search_url = '{}?q={}'.format(reverse('admin:student_courseenrollment_changelist'), self.user.username) - with COURSE_ENROLLMENT_ADMIN_SWITCH.override(active=True): + with override_waffle_switch(COURSE_ENROLLMENT_ADMIN_SWITCH, active=True): response = self.client.get(search_url) self.assertEqual(response.status_code, 200) @@ -283,7 +284,7 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase): 'mode': self.course_enrollment.mode, } - with COURSE_ENROLLMENT_ADMIN_SWITCH.override(active=True): + with override_waffle_switch(COURSE_ENROLLMENT_ADMIN_SWITCH, active=True): response = self.client.post( reverse('admin:student_courseenrollment_change', args=(self.course_enrollment.id, )), data=data, @@ -304,7 +305,7 @@ class CourseEnrollmentAdminTest(SharedModuleStoreTestCase): 'mode': self.course_enrollment.mode, } - with COURSE_ENROLLMENT_ADMIN_SWITCH.override(active=True): + with override_waffle_switch(COURSE_ENROLLMENT_ADMIN_SWITCH, active=True): with self.assertRaises(ValidationError): self.client.post( reverse('admin:student_courseenrollment_change', args=(self.course_enrollment.id, )), diff --git a/lms/djangoapps/certificates/tests/test_signals.py b/lms/djangoapps/certificates/tests/test_signals.py index 87fcc6eb824534292a73f094a2f27aefaf1e8c80..3efdf947bbeb77a00381bd292f0b6ee5fdb90552 100644 --- a/lms/djangoapps/certificates/tests/test_signals.py +++ b/lms/djangoapps/certificates/tests/test_signals.py @@ -20,10 +20,14 @@ 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 openedx.core.djangoapps.certificates.config import waffle +from openedx.core.djangoapps.waffle_utils import WaffleSwitch +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +AUTO_CERTIFICATE_GENERATION_SWITCH = WaffleSwitch(waffle.waffle(), waffle.AUTO_CERTIFICATE_GENERATION) + class SelfGeneratedCertsSignalTest(ModuleStoreTestCase): """ @@ -84,13 +88,13 @@ class WhitelistGeneratedCertificatesTest(ModuleStoreTestCase): '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=False): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=False): CertificateWhitelist.objects.create( user=self.user, course_id=self.course.id ) mock_generate_certificate_apply_async.assert_not_called() - with waffle.waffle().override(waffle.AUTO_CERTIFICATE_GENERATION, active=True): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): CertificateWhitelist.objects.create( user=self.user, course_id=self.course.id @@ -112,13 +116,13 @@ class WhitelistGeneratedCertificatesTest(ModuleStoreTestCase): '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=False): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=False): CertificateWhitelist.objects.create( user=self.user, course_id=self.ip_course.id ) mock_generate_certificate_apply_async.assert_not_called() - with waffle.waffle().override(waffle.AUTO_CERTIFICATE_GENERATION, active=True): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): CertificateWhitelist.objects.create( user=self.user, course_id=self.ip_course.id @@ -167,7 +171,7 @@ class PassingGradeCertsTest(ModuleStoreTestCase): '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): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): grade_factory = CourseGradeFactory() # Not passing grade_factory.update(self.user, self.course) @@ -188,7 +192,7 @@ class PassingGradeCertsTest(ModuleStoreTestCase): '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): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): grade_factory = CourseGradeFactory() # Not passing grade_factory.update(self.user, self.ip_course) @@ -312,7 +316,7 @@ class LearnerTrackChangeCertsTest(ModuleStoreTestCase): '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): + 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, @@ -333,7 +337,7 @@ class LearnerTrackChangeCertsTest(ModuleStoreTestCase): '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): + 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, @@ -386,7 +390,7 @@ class CertificateGenerationTaskTest(ModuleStoreTestCase): '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): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): fire_ungenerated_certificate_task(self.user, self.course.id) task_created = mock_generate_certificate_apply_async.called self.assertEqual(task_created, should_create) diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index a3997f33c643bce30356aa897d1d523c2f18ec67..aa2327a76973cbc285d158dc613db7f5e50d0784 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -14,8 +14,10 @@ from django.test.client import Client, RequestFactory from django.test.utils import override_settings from django.urls import reverse from mock import patch +from urllib.parse import urlencode from course_modes.models import CourseMode +from edx_toggles.toggles.testutils import override_waffle_switch from lms.djangoapps.badges.events.course_complete import get_completion_badge from lms.djangoapps.badges.tests.factories import ( BadgeAssertionFactory, @@ -44,6 +46,7 @@ from openedx.core.djangoapps.site_configuration.tests.test_util import ( with_site_configuration, with_site_configuration_context ) +from openedx.core.djangoapps.waffle_utils import WaffleSwitch from openedx.core.djangolib.js_utils import js_escaped_string from openedx.core.djangolib.testing.utils import CacheIsolationTestCase from openedx.core.lib.tests.assertions.events import assert_event_matches @@ -55,6 +58,7 @@ from util.date_utils import strftime_localized from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory +AUTO_CERTIFICATE_GENERATION_SWITCH = WaffleSwitch(waffle.waffle(), waffle.AUTO_CERTIFICATE_GENERATION) FEATURES_WITH_CERTS_ENABLED = settings.FEATURES.copy() FEATURES_WITH_CERTS_ENABLED['CERTIFICATES_HTML_VIEW'] = True FEATURES_WITH_BADGES_ENABLED = FEATURES_WITH_CERTS_ENABLED.copy() @@ -937,7 +941,7 @@ class CertificatesViewsTests(CommonCertificatesTestCase, CacheIsolationTestCase) expected_date = today else: expected_date = self.course.certificate_available_date - with waffle.waffle().override(waffle.AUTO_CERTIFICATE_GENERATION, active=True): + with override_waffle_switch(AUTO_CERTIFICATE_GENERATION_SWITCH, active=True): response = self.client.get(test_url) date = u'{month} {day}, {year}'.format( month=strftime_localized(expected_date, "%B"), diff --git a/lms/djangoapps/course_api/blocks/tests/test_api.py b/lms/djangoapps/course_api/blocks/tests/test_api.py index 4b0fe82a42b31268fc2615bcb8013c37b712ffcb..bf8d2618666a2932dfc93b05946ac49ec32bd10c 100644 --- a/lms/djangoapps/course_api/blocks/tests/test_api.py +++ b/lms/djangoapps/course_api/blocks/tests/test_api.py @@ -11,7 +11,8 @@ from django.test.client import RequestFactory from mock import patch from openedx.core.djangoapps.content.block_structure.api import clear_course_from_cache -from openedx.core.djangoapps.content.block_structure.config import STORAGE_BACKING_FOR_CACHE, waffle +from openedx.core.djangoapps.content.block_structure.config import STORAGE_BACKING_FOR_CACHE, waffle_switch +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from student.tests.factories import UserFactory from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase @@ -229,7 +230,7 @@ class TestGetBlocksQueryCounts(TestGetBlocksQueryCountsBase): ) @ddt.unpack def test_query_counts_cached(self, store_type, with_storage_backing): - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=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, @@ -246,7 +247,7 @@ class TestGetBlocksQueryCounts(TestGetBlocksQueryCountsBase): @ddt.unpack def test_query_counts_uncached(self, store_type_tuple, with_storage_backing): store_type, expected_mongo_queries = store_type_tuple - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): + with override_waffle_switch(waffle_switch(STORAGE_BACKING_FOR_CACHE), active=with_storage_backing): course = self._create_course(store_type) clear_course_from_cache(course.id) diff --git a/lms/djangoapps/courseware/tests/test_module_render.py b/lms/djangoapps/courseware/tests/test_module_render.py index 005f864ea7605c065ad501f086246b7f946e0141..86b710932733e9f89d7d2cf8c043a022c9e1604a 100644 --- a/lms/djangoapps/courseware/tests/test_module_render.py +++ b/lms/djangoapps/courseware/tests/test_module_render.py @@ -48,21 +48,26 @@ from course_modes.models import CourseMode from lms.djangoapps.courseware import module_render as render from lms.djangoapps.courseware.access_response import AccessResponse from lms.djangoapps.courseware.courses import get_course_info_section, get_course_with_access +from lms.djangoapps.courseware.field_overrides import OverrideFieldData from lms.djangoapps.courseware.masquerade import CourseMasquerade from lms.djangoapps.courseware.model_data import FieldDataCache from lms.djangoapps.courseware.models import StudentModule from lms.djangoapps.courseware.module_render import get_module_for_descriptor, hash_resource from lms.djangoapps.courseware.tests.factories import ( - GlobalStaffFactory, RequestFactoryNoCsrf, StudentModuleFactory, UserFactory, + GlobalStaffFactory, + RequestFactoryNoCsrf, + StudentModuleFactory, + UserFactory ) from lms.djangoapps.courseware.tests.test_submitting_problems import TestSubmittingProblems from lms.djangoapps.courseware.tests.tests import LoginEnrollmentTestCase -from lms.djangoapps.courseware.field_overrides import OverrideFieldData from lms.djangoapps.lms_xblock.field_data import LmsFieldData from openedx.core.djangoapps.credit.api import set_credit_requirement_status, set_credit_requirements from openedx.core.djangoapps.credit.models import CreditCourse from openedx.core.djangoapps.oauth_dispatch.jwt import create_jwt_for_user -from openedx.core.djangoapps.oauth_dispatch.tests.factories import ApplicationFactory, AccessTokenFactory +from openedx.core.djangoapps.oauth_dispatch.tests.factories import AccessTokenFactory, ApplicationFactory +from openedx.core.djangoapps.waffle_utils import WaffleSwitch +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from openedx.core.lib.courses import course_image_url from openedx.core.lib.gating import api as gating_api from openedx.core.lib.url_utils import quote_slashes @@ -86,6 +91,10 @@ from xmodule.x_module import STUDENT_VIEW, CombinedSystem, XModule, XModuleDescr TEST_DATA_DIR = settings.COMMON_TEST_DATA_ROOT +ENABLE_COMPLETION_TRACKING_SWITCH = WaffleSwitch( + completion_waffle.waffle(), completion_waffle.ENABLE_COMPLETION_TRACKING, __name__ +) + @XBlock.needs("field-data") @XBlock.needs("i18n") @@ -760,7 +769,7 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas @ddt.unpack @XBlock.register_temp_plugin(StubCompletableXBlock, identifier='comp') def test_completion_events_with_completion_disabled(self, signal, data): - with completion_waffle.waffle().override(completion_waffle.ENABLE_COMPLETION_TRACKING, False): + with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, False): course = CourseFactory.create() block = ItemFactory.create(category='comp', parent=course) request = self.request_factory.post( @@ -782,7 +791,7 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas @XBlock.register_temp_plugin(StubCompletableXBlock, identifier='comp') def test_completion_signal_for_completable_xblock(self): - with completion_waffle.waffle().override(completion_waffle.ENABLE_COMPLETION_TRACKING, True): + with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): course = CourseFactory.create() block = ItemFactory.create(category='comp', parent=course) @@ -862,7 +871,7 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas @XBlock.register_temp_plugin(StubCompletableXBlock, identifier='comp') def test_progress_signal_ignored_for_completable_xblock(self): - with completion_waffle.waffle().override(completion_waffle.ENABLE_COMPLETION_TRACKING, True): + with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): course = CourseFactory.create() block = ItemFactory.create(category='comp', parent=course) @@ -875,7 +884,7 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas @XBlock.register_temp_plugin(XBlockWithoutCompletionAPI, identifier='no_comp') def test_progress_signal_processed_for_xblock_without_completion_api(self): - with completion_waffle.waffle().override(completion_waffle.ENABLE_COMPLETION_TRACKING, True): + with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): course = CourseFactory.create() block = ItemFactory.create(category='no_comp', parent=course) @@ -889,7 +898,7 @@ class TestHandleXBlockCallback(SharedModuleStoreTestCase, LoginEnrollmentTestCas @XBlock.register_temp_plugin(StubCompletableXBlock, identifier='comp') def test_skip_handlers_for_masquerading_staff(self): - with completion_waffle.waffle().override(completion_waffle.ENABLE_COMPLETION_TRACKING, True): + with override_waffle_switch(ENABLE_COMPLETION_TRACKING_SWITCH, True): course = CourseFactory.create() block = ItemFactory.create(category='comp', parent=course) request = self.request_factory.post( diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index d058fcde8836e0e47e69519a2caa35bb1e0fc771..1007bc758ad9de736158f59f111e2c531cc4f9a0 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -65,7 +65,7 @@ from lms.djangoapps.courseware.user_state_client import DjangoXBlockUserStateCli from lms.djangoapps.courseware.views.index import show_courseware_mfe_link from lms.djangoapps.experiments.testutils import override_experiment_waffle_flag from lms.djangoapps.grades.config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT -from lms.djangoapps.grades.config.waffle import waffle as grades_waffle +from lms.djangoapps.grades.config.waffle import waffle_switch as grades_waffle_switch from lms.djangoapps.verify_student.models import VerificationDeadline from lms.djangoapps.verify_student.services import IDVerificationService from openedx.core.djangoapps.catalog.tests.factories import CourseFactory as CatalogCourseFactory @@ -74,7 +74,7 @@ from openedx.core.djangoapps.content.course_overviews.models import CourseOvervi from openedx.core.djangoapps.crawlers.models import CrawlersConfig from openedx.core.djangoapps.credit.api import set_credit_requirements from openedx.core.djangoapps.credit.models import CreditCourse, CreditProvider -from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES +from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES, override_waffle_switch from openedx.core.djangolib.testing.utils import get_mock_request from openedx.core.lib.gating import api as gating_api from openedx.core.lib.url_utils import quote_slashes @@ -1446,7 +1446,7 @@ class ProgressPageTests(ProgressPageBaseTests): def test_progress_queries(self, enable_waffle, initial, subsequent): ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1)) self.setup_course() - with grades_waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=enable_waffle): + with override_waffle_switch(grades_waffle_switch(ASSUME_ZERO_GRADE_IF_ABSENT), active=enable_waffle): with self.assertNumQueries( initial, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST ), check_mongo_calls(1): diff --git a/lms/djangoapps/email_marketing/tests/test_signals.py b/lms/djangoapps/email_marketing/tests/test_signals.py index 43d158e9aae551b6689cbf752b64002389ea6365..d21c2d0e94da426fb803705722f550f88b910e86 100644 --- a/lms/djangoapps/email_marketing/tests/test_signals.py +++ b/lms/djangoapps/email_marketing/tests/test_signals.py @@ -7,6 +7,7 @@ import datetime import logging import ddt +import six from django.conf import settings from django.contrib.auth.models import AnonymousUser from django.contrib.sites.models import Site @@ -17,16 +18,8 @@ from mock import ANY, Mock, patch from opaque_keys.edx.keys import CourseKey from sailthru.sailthru_error import SailthruClientError from sailthru.sailthru_response import SailthruResponse -import six from testfixtures import LogCapture -from ..models import EmailMarketingConfiguration -from ..signals import ( - add_email_marketing_cookies, - email_marketing_register_user, - email_marketing_user_field_changed, - update_sailthru -) from lms.djangoapps.email_marketing.tasks import ( _create_user_list, _get_list_from_email_marketing_provider, @@ -41,6 +34,14 @@ from student.models import Registration from student.tests.factories import CourseEnrollmentFactory, UserFactory, UserProfileFactory from util.json_request import JsonResponse +from ..models import EmailMarketingConfiguration +from ..signals import ( + add_email_marketing_cookies, + email_marketing_register_user, + email_marketing_user_field_changed, + update_sailthru +) + log = logging.getLogger(__name__) LOGGER_NAME = "lms.djangoapps.email_marketing.signals" @@ -620,7 +621,7 @@ class SailthruTests(TestCase): @patch('sailthru.sailthru_client.SailthruClient.purchase') @patch('sailthru.sailthru_client.SailthruClient.api_get') @patch('sailthru.sailthru_client.SailthruClient.api_post') - @patch('openedx.core.djangoapps.waffle_utils.WaffleSwitchNamespace.is_enabled') + @patch('edx_toggles.toggles.WaffleSwitchNamespace.is_enabled') def test_update_course_enrollment_whitelabel( self, switch, @@ -645,7 +646,7 @@ class SailthruTests(TestCase): update_sailthru(None, self.user, 'verified', self.course_id) self.assertFalse(mock_sailthru_purchase.called) - @patch('openedx.core.djangoapps.waffle_utils.WaffleSwitchNamespace.is_enabled') + @patch('edx_toggles.toggles.WaffleSwitchNamespace.is_enabled') @patch('sailthru.sailthru_client.SailthruClient.purchase') def test_purchase_is_not_invoked(self, mock_sailthru_purchase, switch): """Make sure purchase is not called in the following condition: @@ -655,7 +656,7 @@ class SailthruTests(TestCase): update_sailthru(None, self.user, 'verified', self.course_id) self.assertFalse(mock_sailthru_purchase.called) - @patch('openedx.core.djangoapps.waffle_utils.WaffleSwitchNamespace.is_enabled') + @patch('edx_toggles.toggles.WaffleSwitchNamespace.is_enabled') @patch('sailthru.sailthru_client.SailthruClient.purchase') def test_encoding_is_working_for_email_contains_unicode(self, mock_sailthru_purchase, switch): """Make sure encoding is working for emails contains unicode characters diff --git a/lms/djangoapps/grades/config/waffle.py b/lms/djangoapps/grades/config/waffle.py index fde298543212f005332d4cbb494acc80f6921fce..5280de8ee4cb9b3ed656b0060962057cae4891bc 100644 --- a/lms/djangoapps/grades/config/waffle.py +++ b/lms/djangoapps/grades/config/waffle.py @@ -4,7 +4,12 @@ waffle switches for the Grades app. """ -from openedx.core.djangoapps.waffle_utils import CourseWaffleFlag, WaffleFlagNamespace, WaffleSwitchNamespace +from openedx.core.djangoapps.waffle_utils import ( + CourseWaffleFlag, + WaffleFlagNamespace, + WaffleSwitch, + WaffleSwitchNamespace +) # Namespace WAFFLE_NAMESPACE = u'grades' @@ -81,6 +86,16 @@ def waffle(): return WaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'Grades: ') +def waffle_switch(name): + """ + Return the corresponding namespaced waffle switch. + + WARNING: do not replicate this pattern. Instead of declaring waffle switch names as strings, you should create + WaffleSwitch objects as top-level constants. + """ + return WaffleSwitch(waffle(), name, module_name=__name__) + + def waffle_flags(): """ Returns the namespaced, cached, audited Waffle flags dictionary for Grades. diff --git a/lms/djangoapps/grades/tests/test_course_grade.py b/lms/djangoapps/grades/tests/test_course_grade.py index 6920621fdc7aa6a0669aba7182b901479273c2fc..bf8fd188affebd04c4f1aefa4bf23c0afa41273f 100644 --- a/lms/djangoapps/grades/tests/test_course_grade.py +++ b/lms/djangoapps/grades/tests/test_course_grade.py @@ -4,13 +4,14 @@ from crum import set_current_request from django.conf import settings from mock import patch +from edx_toggles.toggles.testutils import override_waffle_switch from openedx.core.djangolib.testing.utils import get_mock_request from student.models import CourseEnrollment from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory -from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT, waffle +from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT, waffle_switch from ..course_data import CourseData from ..course_grade import ZeroCourseGrade from ..course_grade_factory import CourseGradeFactory @@ -31,7 +32,7 @@ class ZeroGradeTest(GradeTestBase): """ Creates a ZeroCourseGrade and ensures it's empty. """ - with waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=assume_zero_enabled): + 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: @@ -46,7 +47,7 @@ class ZeroGradeTest(GradeTestBase): """ Creates a zero course grade and ensures that null scores aren't included in the section problem scores. """ - with waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=assume_zero_enabled): + 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 diff --git a/lms/djangoapps/grades/tests/test_course_grade_factory.py b/lms/djangoapps/grades/tests/test_course_grade_factory.py index fe1b193870a20f64aed3091fa215717e8d065576..4ced0b961afae4a940e1e8b61028c1a2a49038d3 100644 --- a/lms/djangoapps/grades/tests/test_course_grade_factory.py +++ b/lms/djangoapps/grades/tests/test_course_grade_factory.py @@ -9,7 +9,7 @@ import ddt from django.conf import settings from mock import patch from six import text_type - +from edx_toggles.toggles.testutils import override_waffle_switch from lms.djangoapps.courseware.access import has_access from lms.djangoapps.grades.config.tests.utils import persistent_grades_feature_flags from openedx.core.djangoapps.content.block_structure.factory import BlockStructureFactory @@ -17,7 +17,7 @@ from student.tests.factories import UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory -from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT, waffle +from ..config.waffle import ASSUME_ZERO_GRADE_IF_ABSENT, waffle_switch from ..course_grade import CourseGrade, ZeroCourseGrade from ..course_grade_factory import CourseGradeFactory from ..subsection_grade import ReadSubsectionGrade, ZeroSubsectionGrade @@ -132,7 +132,7 @@ class TestCourseGradeFactory(GradeTestBase): @ddt.data(*itertools.product((True, False), (True, False))) @ddt.unpack def test_read_zero(self, assume_zero_enabled, create_if_needed): - with waffle().override(ASSUME_ZERO_GRADE_IF_ABSENT, active=assume_zero_enabled): + 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: diff --git a/openedx/core/djangoapps/certificates/tests/test_api.py b/openedx/core/djangoapps/certificates/tests/test_api.py index 319e727621c304b034332f19a5ec38e4fb67fc37..7cf4f508ac28995964085c6fd891ead667522b2b 100644 --- a/openedx/core/djangoapps/certificates/tests/test_api.py +++ b/openedx/core/djangoapps/certificates/tests/test_api.py @@ -13,6 +13,8 @@ from course_modes.models import CourseMode from openedx.core.djangoapps.certificates import api from openedx.core.djangoapps.certificates.config import waffle as certs_waffle from openedx.core.djangoapps.content.course_overviews.tests.factories import CourseOverviewFactory +from openedx.core.djangoapps.waffle_utils import WaffleSwitch +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from student.tests.factories import CourseEnrollmentFactory, UserFactory @@ -66,8 +68,8 @@ class MockGeneratedCertificate(object): @contextmanager def configure_waffle_namespace(feature_enabled): namespace = certs_waffle.waffle() - - with namespace.override(certs_waffle.AUTO_CERTIFICATE_GENERATION, active=feature_enabled): + auto_certificate_generation_switch = WaffleSwitch(namespace, certs_waffle.AUTO_CERTIFICATE_GENERATION) + with override_waffle_switch(auto_certificate_generation_switch, active=feature_enabled): yield diff --git a/openedx/core/djangoapps/content/block_structure/config/__init__.py b/openedx/core/djangoapps/content/block_structure/config/__init__.py index 05b0b4e7660eb09089857644f485a3ae3f85f1a4..c0df1cbe6d8c916bb3390ea8f9a8903784a30a24 100644 --- a/openedx/core/djangoapps/content/block_structure/config/__init__.py +++ b/openedx/core/djangoapps/content/block_structure/config/__init__.py @@ -4,7 +4,7 @@ waffle switches for the Block Structure framework. """ -from openedx.core.djangoapps.waffle_utils import WaffleSwitchNamespace +from openedx.core.djangoapps.waffle_utils import WaffleSwitch, WaffleSwitchNamespace from openedx.core.lib.cache_utils import request_cached from .models import BlockStructureConfiguration @@ -25,6 +25,16 @@ def waffle(): return WaffleSwitchNamespace(name=WAFFLE_NAMESPACE, log_prefix=u'BlockStructure: ') +def waffle_switch(name): + """ + Return the waffle switch associated to this namespace. + + WARNING: do not replicate this pattern. Instead of declaring waffle switch names as strings, you should create + WaffleSwitch objects as top-level constants. + """ + return WaffleSwitch(waffle(), name, module_name=__name__) + + @request_cached() def num_versions_to_keep(): """ diff --git a/openedx/core/djangoapps/content/block_structure/management/commands/generate_course_blocks.py b/openedx/core/djangoapps/content/block_structure/management/commands/generate_course_blocks.py index 7d9c22cebccab2eb9d7ddf42daae7cd5d71e409e..1ee189a52880e831e170e6dc569dce55030aeeab 100644 --- a/openedx/core/djangoapps/content/block_structure/management/commands/generate_course_blocks.py +++ b/openedx/core/djangoapps/content/block_structure/management/commands/generate_course_blocks.py @@ -133,7 +133,7 @@ class Command(BaseCommand): Generates course blocks for the given course_keys per the given options. """ if options.get('with_storage'): - waffle().override_for_request(STORAGE_BACKING_FOR_CACHE) + waffle().set_request_cache_with_short_name(STORAGE_BACKING_FOR_CACHE, True) for course_key in course_keys: try: diff --git a/openedx/core/djangoapps/content/block_structure/tasks.py b/openedx/core/djangoapps/content/block_structure/tasks.py index 7301c3f6b6f71b08ab6cc8559caa49dfaa42fe93..5989a68e57b9888d58975437e46dff70e17cb878 100644 --- a/openedx/core/djangoapps/content/block_structure/tasks.py +++ b/openedx/core/djangoapps/content/block_structure/tasks.py @@ -60,7 +60,7 @@ def _update_course_in_cache(self, **kwargs): Updates the course blocks (mongo -> BlockStructure) for the specified course. """ if kwargs.get('with_storage'): - waffle().override_for_request(STORAGE_BACKING_FOR_CACHE) + waffle().set_request_cache_with_short_name(STORAGE_BACKING_FOR_CACHE, True) _call_and_retry_if_needed(self, api.update_course_in_cache, **kwargs) @@ -89,7 +89,7 @@ def _get_course_in_cache(self, **kwargs): Gets the course blocks for the specified course, updating the cache if needed. """ if kwargs.get('with_storage'): - waffle().override_for_request(STORAGE_BACKING_FOR_CACHE) + waffle().set_request_cache_with_short_name(STORAGE_BACKING_FOR_CACHE, True) _call_and_retry_if_needed(self, api.get_course_in_cache, **kwargs) diff --git a/openedx/core/djangoapps/content/block_structure/tests/test_manager.py b/openedx/core/djangoapps/content/block_structure/tests/test_manager.py index 6fb13a3f2b586c3495dd6f6fe329293eb0b1381c..04d0c9a57ef5f1b762800f7738e4e1f05b2b7d88 100644 --- a/openedx/core/djangoapps/content/block_structure/tests/test_manager.py +++ b/openedx/core/djangoapps/content/block_structure/tests/test_manager.py @@ -7,8 +7,10 @@ import ddt import six from django.test import TestCase +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch + from ..block_structure import BlockStructureBlockData -from ..config import RAISE_ERROR_WHEN_NOT_FOUND, STORAGE_BACKING_FOR_CACHE, waffle +from ..config import RAISE_ERROR_WHEN_NOT_FOUND, STORAGE_BACKING_FOR_CACHE, waffle_switch from ..exceptions import BlockStructureNotFound, UsageKeyNotInBlockStructure from ..manager import BlockStructureManager from ..transformers import BlockStructureTransformers @@ -178,14 +180,14 @@ class TestBlockStructureManager(UsageKeyFactoryMixin, ChildrenMapTestMixin, Test assert TestTransformer1.collect_call_count == 1 def test_get_collected_error_raised(self): - with waffle().override(RAISE_ERROR_WHEN_NOT_FOUND, active=True): + with override_waffle_switch(waffle_switch(RAISE_ERROR_WHEN_NOT_FOUND), active=True): with mock_registered_transformers(self.registered_transformers): with self.assertRaises(BlockStructureNotFound): self.bs_manager.get_collected() @ddt.data(True, False) def test_update_collected_if_needed(self, with_storage_backing): - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): + with override_waffle_switch(waffle_switch(STORAGE_BACKING_FOR_CACHE), active=with_storage_backing): with mock_registered_transformers(self.registered_transformers): assert TestTransformer1.collect_call_count == 0 diff --git a/openedx/core/djangoapps/content/block_structure/tests/test_signals.py b/openedx/core/djangoapps/content/block_structure/tests/test_signals.py index d1679edb39af382bbb395123b9f3e9e272cea935..ebea9613e63db69244c127a82a89522990d22046 100644 --- a/openedx/core/djangoapps/content/block_structure/tests/test_signals.py +++ b/openedx/core/djangoapps/content/block_structure/tests/test_signals.py @@ -7,12 +7,13 @@ import ddt from mock import patch from opaque_keys.edx.locator import CourseLocator, LibraryLocator +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from xmodule.modulestore.exceptions import ItemNotFoundError from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory from ..api import get_block_structure_manager -from ..config import INVALIDATE_CACHE_ON_PUBLISH, waffle +from ..config import INVALIDATE_CACHE_ON_PUBLISH, waffle_switch from ..signals import update_block_structure_on_course_publish from .helpers import is_course_in_block_structure_cache @@ -56,7 +57,7 @@ class CourseBlocksSignalTest(ModuleStoreTestCase): def test_cache_invalidation(self, invalidate_cache_enabled, mock_bs_manager_clear): test_display_name = "Jedi 101" - with waffle().override(INVALIDATE_CACHE_ON_PUBLISH, active=invalidate_cache_enabled): + with override_waffle_switch(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) diff --git a/openedx/core/djangoapps/content/block_structure/tests/test_store.py b/openedx/core/djangoapps/content/block_structure/tests/test_store.py index aa35635ab4ad65640b132d90896baf09cb299dd0..5db54bd0fbd390ce697bb1e0dffe8bd1cbfbc718 100644 --- a/openedx/core/djangoapps/content/block_structure/tests/test_store.py +++ b/openedx/core/djangoapps/content/block_structure/tests/test_store.py @@ -5,9 +5,10 @@ Tests for block_structure/cache.py import ddt +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from openedx.core.djangolib.testing.utils import CacheIsolationTestCase -from ..config import STORAGE_BACKING_FOR_CACHE, waffle +from ..config import STORAGE_BACKING_FOR_CACHE, waffle_switch from ..config.models import BlockStructureConfiguration from ..exceptions import BlockStructureNotFound from ..store import BlockStructureStore @@ -47,13 +48,13 @@ class TestBlockStructureStore(UsageKeyFactoryMixin, ChildrenMapTestMixin, CacheI @ddt.data(True, False) def test_get_none(self, with_storage_backing): - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): + with override_waffle_switch(waffle_switch(STORAGE_BACKING_FOR_CACHE), active=with_storage_backing): with self.assertRaises(BlockStructureNotFound): self.store.get(self.block_structure.root_block_usage_key) @ddt.data(True, False) def test_add_and_get(self, with_storage_backing): - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): + with override_waffle_switch(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) self.assertIsNotNone(stored_value) @@ -61,7 +62,7 @@ class TestBlockStructureStore(UsageKeyFactoryMixin, ChildrenMapTestMixin, CacheI @ddt.data(True, False) def test_delete(self, with_storage_backing): - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=with_storage_backing): + with override_waffle_switch(waffle_switch(STORAGE_BACKING_FOR_CACHE), active=with_storage_backing): self.store.add(self.block_structure) self.store.delete(self.block_structure.root_block_usage_key) with self.assertRaises(BlockStructureNotFound): @@ -74,7 +75,7 @@ class TestBlockStructureStore(UsageKeyFactoryMixin, ChildrenMapTestMixin, CacheI self.store.get(self.block_structure.root_block_usage_key) def test_uncached_with_storage(self): - with waffle().override(STORAGE_BACKING_FOR_CACHE, active=True): + 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) diff --git a/openedx/core/djangoapps/coursegraph/management/commands/tests/test_dump_to_neo4j.py b/openedx/core/djangoapps/coursegraph/management/commands/tests/test_dump_to_neo4j.py index cf7ecd043a0340bfeae4a0a42b766007a855a898..4576332a13d3bbd758788172e06be102da73f880 100644 --- a/openedx/core/djangoapps/coursegraph/management/commands/tests/test_dump_to_neo4j.py +++ b/openedx/core/djangoapps/coursegraph/management/commands/tests/test_dump_to_neo4j.py @@ -10,29 +10,22 @@ import ddt import mock import six from django.core.management import call_command -from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase -from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory - -from openedx.core.djangolib.testing.utils import skip_unless_lms -from openedx.core.djangoapps.coursegraph.management.commands.dump_to_neo4j import ( - ModuleStoreSerializer -) -from openedx.core.djangoapps.coursegraph.management.commands.tests.utils import ( - MockGraph, - MockNodeSelector, -) +import openedx.core.djangoapps.content.block_structure.config as block_structure_config +from openedx.core.djangoapps.content.block_structure.signals import update_block_structure_on_course_publish +from openedx.core.djangoapps.coursegraph.management.commands.dump_to_neo4j import ModuleStoreSerializer +from openedx.core.djangoapps.coursegraph.management.commands.tests.utils import MockGraph, MockNodeSelector from openedx.core.djangoapps.coursegraph.tasks import ( - serialize_item, - serialize_course, coerce_types, + serialize_course, + serialize_item, should_dump_course, - strip_branch_and_version, -) -from openedx.core.djangoapps.content.block_structure.signals import ( - update_block_structure_on_course_publish + strip_branch_and_version ) -import openedx.core.djangoapps.content.block_structure.config as block_structure_config +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch +from openedx.core.djangolib.testing.utils import skip_unless_lms +from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase +from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory class TestDumpToNeo4jCommandBase(SharedModuleStoreTestCase): @@ -507,7 +500,9 @@ class TestModuleStoreSerializer(TestDumpToNeo4jCommandBase): self.assertEqual(len(submitted), len(self.course_strings)) # simulate one of the courses being published - with block_structure_config.waffle().override(block_structure_config.STORAGE_BACKING_FOR_CACHE): + with override_waffle_switch( + block_structure_config.waffle_switch(block_structure_config.STORAGE_BACKING_FOR_CACHE), True + ): update_block_structure_on_course_publish(None, self.course.id) # make sure only the published course was dumped diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_login.py b/openedx/core/djangoapps/user_authn/views/tests/test_login.py index cf2157e0b7d10a62c47514ca23aae3e9146621ee..c86e206e30c88a0b86288b5c947dc934c4990a69 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_login.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_login.py @@ -32,6 +32,7 @@ from openedx.core.djangoapps.user_authn.views.login import ( AllowedAuthUser, _check_user_auth_flow ) +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin from openedx.core.lib.api.test_utils import ApiTestCase @@ -725,7 +726,7 @@ class LoginTest(SiteMixin, CacheIsolationTestCase): 'THIRD_PARTY_AUTH_ONLY_HINT': provider_tpa_hint, } - with ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY.override(switch_enabled): + with override_waffle_switch(ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY, switch_enabled): if not is_third_party_authenticated: site = self.set_up_site(allowed_domain, default_site_configuration_values) @@ -778,7 +779,7 @@ class LoginTest(SiteMixin, CacheIsolationTestCase): 'THIRD_PARTY_AUTH_ONLY_HINT': provider_tpa_hint, } - with ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY.override(True): + with override_waffle_switch(ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY, True): site = self.set_up_site(allowed_domain, default_site_configuration_values) with self.assertLogs(level='WARN') as log: diff --git a/openedx/core/djangoapps/waffle_utils/__init__.py b/openedx/core/djangoapps/waffle_utils/__init__.py index 98a3b4afd756d9adf747413bb71846c3cfea412a..72e6912856536c0c5a2086a4f00bf0469ef7b57b 100644 --- a/openedx/core/djangoapps/waffle_utils/__init__.py +++ b/openedx/core/djangoapps/waffle_utils/__init__.py @@ -3,97 +3,15 @@ Extra utilities for waffle: most classes are defined in edx_toggles.toggles (htt we keep here some extra classes for usage within edx-platform. These classes cover course override use cases. """ import logging -from contextlib import contextmanager from opaque_keys.edx.keys import CourseKey -from edx_toggles.toggles import WaffleFlag, WaffleFlagNamespace -from edx_toggles.toggles import WaffleSwitch as BaseWaffleSwitch -from edx_toggles.toggles import WaffleSwitchNamespace as BaseWaffleSwitchNamespace +# pylint: disable=unused-import +from edx_toggles.toggles import WaffleFlag, WaffleFlagNamespace, WaffleSwitch, WaffleSwitchNamespace log = logging.getLogger(__name__) -class WaffleSwitchNamespace(BaseWaffleSwitchNamespace): - """ - Waffle switch namespace that implements custom overriding methods. We should eventually get rid of this class. - - To test WaffleSwitchNamespace, use the provided context managers. For example: - - with WAFFLE_SWITCHES.override(waffle.ESTIMATE_FIRST_ATTEMPTED, active=True): - ... - - Note: this should eventually be deprecated in favour of a dedicated `override_waffle_switch` context manager. - """ - - @contextmanager - def override(self, switch_name, active=True): - """ - Overrides the active value for the given switch for the duration of this - contextmanager. - Note: The value is overridden in the request cache AND in the model. - """ - previous_active = self.is_enabled(switch_name) - try: - self.override_for_request(switch_name, active) - with self.override_in_model(switch_name, active): - yield - finally: - self.override_for_request(switch_name, previous_active) - - def override_for_request(self, switch_name, active=True): - """ - Overrides the active value for the given switch for the remainder of - this request (as this is not a context manager). - Note: The value is overridden in the request cache, not in the model. - """ - namespaced_switch_name = self._namespaced_name(switch_name) - self._cached_switches[namespaced_switch_name] = active - log.info( - "%sSwitch '%s' set to %s for request.", - self.log_prefix, - namespaced_switch_name, - active, - ) - - @contextmanager - def override_in_model(self, switch_name, active=True): - """ - Overrides the active value for the given switch for the duration of this - contextmanager. - Note: The value is overridden in the model, not the request cache. - Note: This should probably be moved to a test class. - """ - # Import is placed here to avoid model import at project startup. - # pylint: disable=import-outside-toplevel - from waffle.testutils import override_switch as waffle_override_switch - - namespaced_switch_name = self._namespaced_name(switch_name) - with waffle_override_switch(namespaced_switch_name, active): - log.info( - "%sSwitch '%s' set to %s in model.", - self.log_prefix, - namespaced_switch_name, - active, - ) - yield - - -class WaffleSwitch(BaseWaffleSwitch): - """ - This class should be removed in favour of edx_toggles.toggles.WaffleSwitch once we get rid of the - WaffleSwitchNamespace class. - """ - - NAMESPACE_CLASS = WaffleSwitchNamespace - - @contextmanager - def override(self, active=True): - with self.waffle_namespace.override(self.switch_name, active): - yield - - - class CourseWaffleFlag(WaffleFlag): """ Represents a single waffle flag that can be forced on/off for a course. This class should be used instead of diff --git a/openedx/core/djangoapps/waffle_utils/tests/test_init.py b/openedx/core/djangoapps/waffle_utils/tests/test_init.py index e39359dc4f3979529ad922c45710471e64cf1076..92220ac7588fe7a8934f1d670d81128685309297 100644 --- a/openedx/core/djangoapps/waffle_utils/tests/test_init.py +++ b/openedx/core/djangoapps/waffle_utils/tests/test_init.py @@ -7,7 +7,8 @@ import ddt from django.test import TestCase from django.test.client import RequestFactory from django.test.utils import override_settings -# Note that we really shouldn't import from edx_toggles' internal API +# TODO: we really shouldn't import from edx_toggles' internal API, but that's currently the only way to mock the +# monitoring functions. import edx_toggles.toggles.internal.waffle from edx_django_utils.cache import RequestCache from mock import call, patch diff --git a/openedx/core/djangoapps/waffle_utils/testutils.py b/openedx/core/djangoapps/waffle_utils/testutils.py index 03c44ce3a50799e5207bece6bc17c322aa3e24c0..896e81dffff157c1fc475915c1bc75080536c73d 100644 --- a/openedx/core/djangoapps/waffle_utils/testutils.py +++ b/openedx/core/djangoapps/waffle_utils/testutils.py @@ -5,7 +5,7 @@ Test utilities for waffle utilities. # Import from edx-toggles to preserve import paths # TODO: Deprecate and remove # pylint: disable=unused-import -from edx_toggles.toggles.testutils import override_waffle_flag +from edx_toggles.toggles.testutils import override_waffle_flag, override_waffle_switch # Can be used with FilteredQueryCountMixin.assertNumQueries() to blacklist # waffle tables. For example: diff --git a/openedx/core/tests/test_admin_view.py b/openedx/core/tests/test_admin_view.py index 7362a8c14610aa7b377ddae336d315242a2c8d5c..d7f3cae626b9979ee598af7b735c869cd50d6f93 100644 --- a/openedx/core/tests/test_admin_view.py +++ b/openedx/core/tests/test_admin_view.py @@ -4,10 +4,12 @@ Tests that verify that the admin view loads. This is not inside a django app because it is a global property of the system. """ -from django.test import TestCase, Client +from django.test import Client, TestCase from django.urls import reverse -from student.tests.factories import UserFactory, TEST_PASSWORD + from openedx.core.djangoapps.user_authn.views.login import ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_switch +from student.tests.factories import TEST_PASSWORD, UserFactory class TestAdminView(TestCase): @@ -37,10 +39,10 @@ class TestAdminView(TestCase): assert response.status_code == 302 def test_admin_login_redirect(self): - with ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY.override(True): + 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 ENABLE_LOGIN_USING_THIRDPARTY_AUTH_ONLY.override(False): + 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'] diff --git a/openedx/tests/completion_integration/test_views.py b/openedx/tests/completion_integration/test_views.py index 15be7248f38d09774187a47edf3cb761266ac96c..9f05624f0a28c3eff4c947c040bf89102dbdaa77 100644 --- a/openedx/tests/completion_integration/test_views.py +++ b/openedx/tests/completion_integration/test_views.py @@ -5,11 +5,11 @@ Test models, managers, and validators. import ddt +import six from completion import waffle from completion.test_utils import CompletionWaffleTestMixin from django.urls import reverse from rest_framework.test import APIClient -import six from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import CourseEnrollmentFactory, UserFactory