From e0b5e5cb977bd90d2eacc4bec005dd265337be90 Mon Sep 17 00:00:00 2001 From: Matthew Piatetsky Date: Thu, 20 Dec 2018 23:49:54 -0500 Subject: [PATCH] write tests for when verified mode expires --- lms/djangoapps/courseware/tests/helpers.py | 39 +++++++----- .../content_type_gating/tests/test_access.py | 48 +++++++++++++- .../features/course_duration_limits/access.py | 62 ++++++++++++------- .../tests/test_course_expiration.py | 16 +++++ .../tests/views/test_course_home.py | 23 ++++++- 5 files changed, 145 insertions(+), 43 deletions(-) diff --git a/lms/djangoapps/courseware/tests/helpers.py b/lms/djangoapps/courseware/tests/helpers.py index 3cf2d6609f..a9de6f056a 100644 --- a/lms/djangoapps/courseware/tests/helpers.py +++ b/lms/djangoapps/courseware/tests/helpers.py @@ -364,26 +364,35 @@ def get_expiration_banner_text(user, course): upgrade_link = verified_upgrade_deadline_link(user=user, course=course) enrollment = CourseEnrollment.get_enrollment(user, course.id) upgrade_deadline = enrollment.upgrade_deadline - if upgrade_deadline is None: - return - if now() < upgrade_deadline: + if upgrade_deadline is None or now() < upgrade_deadline: upgrade_deadline = enrollment.course_upgrade_deadline language = get_language() - if language and language.split('-')[0].lower() == 'es': + language_is_es = language and language.split('-')[0].lower() == 'es' + if language_is_es: formatted_expiration_date = strftime_localized(expiration_date, '%-d de %b. de %Y').lower() - formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%-d de %b. de %Y').lower() else: formatted_expiration_date = strftime_localized(expiration_date, '%b. %-d, %Y') - formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%b. %-d, %Y') - bannerText = 'Audit Access Expires {expiration_date}
\ - You lose all access to this course, including your progress, on {expiration_date}.
\ - Upgrade by {upgrade_deadline} to get unlimited access to the course as long as it exists on the site.\ - Upgrade now to retain access past {expiration_date}\ - '.format( - expiration_date=formatted_expiration_date, - upgrade_link=upgrade_link, - upgrade_deadline=formatted_upgrade_deadline - ) + if upgrade_deadline: + if language_is_es: + formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%-d de %b. de %Y').lower() + else: + formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%b. %-d, %Y') + + bannerText = 'Audit Access Expires {expiration_date}
\ + You lose all access to this course, including your progress, on {expiration_date}.\ +
Upgrade by {upgrade_deadline} to get unlimited access to the course as long as it exists\ + on the site. Upgrade now to retain access past\ + {expiration_date}'.format( + expiration_date=formatted_expiration_date, + upgrade_link=upgrade_link, + upgrade_deadline=formatted_upgrade_deadline + ) + else: + bannerText = 'Audit Access Expires {expiration_date}
\ + You lose all access to this course, including your progress, on {expiration_date}.\ + '.format( + expiration_date=formatted_expiration_date + ) return bannerText diff --git a/openedx/features/content_type_gating/tests/test_access.py b/openedx/features/content_type_gating/tests/test_access.py index 5012b0f1f4..166f94b006 100644 --- a/openedx/features/content_type_gating/tests/test_access.py +++ b/openedx/features/content_type_gating/tests/test_access.py @@ -35,7 +35,11 @@ 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.lib.url_utils import quote_slashes -from openedx.features.content_type_gating.partitions import CONTENT_GATING_PARTITION_ID, CONTENT_TYPE_GATE_GROUP_IDS +from openedx.features.content_type_gating.partitions import ( + CONTENT_GATING_PARTITION_ID, + CONTENT_TYPE_GATE_GROUP_IDS, + ContentTypeGatingPartition +) from openedx.features.content_type_gating.models import ContentTypeGatingConfig from openedx.features.course_duration_limits.config import ( EXPERIMENT_ID, @@ -89,7 +93,7 @@ def _get_fragment_from_block(block, user_id, course, request_factory, mock_get_c return frag -def _assert_block_is_gated(block, is_gated, user_id, course, request_factory): +def _assert_block_is_gated(block, is_gated, user_id, course, request_factory, has_upgrade_link=True): """ Asserts that a block in a specific course is gated for a specific user Arguments: @@ -98,9 +102,15 @@ def _assert_block_is_gated(block, is_gated, user_id, course, request_factory): user_id (int): id of user course_id (CourseLocator): id of course """ - frag = _get_fragment_from_block(block, user_id, course, request_factory) + checkout_link = '#' if has_upgrade_link else None + with patch.object(ContentTypeGatingPartition, '_get_checkout_link', return_value=checkout_link): + frag = _get_fragment_from_block(block, user_id, course, request_factory) if is_gated: assert 'content-paywall' in frag.content + if has_upgrade_link: + assert 'certA_1' in frag.content + else: + assert 'certA_1' not in frag.content else: assert 'content-paywall' not in frag.content @@ -236,6 +246,18 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase): component_types=['problem', 'html'] ) + cls.courses['expired_upgrade_deadline'] = cls._create_course( + run='expired_upgrade_deadline_run_1', + display_name='Expired Upgrade Deadline Course Title', + modes=['audit'], + component_types=['problem', 'html'] + ) + CourseModeFactory.create( + course_id=cls.courses['expired_upgrade_deadline']['course'].scope_ids.usage_id.course_key, + mode_slug='verified', + expiration_datetime=datetime(2018, 1, 1) + ) + def setUp(self): super(TestProblemTypeAccess, self).setUp() @@ -266,6 +288,12 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase): course_id=self.courses['audit_only']['course'].id, mode='audit' ) + # enroll audit user into the upgrade expired course + CourseEnrollmentFactory.create( + user=self.audit_user, + course_id=self.courses['expired_upgrade_deadline']['course'].id, + mode='audit' + ) ContentTypeGatingConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1)) @classmethod @@ -406,6 +434,20 @@ class TestProblemTypeAccess(SharedModuleStoreTestCase): request_factory=self.factory, ) + def test_access_expired_upgrade_deadline(self): + """ + If a user is enrolled as an audit user and the upgrade deadline has passed + the user will continue to see gated content, but the upgrade messaging will be removed. + """ + _assert_block_is_gated( + block=self.courses['default']['blocks']['problem'], + user_id=self.users['audit'].id, + course=self.courses['default']['course'], + is_gated=True, + request_factory=self.factory, + has_upgrade_link=False + ) + @ddt.data( ('problem', 'graded_problem', 'audit', 404), ('problem', 'graded_problem', 'verified', 200), diff --git a/openedx/features/course_duration_limits/access.py b/openedx/features/course_duration_limits/access.py index be08bf56eb..167ab24c66 100644 --- a/openedx/features/course_duration_limits/access.py +++ b/openedx/features/course_duration_limits/access.py @@ -66,7 +66,7 @@ def get_user_course_expiration_date(user, course): access_duration = MIN_DURATION - if not CourseMode.verified_mode_for_course(course.id): + if not CourseMode.verified_mode_for_course(course.id, include_expired=True): return None enrollment = CourseEnrollment.get_enrollment(user, course.id) @@ -138,11 +138,9 @@ def register_course_expired_message(request, course): return upgrade_deadline = enrollment.upgrade_deadline - if upgrade_deadline is None: - return now = timezone.now() course_upgrade_deadline = enrollment.course_upgrade_deadline - if now > upgrade_deadline: + if upgrade_deadline and now > upgrade_deadline: upgrade_deadline = course_upgrade_deadline expiration_message = _('{strong_open}Audit Access Expires {expiration_date}{strong_close}' @@ -152,31 +150,47 @@ def register_course_expired_message(request, course): 'as long as it exists on the site. {a_open}Upgrade now{sronly_span_open} to ' 'retain access past {expiration_date}{span_close}{a_close}') full_message = expiration_message - if now < course_upgrade_deadline: + if course_upgrade_deadline and now < course_upgrade_deadline: full_message += upgrade_deadline_message language = get_language() - if language and language.split('-')[0].lower() == 'es': + language_is_es = language and language.split('-')[0].lower() == 'es' + if language_is_es: formatted_expiration_date = strftime_localized(expiration_date, '%-d de %b. de %Y').lower() - formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%-d de %b. de %Y').lower() else: formatted_expiration_date = strftime_localized(expiration_date, '%b. %-d, %Y') - formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%b. %-d, %Y') - PageLevelMessages.register_info_message( - request, - Text(full_message).format( - a_open=HTML('').format( - upgrade_link=verified_upgrade_deadline_link(user=request.user, course=course) - ), - sronly_span_open=HTML(''), - sighted_only_span_open=HTML(''), - a_close=HTML(''), - expiration_date=formatted_expiration_date, - strong_open=HTML(''), - strong_close=HTML(''), - line_break=HTML('
'), - upgrade_deadline=formatted_upgrade_deadline + if upgrade_deadline: + if language_is_es: + formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%-d de %b. de %Y').lower() + else: + formatted_upgrade_deadline = strftime_localized(upgrade_deadline, '%b. %-d, %Y') + + if upgrade_deadline: + PageLevelMessages.register_info_message( + request, + Text(full_message).format( + a_open=HTML('').format( + upgrade_link=verified_upgrade_deadline_link(user=request.user, course=course) + ), + sronly_span_open=HTML(''), + span_close=HTML(''), + a_close=HTML(''), + expiration_date=formatted_expiration_date, + strong_open=HTML(''), + strong_close=HTML(''), + line_break=HTML('
'), + upgrade_deadline=formatted_upgrade_deadline + ) + ) + else: + PageLevelMessages.register_info_message( + request, + Text(full_message).format( + span_close=HTML(''), + expiration_date=formatted_expiration_date, + strong_open=HTML(''), + strong_close=HTML(''), + line_break=HTML('
'), + ) ) - ) 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 c32227b0e5..77a3a79002 100644 --- a/openedx/features/course_duration_limits/tests/test_course_expiration.py +++ b/openedx/features/course_duration_limits/tests/test_course_expiration.py @@ -129,6 +129,22 @@ class CourseExpirationTestCase(ModuleStoreTestCase): 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") + def test_expired_upgrade_deadline(self, mock_get_course_run_details): + """ + The expiration date still exists if the upgrade deadline has passed + """ + access_duration = timedelta(weeks=7) + mock_get_course_run_details.return_value = {'weeks_to_complete': 7} + + start_date = now() - timedelta(weeks=10) + course = CourseFactory(start=start_date) + enrollment = CourseEnrollment.enroll(self.user, course.id, CourseMode.AUDIT) + add_course_mode(course, upgrade_deadline_expired=True) + result = get_user_course_expiration_date(self.user, course) + content_availability_date = enrollment.created + 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, 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 74bada9282..c17cb36311 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -204,7 +204,7 @@ class TestCourseHomePage(CourseHomePageTestCase): # Fetch the view and verify the query counts # TODO: decrease query count as part of REVO-28 - with self.assertNumQueries(86, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): + with self.assertNumQueries(87, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with check_mongo_calls(4): url = course_home_url(self.course) self.client.get(url) @@ -538,6 +538,27 @@ class TestCourseHomePageAccess(CourseHomePageTestCase): ) self.assertRedirects(response, expected_url) + @mock.patch.dict(settings.FEATURES, {'DISABLE_START_DATES': False}) + def test_expiration_banner_with_expired_upgrade_deadline(self): + """ + Ensure that a user accessing a course with an expired upgrade deadline + will still see the course expiration banner without the upgrade related text. + """ + past = datetime(2010, 1, 1) + CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=past) + course = CourseFactory.create(start=now() - timedelta(days=10)) + CourseModeFactory.create(course_id=course.id, mode_slug=CourseMode.AUDIT) + CourseModeFactory.create(course_id=course.id, mode_slug=CourseMode.VERIFIED, expiration_datetime=past) + user = UserFactory(password=self.TEST_PASSWORD) + self.client.login(username=user.username, password=self.TEST_PASSWORD) + CourseEnrollment.enroll(user, course.id, mode=CourseMode.AUDIT) + + url = course_home_url(course) + response = self.client.get(url) + bannerText = get_expiration_banner_text(user, course) + self.assertContains(response, bannerText, html=True) + self.assertContains(response, TEST_BANNER_CLASS) + def test_audit_only_not_expired(self): """ Verify that enrolled users are NOT shown the course expiration banner and can