diff --git a/lms/templates/preview_menu.html b/lms/templates/preview_menu.html
index 949dc4ee5e..0e64476799 100644
--- a/lms/templates/preview_menu.html
+++ b/lms/templates/preview_menu.html
@@ -22,6 +22,7 @@ show_preview_menu = course and staff_access and supports_preview_menu
course_partitions = get_all_partitions_for_course(course)
masquerade_user_name = masquerade.user_name if masquerade else None
masquerade_group_id = masquerade.group_id if masquerade else None
+ masquerade_user_partition_id = masquerade.user_partition_id if masquerade else None
staff_selected = selected(not masquerade or masquerade.role != "student")
specific_student_selected = selected(not staff_selected and masquerade.user_name)
student_selected = selected(not staff_selected and not specific_student_selected and not masquerade_group_id)
@@ -39,7 +40,7 @@ show_preview_menu = course and staff_access and supports_preview_menu
% if course_partitions:
% for course_partition in course_partitions:
% for group in sorted(course_partition.groups, key=lambda group: group.name):
-
% endfor
diff --git a/openedx/features/content_type_gating/models.py b/openedx/features/content_type_gating/models.py
index b3e4482865..f776647365 100644
--- a/openedx/features/content_type_gating/models.py
+++ b/openedx/features/content_type_gating/models.py
@@ -10,6 +10,7 @@ from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
+from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_specific_student
from experiments.models import ExperimentData
from student.models import CourseEnrollment
@@ -92,15 +93,18 @@ class ContentTypeGatingConfig(StackedConfigurationModel):
# 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
+ no_masquerade = get_course_masquerade(user, course_key) is None
+ student_masquerade = is_masquerading_as_specific_student(user, course_key)
+ if user and (no_masquerade or student_masquerade):
+ 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)
diff --git a/openedx/features/content_type_gating/partitions.py b/openedx/features/content_type_gating/partitions.py
index 3d0383a199..bb192ca5b0 100644
--- a/openedx/features/content_type_gating/partitions.py
+++ b/openedx/features/content_type_gating/partitions.py
@@ -11,6 +11,7 @@ from course_modes.models import CourseMode
import crum
from django.apps import apps
+from django.conf import settings
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
@@ -21,7 +22,7 @@ from lms.djangoapps.courseware.masquerade import (
is_masquerading_as_specific_student,
get_masquerading_user_group,
)
-from xmodule.partitions.partitions import Group, UserPartition, UserPartitionError
+from xmodule.partitions.partitions import Group, UserPartition, UserPartitionError, ENROLLMENT_TRACK_PARTITION_ID
from openedx.core.lib.mobile_utils import is_request_from_mobile_app
from openedx.features.content_type_gating.models import ContentTypeGatingConfig
from student.roles import CourseBetaTesterRole
@@ -140,8 +141,18 @@ class ContentTypeGatingPartitionScheme(object):
# same logic as normal to return that student's group. If the current
# user is masquerading as a generic student in a specific group, then
# return that group.
- if get_course_masquerade(user, course_key) and not is_masquerading_as_specific_student(user, course_key):
- return get_masquerading_user_group(course_key, user, user_partition)
+ course_masquerade = get_course_masquerade(user, course_key)
+ if course_masquerade and not is_masquerading_as_specific_student(user, course_key):
+ masquerade_group = get_masquerading_user_group(course_key, user, user_partition)
+ if masquerade_group is not None:
+ return masquerade_group
+ else:
+ audit_mode_id = settings.COURSE_ENROLLMENT_MODES.get(CourseMode.AUDIT, {}).get('id')
+ if course_masquerade.user_partition_id == ENROLLMENT_TRACK_PARTITION_ID:
+ if course_masquerade.group_id != audit_mode_id:
+ return cls.FULL_ACCESS
+ else:
+ return cls.LIMITED_ACCESS
# For now, treat everyone as a Full-access user, until we have the rest of the
# feature gating logic in place.
diff --git a/openedx/features/content_type_gating/tests/test_access.py b/openedx/features/content_type_gating/tests/test_access.py
index e0a499237b..b618a38dfd 100644
--- a/openedx/features/content_type_gating/tests/test_access.py
+++ b/openedx/features/content_type_gating/tests/test_access.py
@@ -1,17 +1,20 @@
"""
Test audit user's access to various content based on content-gating features.
"""
-
-from datetime import datetime
+import json
+from datetime import datetime, timedelta
import ddt
from django.conf import settings
from django.test.client import RequestFactory
from django.test.utils import override_settings
from django.urls import reverse
+from django.utils import timezone
from mock import patch
from course_modes.tests.factories import CourseModeFactory
from experiments.models import ExperimentKeyValue
+from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID
+
from lms.djangoapps.courseware.module_render import load_single_xblock
from lms.djangoapps.courseware.tests.factories import (
InstructorFactory,
@@ -23,16 +26,14 @@ from lms.djangoapps.courseware.tests.factories import (
)
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.partitions import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
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.roles import CourseInstructorRole
from student.tests.factories import (
CourseEnrollmentFactory,
UserFactory,
@@ -201,7 +202,7 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase):
# enroll all users into the all track types course
self.users = {}
for mode_type in self.MODE_TYPES:
- self.users[mode_type] = UserFactory.create()
+ self.users[mode_type] = UserFactory.create(username=mode_type)
CourseEnrollmentFactory.create(
user=self.users[mode_type],
course_id=self.courses['all_track_types']['course'].id,
@@ -245,7 +246,8 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase):
....
}
"""
- course = CourseFactory.create(run=run, display_name=display_name)
+ start_date = timezone.now() - timedelta(weeks=1)
+ course = CourseFactory.create(run=run, display_name=display_name, start=start_date)
for mode in modes:
CourseModeFactory.create(course_id=course.id, mode_slug=mode)
@@ -465,6 +467,64 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase):
request_factory=self.factory,
)
+ @ddt.data(
+ ({'user_partition_id': CONTENT_GATING_PARTITION_ID,
+ 'group_id': CONTENT_TYPE_GATE_GROUP_IDS['limited_access']}, True),
+ ({'user_partition_id': CONTENT_GATING_PARTITION_ID,
+ 'group_id': CONTENT_TYPE_GATE_GROUP_IDS['full_access']}, False),
+ ({'user_partition_id': ENROLLMENT_TRACK_PARTITION_ID,
+ 'group_id': settings.COURSE_ENROLLMENT_MODES['audit']['id']}, True),
+ ({'user_partition_id': ENROLLMENT_TRACK_PARTITION_ID,
+ 'group_id': settings.COURSE_ENROLLMENT_MODES['verified']['id']}, False),
+ ({'role': 'staff'}, False),
+ ({'role': 'student'}, True),
+ ({'username': 'audit'}, True),
+ ({'username': 'verified'}, False),
+ )
+ @ddt.unpack
+ def test_masquerade(self, masquerade_config, is_gated):
+ instructor = UserFactory.create()
+ CourseEnrollmentFactory.create(
+ user=instructor,
+ course_id=self.course.id,
+ mode='audit'
+ )
+ CourseInstructorRole(self.course.id).add_users(instructor)
+ self.client.login(username=instructor.username, password=TEST_PASSWORD)
+
+ self.update_masquerade(**masquerade_config)
+
+ block = self.blocks_dict['problem']
+ block_view_url = reverse('render_xblock', kwargs={'usage_key_string': unicode(block.scope_ids.usage_id)})
+ response = self.client.get(block_view_url)
+ if is_gated:
+ self.assertEquals(response.status_code, 404)
+ else:
+ self.assertEquals(response.status_code, 200)
+
+ def update_masquerade(self, role='student', group_id=None, username=None, user_partition_id=None):
+ """
+ Toggle masquerade state.
+ """
+ masquerade_url = reverse(
+ 'masquerade_update',
+ kwargs={
+ 'course_key_string': unicode(self.course.id),
+ }
+ )
+ response = self.client.post(
+ masquerade_url,
+ json.dumps({
+ 'role': role,
+ 'group_id': group_id,
+ 'user_name': username,
+ 'user_partition_id': user_partition_id,
+ }),
+ 'application/json'
+ )
+ self.assertEqual(response.status_code, 200)
+ return response
+
@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 dc2c2e3e98..693ae87daf 100644
--- a/openedx/features/content_type_gating/tests/test_models.py
+++ b/openedx/features/content_type_gating/tests/test_models.py
@@ -70,9 +70,12 @@ class TestContentTypeGatingConfig(CacheIsolationTestCase):
user = self.user
course_key = self.course_overview.id
- query_count = 5
- if not pass_enrollment and already_enrolled:
+ if already_enrolled and pass_enrollment:
+ query_count = 4
+ elif not pass_enrollment and already_enrolled:
query_count = 6
+ else:
+ query_count = 5
with self.assertNumQueries(query_count):
enabled = ContentTypeGatingConfig.enabled_for_enrollment(
diff --git a/openedx/features/course_duration_limits/access.py b/openedx/features/course_duration_limits/access.py
index 2232a7806d..a00e46062c 100644
--- a/openedx/features/course_duration_limits/access.py
+++ b/openedx/features/course_duration_limits/access.py
@@ -9,11 +9,14 @@ from django.apps import apps
from django.utils import timezone
from django.utils.translation import ugettext as _
-from course_modes.models import CourseMode
from util.date_utils import DEFAULT_SHORT_DATE_FORMAT, strftime_localized
+
+from course_modes.models import CourseMode
+
from lms.djangoapps.courseware.access_response import AccessError
from lms.djangoapps.courseware.access_utils import ACCESS_GRANTED
from lms.djangoapps.courseware.date_summary import verified_upgrade_deadline_link
+from lms.djangoapps.courseware.masquerade import get_course_masquerade, is_masquerading_as_student
from openedx.core.djangoapps.catalog.utils import get_course_run_details
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
from openedx.core.djangoapps.util.user_messages import PageLevelMessages
@@ -93,6 +96,10 @@ def check_course_expired(user, course):
"""
Check if the course expired for the user.
"""
+ # masquerading course staff should always have access
+ if get_course_masquerade(user, course.id):
+ return ACCESS_GRANTED
+
if not CourseDurationLimitConfig.enabled_for_enrollment(user=user, course_key=course.id):
return ACCESS_GRANTED
@@ -107,15 +114,29 @@ def register_course_expired_message(request, course):
"""
Add a banner notifying the user of the user course expiration date if it exists.
"""
- if CourseDurationLimitConfig.enabled_for_enrollment(user=request.user, course_key=course.id):
- expiration_date = get_user_course_expiration_date(request.user, course)
- if expiration_date:
- upgrade_message = _('Your access to this course expires on {expiration_date}. \
- Upgrade now for unlimited access.')
- PageLevelMessages.register_info_message(
- request,
- HTML(upgrade_message).format(
- expiration_date=expiration_date.strftime('%b %-d'),
- upgrade_link=verified_upgrade_deadline_link(user=request.user, course=course)
- )
+ if not CourseDurationLimitConfig.enabled_for_enrollment(user=request.user, course_key=course.id):
+ return
+
+ expiration_date = get_user_course_expiration_date(request.user, course)
+ if not expiration_date:
+ return
+
+ if is_masquerading_as_student(request.user, course.id) and timezone.now() > expiration_date:
+ upgrade_message = _('This learner would not have access to this course. '
+ 'Their access expired on {expiration_date}.')
+ PageLevelMessages.register_warning_message(
+ request,
+ HTML(upgrade_message).format(
+ expiration_date=expiration_date.strftime('%b %-d')
)
+ )
+ else:
+ upgrade_message = _('Your access to this course expires on {expiration_date}. \
+ Upgrade now for unlimited access.')
+ PageLevelMessages.register_info_message(
+ request,
+ HTML(upgrade_message).format(
+ expiration_date=expiration_date.strftime('%b %-d'),
+ upgrade_link=verified_upgrade_deadline_link(user=request.user, course=course)
+ )
+ )
diff --git a/openedx/features/course_duration_limits/models.py b/openedx/features/course_duration_limits/models.py
index 76272ee8a5..ac476e8a13 100644
--- a/openedx/features/course_duration_limits/models.py
+++ b/openedx/features/course_duration_limits/models.py
@@ -5,14 +5,21 @@ Course Duration Limit Configuration Models
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
+from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
+from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID
+
+from course_modes.models import CourseMode
+from lms.djangoapps.courseware.masquerade import get_masquerade_role, get_course_masquerade, \
+ is_masquerading_as_specific_student
from experiments.models import ExperimentData
from openedx.core.djangoapps.config_model_utils.models import StackedConfigurationModel
+from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
from openedx.features.course_duration_limits.config import (
CONTENT_TYPE_GATING_FLAG,
EXPERIMENT_ID,
@@ -79,13 +86,25 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
# if the user is has a role of staff, instructor or beta tester their access should not expire
if user is None and enrollment is not None:
user = enrollment.user
- if user:
- staff_role = CourseStaffRole(course_key).has_user(user)
- instructor_role = CourseInstructorRole(course_key).has_user(user)
- beta_tester_role = CourseBetaTesterRole(course_key).has_user(user)
- if staff_role or instructor_role or beta_tester_role:
- return False
+ if user:
+ course_masquerade = get_course_masquerade(user, course_key)
+ if course_masquerade:
+ verified_mode_id = settings.COURSE_ENROLLMENT_MODES.get(CourseMode.VERIFIED, {}).get('id')
+ is_verified = (course_masquerade.user_partition_id == ENROLLMENT_TRACK_PARTITION_ID
+ and course_masquerade.group_id == verified_mode_id)
+ is_full_access = (course_masquerade.user_partition_id == CONTENT_GATING_PARTITION_ID
+ and course_masquerade.group_id == CONTENT_TYPE_GATE_GROUP_IDS['full_access'])
+ is_staff = get_masquerade_role(user, course_key) == 'staff'
+ if is_verified or is_full_access or is_staff:
+ return False
+ else:
+ staff_role = CourseStaffRole(course_key).has_user(user)
+ instructor_role = CourseInstructorRole(course_key).has_user(user)
+ beta_tester_role = CourseBetaTesterRole(course_key).has_user(user)
+
+ if staff_role or instructor_role or beta_tester_role:
+ return False
# enrollment might be None if the user isn't enrolled. In that case,
# return enablement as if the user enrolled today
@@ -95,15 +114,18 @@ class CourseDurationLimitConfig(StackedConfigurationModel):
# 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
+ no_masquerade = get_course_masquerade(user, course_key) is None
+ student_masquerade = is_masquerading_as_specific_student(user, course_key)
+ if user and (no_masquerade or student_masquerade):
+ 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)
diff --git a/openedx/features/course_duration_limits/tests/test_course_expiration.py b/openedx/features/course_duration_limits/tests/test_course_expiration.py
index 799311abad..a0be81ca01 100644
--- a/openedx/features/course_duration_limits/tests/test_course_expiration.py
+++ b/openedx/features/course_duration_limits/tests/test_course_expiration.py
@@ -1,17 +1,27 @@
"""
Contains tests to verify correctness of course expiration functionality
"""
+import json
from datetime import timedelta
+
+from django.conf import settings
+from django.urls import reverse
from django.utils.timezone import now
import ddt
import mock
-
from course_modes.models import CourseMode
+from experiments.models import ExperimentData
+from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
+from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS
from openedx.features.course_duration_limits.access import get_user_course_expiration_date, MIN_DURATION, MAX_DURATION
+from openedx.features.course_duration_limits.config import EXPERIMENT_ID, EXPERIMENT_DATA_HOLDBACK_KEY
from openedx.features.course_experience.tests.views.helpers import add_course_mode
+from openedx.features.course_duration_limits.models import CourseDurationLimitConfig
from student.models import CourseEnrollment
-from student.tests.factories import UserFactory
+from student.roles import CourseInstructorRole
+from student.tests.factories import UserFactory, CourseEnrollmentFactory
+from xmodule.partitions.partitions import ENROLLMENT_TRACK_PARTITION_ID
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
@@ -101,3 +111,158 @@ class CourseExpirationTestCase(ModuleStoreTestCase):
result = get_user_course_expiration_date(self.user, future_course)
content_availability_date = start_date
self.assertEqual(result, content_availability_date + access_duration)
+
+ @mock.patch("openedx.features.course_duration_limits.access.get_course_run_details")
+ @ddt.data(
+ ({'user_partition_id': CONTENT_GATING_PARTITION_ID,
+ 'group_id': CONTENT_TYPE_GATE_GROUP_IDS['limited_access']}, True),
+ ({'user_partition_id': CONTENT_GATING_PARTITION_ID,
+ 'group_id': CONTENT_TYPE_GATE_GROUP_IDS['full_access']}, False),
+ ({'user_partition_id': ENROLLMENT_TRACK_PARTITION_ID,
+ 'group_id': settings.COURSE_ENROLLMENT_MODES['audit']['id']}, True),
+ ({'user_partition_id': ENROLLMENT_TRACK_PARTITION_ID,
+ 'group_id': settings.COURSE_ENROLLMENT_MODES['verified']['id']}, False),
+ ({'role': 'staff'}, False),
+ ({'role': 'student'}, True),
+ ({'username': 'audit'}, True),
+ ({'username': 'verified'}, False),
+ )
+ @ddt.unpack
+ def test_masquerade(self, masquerade_config, show_expiration_banner, mock_get_course_run_details):
+ mock_get_course_run_details.return_value = {'weeks_to_complete': 12}
+ audit_student = UserFactory(username='audit')
+ CourseEnrollmentFactory.create(
+ user=audit_student,
+ course_id=self.course.id,
+ mode='audit'
+ )
+ verified_student = UserFactory(username='verified')
+ CourseEnrollmentFactory.create(
+ user=verified_student,
+ course_id=self.course.id,
+ mode='verified'
+ )
+ CourseDurationLimitConfig.objects.create(
+ enabled=True,
+ course=CourseOverview.get_from_id(self.course.id),
+ enabled_as_of=self.course.start,
+ )
+
+ instructor = UserFactory.create(username='instructor')
+ CourseEnrollmentFactory.create(
+ user=instructor,
+ course_id=self.course.id,
+ mode='audit'
+ )
+ CourseInstructorRole(self.course.id).add_users(instructor)
+ self.client.login(username=instructor.username, password='test')
+
+ self.update_masquerade(**masquerade_config)
+
+ course_home_url = reverse('openedx.course_experience.course_home', args=[unicode(self.course.id)])
+ response = self.client.get(course_home_url, follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertItemsEqual(response.redirect_chain, [])
+ banner_text = 'Your access to this course expires on'
+ if show_expiration_banner:
+ self.assertIn(banner_text, response.content)
+ else:
+ self.assertNotIn(banner_text, response.content)
+
+ def update_masquerade(self, role='student', group_id=None, username=None, user_partition_id=None):
+ """
+ Toggle masquerade state.
+ """
+ masquerade_url = reverse(
+ 'masquerade_update',
+ kwargs={
+ 'course_key_string': unicode(self.course.id),
+ }
+ )
+ response = self.client.post(
+ masquerade_url,
+ json.dumps({
+ 'role': role,
+ 'group_id': group_id,
+ 'user_name': username,
+ 'user_partition_id': user_partition_id,
+ }),
+ 'application/json'
+ )
+ self.assertEqual(response.status_code, 200)
+ return response
+
+ @mock.patch("openedx.features.course_duration_limits.access.get_course_run_details")
+ def test_masquerade_in_holdback(self, mock_get_course_run_details):
+ mock_get_course_run_details.return_value = {'weeks_to_complete': 12}
+ audit_student = UserFactory(username='audit')
+ CourseEnrollmentFactory.create(
+ user=audit_student,
+ course_id=self.course.id,
+ mode='audit'
+ )
+ ExperimentData.objects.create(
+ user=audit_student,
+ experiment_id=EXPERIMENT_ID,
+ key=EXPERIMENT_DATA_HOLDBACK_KEY.format(audit_student),
+ value='True'
+ )
+ CourseDurationLimitConfig.objects.create(
+ enabled=True,
+ course=CourseOverview.get_from_id(self.course.id),
+ enabled_as_of=self.course.start,
+ )
+
+ instructor = UserFactory.create(username='instructor')
+ CourseEnrollmentFactory.create(
+ user=instructor,
+ course_id=self.course.id,
+ mode='audit'
+ )
+ CourseInstructorRole(self.course.id).add_users(instructor)
+ self.client.login(username=instructor.username, password='test')
+
+ self.update_masquerade(username='audit')
+
+ course_home_url = reverse('openedx.course_experience.course_home', args=[unicode(self.course.id)])
+ response = self.client.get(course_home_url, follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertItemsEqual(response.redirect_chain, [])
+ banner_text = 'Your access to this course expires on'
+ self.assertNotIn(banner_text, response.content)
+
+ @mock.patch("openedx.features.course_duration_limits.access.get_course_run_details")
+ def test_masquerade_expired(self, mock_get_course_run_details):
+ mock_get_course_run_details.return_value = {'weeks_to_complete': 1}
+
+ audit_student = UserFactory(username='audit')
+ enrollment = CourseEnrollmentFactory.create(
+ user=audit_student,
+ course_id=self.course.id,
+ mode='audit',
+ )
+ enrollment.created = self.course.start
+ enrollment.save()
+ CourseDurationLimitConfig.objects.create(
+ enabled=True,
+ course=CourseOverview.get_from_id(self.course.id),
+ enabled_as_of=self.course.start,
+ )
+
+ instructor = UserFactory.create(username='instructor')
+ CourseEnrollmentFactory.create(
+ user=instructor,
+ course_id=self.course.id,
+ mode='audit'
+ )
+ CourseInstructorRole(self.course.id).add_users(instructor)
+ self.client.login(username=instructor.username, password='test')
+
+ self.update_masquerade(username='audit')
+
+ course_home_url = reverse('openedx.course_experience.course_home', args=[unicode(self.course.id)])
+ response = self.client.get(course_home_url, follow=True)
+ self.assertEqual(response.status_code, 200)
+ self.assertItemsEqual(response.redirect_chain, [])
+ banner_text = 'This learner would not have access to this course. Their access expired on'
+ self.assertIn(banner_text, response.content)