Populate experiment data with holdback key post enrollment and check holdback key when setting user group
This commit is contained in:
@@ -1168,6 +1168,7 @@ INSTALLED_APPS = [
|
||||
|
||||
'openedx.features.course_duration_limits',
|
||||
'openedx.features.content_type_gating',
|
||||
'experiments',
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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',
|
||||
))
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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())
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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',
|
||||
|
||||
Reference in New Issue
Block a user