diff --git a/cms/envs/common.py b/cms/envs/common.py index 3d74d1b808..73c9bde775 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1168,6 +1168,7 @@ INSTALLED_APPS = [ 'openedx.features.course_duration_limits', 'openedx.features.content_type_gating', + 'experiments', ] diff --git a/lms/djangoapps/ccx/tests/test_field_override_performance.py b/lms/djangoapps/ccx/tests/test_field_override_performance.py index b5de22f460..853d1ab28c 100644 --- a/lms/djangoapps/ccx/tests/test_field_override_performance.py +++ b/lms/djangoapps/ccx/tests/test_field_override_performance.py @@ -244,18 +244,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase): # # of sql queries to default, # # of mongo queries, # ) - ('no_overrides', 1, True, False): (26, 1), - ('no_overrides', 2, True, False): (26, 1), - ('no_overrides', 3, True, False): (26, 1), - ('ccx', 1, True, False): (26, 1), - ('ccx', 2, True, False): (26, 1), - ('ccx', 3, True, False): (26, 1), - ('no_overrides', 1, False, False): (26, 1), - ('no_overrides', 2, False, False): (26, 1), - ('no_overrides', 3, False, False): (26, 1), - ('ccx', 1, False, False): (26, 1), - ('ccx', 2, False, False): (26, 1), - ('ccx', 3, False, False): (26, 1), + ('no_overrides', 1, True, False): (31, 1), + ('no_overrides', 2, True, False): (31, 1), + ('no_overrides', 3, True, False): (31, 1), + ('ccx', 1, True, False): (31, 1), + ('ccx', 2, True, False): (31, 1), + ('ccx', 3, True, False): (31, 1), + ('no_overrides', 1, False, False): (31, 1), + ('no_overrides', 2, False, False): (31, 1), + ('no_overrides', 3, False, False): (31, 1), + ('ccx', 1, False, False): (31, 1), + ('ccx', 2, False, False): (31, 1), + ('ccx', 3, False, False): (31, 1), } @@ -267,19 +267,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase): __test__ = True TEST_DATA = { - ('no_overrides', 1, True, False): (26, 3), - ('no_overrides', 2, True, False): (26, 3), - ('no_overrides', 3, True, False): (26, 3), - ('ccx', 1, True, False): (26, 3), - ('ccx', 2, True, False): (26, 3), - ('ccx', 3, True, False): (26, 3), - ('ccx', 1, True, True): (27, 3), - ('ccx', 2, True, True): (27, 3), - ('ccx', 3, True, True): (27, 3), - ('no_overrides', 1, False, False): (26, 3), - ('no_overrides', 2, False, False): (26, 3), - ('no_overrides', 3, False, False): (26, 3), - ('ccx', 1, False, False): (26, 3), - ('ccx', 2, False, False): (26, 3), - ('ccx', 3, False, False): (26, 3), + ('no_overrides', 1, True, False): (31, 3), + ('no_overrides', 2, True, False): (31, 3), + ('no_overrides', 3, True, False): (31, 3), + ('ccx', 1, True, False): (31, 3), + ('ccx', 2, True, False): (31, 3), + ('ccx', 3, True, False): (31, 3), + ('ccx', 1, True, True): (32, 3), + ('ccx', 2, True, True): (32, 3), + ('ccx', 3, True, True): (32, 3), + ('no_overrides', 1, False, False): (31, 3), + ('no_overrides', 2, False, False): (31, 3), + ('no_overrides', 3, False, False): (31, 3), + ('ccx', 1, False, False): (31, 3), + ('ccx', 2, False, False): (31, 3), + ('ccx', 3, False, False): (31, 3), } diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index b73419dae8..81f7da6d9b 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase): NUM_PROBLEMS = 20 @ddt.data( - (ModuleStoreEnum.Type.mongo, 10, 162), - (ModuleStoreEnum.Type.split, 4, 160), + (ModuleStoreEnum.Type.mongo, 10, 169), + (ModuleStoreEnum.Type.split, 4, 165), ) @ddt.unpack def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count): @@ -1439,8 +1439,8 @@ class ProgressPageTests(ProgressPageBaseTests): self.assertContains(resp, u"Download Your Certificate") @ddt.data( - (True, 46), - (False, 45) + (True, 51), + (False, 50) ) @ddt.unpack def test_progress_queries_paced_courses(self, self_paced, query_count): @@ -1452,8 +1452,8 @@ class ProgressPageTests(ProgressPageBaseTests): @patch.dict(settings.FEATURES, {'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS': False}) @ddt.data( - (False, 53, 33), - (True, 45, 29) + (False, 58, 38), + (True, 50, 34) ) @ddt.unpack def test_progress_queries(self, enable_waffle, initial, subsequent): diff --git a/lms/djangoapps/discussion/tests/test_views.py b/lms/djangoapps/discussion/tests/test_views.py index b239c23b1f..13ba910806 100644 --- a/lms/djangoapps/discussion/tests/test_views.py +++ b/lms/djangoapps/discussion/tests/test_views.py @@ -431,18 +431,18 @@ class SingleThreadQueryCountTestCase(ForumsEnableMixin, ModuleStoreTestCase): # course is outside the context manager that is verifying the number of queries, # and with split mongo, that method ends up querying disabled_xblocks (which is then # cached and hence not queried as part of call_single_thread). - (ModuleStoreEnum.Type.mongo, False, 1, 5, 2, 21, 6), - (ModuleStoreEnum.Type.mongo, False, 50, 5, 2, 21, 6), + (ModuleStoreEnum.Type.mongo, False, 1, 5, 2, 22, 7), + (ModuleStoreEnum.Type.mongo, False, 50, 5, 2, 22, 7), # split mongo: 3 queries, regardless of thread response size. - (ModuleStoreEnum.Type.split, False, 1, 3, 3, 21, 6), - (ModuleStoreEnum.Type.split, False, 50, 3, 3, 21, 6), + (ModuleStoreEnum.Type.split, False, 1, 3, 3, 22, 7), + (ModuleStoreEnum.Type.split, False, 50, 3, 3, 22, 7), # Enabling Enterprise integration should have no effect on the number of mongo queries made. - (ModuleStoreEnum.Type.mongo, True, 1, 5, 2, 21, 6), - (ModuleStoreEnum.Type.mongo, True, 50, 5, 2, 21, 6), + (ModuleStoreEnum.Type.mongo, True, 1, 5, 2, 22, 7), + (ModuleStoreEnum.Type.mongo, True, 50, 5, 2, 22, 7), # split mongo: 3 queries, regardless of thread response size. - (ModuleStoreEnum.Type.split, True, 1, 3, 3, 21, 6), - (ModuleStoreEnum.Type.split, True, 50, 3, 3, 21, 6), + (ModuleStoreEnum.Type.split, True, 1, 3, 3, 22, 7), + (ModuleStoreEnum.Type.split, True, 50, 3, 3, 22, 7), ) @ddt.unpack def test_number_of_mongo_queries( diff --git a/lms/djangoapps/django_comment_client/base/tests.py b/lms/djangoapps/django_comment_client/base/tests.py index a80544e2dd..b7af268758 100644 --- a/lms/djangoapps/django_comment_client/base/tests.py +++ b/lms/djangoapps/django_comment_client/base/tests.py @@ -403,8 +403,8 @@ class ViewsQueryCountTestCase( return inner @ddt.data( - (ModuleStoreEnum.Type.mongo, 3, 4, 39), - (ModuleStoreEnum.Type.split, 3, 13, 39), + (ModuleStoreEnum.Type.mongo, 3, 4, 40), + (ModuleStoreEnum.Type.split, 3, 13, 40), ) @ddt.unpack @count_queries @@ -412,8 +412,8 @@ class ViewsQueryCountTestCase( self.create_thread_helper(mock_request) @ddt.data( - (ModuleStoreEnum.Type.mongo, 3, 3, 35), - (ModuleStoreEnum.Type.split, 3, 10, 35), + (ModuleStoreEnum.Type.mongo, 3, 3, 36), + (ModuleStoreEnum.Type.split, 3, 10, 36), ) @ddt.unpack @count_queries diff --git a/lms/djangoapps/grades/tests/test_course_grade_factory.py b/lms/djangoapps/grades/tests/test_course_grade_factory.py index b365dd4d4f..a205f111ff 100644 --- a/lms/djangoapps/grades/tests/test_course_grade_factory.py +++ b/lms/djangoapps/grades/tests/test_course_grade_factory.py @@ -92,35 +92,35 @@ class TestCourseGradeFactory(GradeTestBase): [self.sequence.display_name, self.sequence2.display_name] ) - with self.assertNumQueries(3), mock_get_score(1, 2): + with self.assertNumQueries(4), mock_get_score(1, 2): _assert_read(expected_pass=False, expected_percent=0) # start off with grade of 0 - num_queries = 42 + num_queries = 43 with self.assertNumQueries(num_queries), mock_get_score(1, 2): grade_factory.update(self.request.user, self.course, force_update_subsections=True) - with self.assertNumQueries(3): + with self.assertNumQueries(4): _assert_read(expected_pass=True, expected_percent=0.5) # updated to grade of .5 - num_queries = 7 + num_queries = 8 with self.assertNumQueries(num_queries), mock_get_score(1, 4): grade_factory.update(self.request.user, self.course, force_update_subsections=False) - with self.assertNumQueries(3): + with self.assertNumQueries(4): _assert_read(expected_pass=True, expected_percent=0.5) # NOT updated to grade of .25 - num_queries = 21 + num_queries = 22 with self.assertNumQueries(num_queries), mock_get_score(2, 2): grade_factory.update(self.request.user, self.course, force_update_subsections=True) - with self.assertNumQueries(3): + with self.assertNumQueries(4): _assert_read(expected_pass=True, expected_percent=1.0) # updated to grade of 1.0 - num_queries = 24 + num_queries = 25 with self.assertNumQueries(num_queries), mock_get_score(0, 0): # the subsection now is worth zero grade_factory.update(self.request.user, self.course, force_update_subsections=True) - with self.assertNumQueries(3): + with self.assertNumQueries(4): _assert_read(expected_pass=False, expected_percent=0.0) # updated to grade of 0.0 @patch.dict(settings.FEATURES, {'ASSUME_ZERO_GRADE_IF_ABSENT_FOR_ALL_TESTS': False}) diff --git a/openedx/features/content_type_gating/models.py b/openedx/features/content_type_gating/models.py index f69d1fc0cc..cbb62d9b6d 100644 --- a/openedx/features/content_type_gating/models.py +++ b/openedx/features/content_type_gating/models.py @@ -11,9 +11,14 @@ from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ +from experiments.models import ExperimentData from student.models import CourseEnrollment from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel -from openedx.features.course_duration_limits.config import CONTENT_TYPE_GATING_FLAG +from openedx.features.course_duration_limits.config import ( + CONTENT_TYPE_GATING_FLAG, + EXPERIMENT_ID, + EXPERIMENT_DATA_HOLDBACK_KEY +) @python_2_unicode_compatible @@ -84,6 +89,20 @@ class ContentTypeGatingConfig(StackedConfigurationModel): if enrollment is None: return cls.enabled_for_course(course_key=course_key, target_date=datetime.utcnow().date()) else: + # TODO: clean up as part of REV-100 + experiment_data_holdback_key = EXPERIMENT_DATA_HOLDBACK_KEY.format(user) + is_in_holdback = False + try: + holdback_value = ExperimentData.objects.get( + user=user, + experiment_id=EXPERIMENT_ID, + key=experiment_data_holdback_key, + ).value + is_in_holdback = holdback_value == 'True' + except ExperimentData.DoesNotExist: + pass + if is_in_holdback: + return False current_config = cls.current(course_key=enrollment.course_id) return current_config.enabled_as_of_date(target_date=enrollment.created.date()) diff --git a/openedx/features/content_type_gating/tests/test_access.py b/openedx/features/content_type_gating/tests/test_access.py index 1396edb034..1f52eabac8 100644 --- a/openedx/features/content_type_gating/tests/test_access.py +++ b/openedx/features/content_type_gating/tests/test_access.py @@ -11,19 +11,8 @@ from django.urls import reverse from mock import patch from course_modes.tests.factories import CourseModeFactory +from experiments.models import ExperimentKeyValue from lms.djangoapps.courseware.module_render import load_single_xblock -from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory -from openedx.core.djangoapps.util.testing import TestConditionalContent -from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag -from openedx.core.lib.url_utils import quote_slashes -from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID -from openedx.features.content_type_gating.models import ContentTypeGatingConfig -from student.roles import CourseBetaTesterRole, CourseInstructorRole, CourseStaffRole -from student.tests.factories import ( - CourseEnrollmentFactory, - UserFactory, - TEST_PASSWORD -) from lms.djangoapps.courseware.tests.factories import ( InstructorFactory, StaffFactory, @@ -32,6 +21,23 @@ from lms.djangoapps.courseware.tests.factories import ( OrgInstructorFactory, GlobalStaffFactory, ) +from openedx.core.djangoapps.user_api.tests.factories import UserCourseTagFactory +from openedx.core.djangoapps.util.testing import TestConditionalContent +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag +from openedx.core.lib.url_utils import quote_slashes +from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID +from openedx.features.content_type_gating.models import ContentTypeGatingConfig +from openedx.features.course_duration_limits.config import ( + EXPERIMENT_DATA_HOLDBACK_KEY, + EXPERIMENT_ID, +) +from student.models import CourseEnrollment +from student.roles import CourseBetaTesterRole, CourseInstructorRole, CourseStaffRole +from student.tests.factories import ( + CourseEnrollmentFactory, + UserFactory, + TEST_PASSWORD +) from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory @@ -430,8 +436,36 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase): request_factory=self.factory, ) + @ddt.data( + (False, True), + (True, False), + ) + @ddt.unpack + def test_content_gating_holdback(self, put_user_in_holdback, is_gated): + """ + Test that putting a user in the content gating holdback disables content gating. + """ + if put_user_in_holdback: + ExperimentKeyValue.objects.create( + experiment_id=EXPERIMENT_ID, + key="content_type_gating_holdback_percentage", + value="100" + ).value + + user = UserFactory.create() + CourseEnrollment.enroll(user, self.course.id) + + graded, has_score, weight = True, True, 1 + block = self.graded_score_weight_blocks[(graded, has_score, weight)] + _assert_block_is_gated( + block=block, + user_id=user.id, + course=self.course, + is_gated=is_gated, + request_factory=self.factory, + ) + -@ddt.ddt @override_settings(FIELD_OVERRIDE_PROVIDERS=( 'openedx.features.content_type_gating.field_override.ContentTypeGatingFieldOverride', )) diff --git a/openedx/features/content_type_gating/tests/test_models.py b/openedx/features/content_type_gating/tests/test_models.py index c8e56d55a2..5d569ab0b5 100644 --- a/openedx/features/content_type_gating/tests/test_models.py +++ b/openedx/features/content_type_gating/tests/test_models.py @@ -68,10 +68,9 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase): user = self.user course_key = self.course_overview.id - if pass_enrollment: - query_count = 4 - else: - query_count = 5 + query_count = 5 + if not pass_enrollment and already_enrolled: + query_count = 6 with self.assertNumQueries(query_count): enabled = ContentTypeGatingConfig.enabled_for_enrollment( diff --git a/openedx/features/course_duration_limits/config.py b/openedx/features/course_duration_limits/config.py index eff4f675e7..be58edac69 100644 --- a/openedx/features/course_duration_limits/config.py +++ b/openedx/features/course_duration_limits/config.py @@ -1,7 +1,14 @@ """ Content type gating waffle flag """ +import random + +from django.dispatch import receiver + +from experiments.models import ExperimentData, ExperimentKeyValue from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace, WaffleFlag +from student.models import EnrollStatusChange +from student.signals import ENROLL_STATUS_CHANGE WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name=u'content_type_gating') @@ -11,3 +18,35 @@ CONTENT_TYPE_GATING_FLAG = WaffleFlag( flag_name=u'debug', flag_undefined_default=False ) + +EXPERIMENT_ID = 11 +EXPERIMENT_DATA_HOLDBACK_KEY = 'holdback_{0}' + + +@receiver(ENROLL_STATUS_CHANGE) +def set_value_for_content_type_gating_holdback(sender, event=None, user=None, **kwargs): # pylint: disable=unused-argument + experiment_data_holdback_key = EXPERIMENT_DATA_HOLDBACK_KEY.format(user) + if event == EnrollStatusChange.enroll: + user_holdback_data = ExperimentData.objects.filter( + user=user, + experiment_id=EXPERIMENT_ID, + key=experiment_data_holdback_key, + ) + user_holdback_data_already_set = user_holdback_data.exists() + if not user_holdback_data_already_set: + try: + content_type_gating_holdback_percentage_value = ExperimentKeyValue.objects.get( + experiment_id=EXPERIMENT_ID, + key="content_type_gating_holdback_percentage" + ).value + content_type_gating_holdback_percentage = float(content_type_gating_holdback_percentage_value) / 100 + is_in_holdback = str(random.random() < content_type_gating_holdback_percentage) + + ExperimentData.objects.create( + user=user, + experiment_id=EXPERIMENT_ID, + key=experiment_data_holdback_key, + value=is_in_holdback + ) + except (ExperimentKeyValue.DoesNotExist, AttributeError): + pass diff --git a/openedx/features/course_duration_limits/models.py b/openedx/features/course_duration_limits/models.py index 5364b96b19..34c7f2fa50 100644 --- a/openedx/features/course_duration_limits/models.py +++ b/openedx/features/course_duration_limits/models.py @@ -11,9 +11,14 @@ from django.db import models from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ +from experiments.models import ExperimentData from student.models import CourseEnrollment from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel -from openedx.features.course_duration_limits.config import CONTENT_TYPE_GATING_FLAG +from openedx.features.course_duration_limits.config import ( + CONTENT_TYPE_GATING_FLAG, + EXPERIMENT_ID, + EXPERIMENT_DATA_HOLDBACK_KEY +) @python_2_unicode_compatible @@ -75,6 +80,20 @@ class CourseDurationLimitConfig(StackedConfigurationModel): if enrollment is None: return cls.enabled_for_course(course_key=course_key, target_date=datetime.utcnow().date()) else: + # TODO: clean up as part of REV-100 + experiment_data_holdback_key = EXPERIMENT_DATA_HOLDBACK_KEY.format(user) + is_in_holdback = False + try: + holdback_value = ExperimentData.objects.get( + user=user, + experiment_id=EXPERIMENT_ID, + key=experiment_data_holdback_key, + ).value + is_in_holdback = holdback_value == 'True' + except ExperimentData.DoesNotExist: + pass + if is_in_holdback: + return False current_config = cls.current(course_key=enrollment.course_id) return current_config.enabled_as_of_date(target_date=enrollment.created.date()) diff --git a/openedx/features/course_duration_limits/tests/test_models.py b/openedx/features/course_duration_limits/tests/test_models.py index 1d38736bf8..1b596b6f6f 100644 --- a/openedx/features/course_duration_limits/tests/test_models.py +++ b/openedx/features/course_duration_limits/tests/test_models.py @@ -76,10 +76,9 @@ class TestCourseDurationLimitConfig(CacheIsolationTestCase): user = self.user course_key = self.course_overview.id - if pass_enrollment: - query_count = 4 - else: - query_count = 5 + query_count = 5 + if not pass_enrollment and already_enrolled: + query_count = 6 with self.assertNumQueries(query_count): enabled = CourseDurationLimitConfig.enabled_for_enrollment( diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py index 761de4d89e..15a158ccd5 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -18,6 +18,7 @@ from waffle.testutils import override_flag from course_modes.models import CourseMode from course_modes.tests.factories import CourseModeFactory from courseware.tests.helpers import get_expiration_banner_text +from experiments.models import ExperimentKeyValue from lms.djangoapps.commerce.models import CommerceConfiguration from lms.djangoapps.commerce.utils import EcommerceService from lms.djangoapps.course_goals.api import add_course_goal, remove_course_goal @@ -32,6 +33,7 @@ from lms.djangoapps.courseware.tests.factories import ( from openedx.core.djangoapps.content.course_overviews.models import CourseOverview from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory from openedx.core.djangoapps.waffle_utils.testutils import WAFFLE_TABLES, override_waffle_flag +from openedx.features.course_duration_limits.config import EXPERIMENT_ID from openedx.features.course_duration_limits.models import CourseDurationLimitConfig from openedx.features.course_experience import ( SHOW_REVIEWS_TOOL_FLAG, @@ -192,7 +194,7 @@ class TestCourseHomePage(CourseHomePageTestCase): course_home_url(self.course) # Fetch the view and verify the query counts - with self.assertNumQueries(68, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): + with self.assertNumQueries(76, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with check_mongo_calls(4): url = course_home_url(self.course) self.client.get(url) @@ -475,6 +477,37 @@ class TestCourseHomePageAccess(CourseHomePageTestCase): self.assertContains(response, TEST_COURSE_TODAY) self.assertNotContains(response, TEST_BANNER_CLASS) + @mock.patch.dict(settings.FEATURES, {'DISABLE_START_DATES': False}) + def test_expired_course_in_holdback(self): + """ + Ensure that a user accessing an expired course that is in the holdback + does not get redirected to the student dashboard, not a 404. + """ + CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=date(2010, 1, 1)) + + course = CourseFactory.create(start=THREE_YEARS_AGO) + url = course_home_url(course) + + for mode in [CourseMode.AUDIT, CourseMode.VERIFIED]: + CourseModeFactory.create(course_id=course.id, mode_slug=mode) + + ExperimentKeyValue.objects.create( + experiment_id=EXPERIMENT_ID, + key="content_type_gating_holdback_percentage", + value="100" + ) + + # assert that an if an expired audit user in the holdback tries to access the course + # they are not redirected to the dashboard + audit_user = UserFactory(password=self.TEST_PASSWORD) + self.client.login(username=audit_user.username, password=self.TEST_PASSWORD) + audit_enrollment = CourseEnrollment.enroll(audit_user, course.id, mode=CourseMode.AUDIT) + ScheduleFactory(start=THREE_YEARS_AGO, enrollment=audit_enrollment) + + response = self.client.get(url) + + self.assertEqual(response.status_code, 200) + @mock.patch.dict(settings.FEATURES, {'DISABLE_START_DATES': False}) @mock.patch("util.date_utils.strftime_localized") def test_non_live_course_other_language(self, mock_strftime_localized): diff --git a/openedx/features/course_experience/tests/views/test_course_updates.py b/openedx/features/course_experience/tests/views/test_course_updates.py index b94e9649e1..789a64fe82 100644 --- a/openedx/features/course_experience/tests/views/test_course_updates.py +++ b/openedx/features/course_experience/tests/views/test_course_updates.py @@ -129,7 +129,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase): course_updates_url(self.course) # Fetch the view and verify that the query counts haven't changed - with self.assertNumQueries(46, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): + with self.assertNumQueries(50, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with check_mongo_calls(4): url = course_updates_url(self.course) self.client.get(url) diff --git a/openedx/tests/settings.py b/openedx/tests/settings.py index 3b14b9f5f8..652568f937 100644 --- a/openedx/tests/settings.py +++ b/openedx/tests/settings.py @@ -75,6 +75,7 @@ INSTALLED_APPS = ( 'openedx.core.djangoapps.content.block_structure.apps.BlockStructureConfig', 'openedx.core.djangoapps.catalog', 'openedx.core.djangoapps.self_paced', + 'experiments', 'openedx.features.content_type_gating', 'openedx.features.course_duration_limits', 'milestones',