From 8b014e1fef136ffdec0ba25bd9125ea4da115222 Mon Sep 17 00:00:00 2001 From: Calen Pennington Date: Fri, 4 Jan 2019 15:24:55 -0500 Subject: [PATCH] Add a test of the FBE Upgrade banner REV-656 --- common/djangoapps/student/models.py | 6 +- lms/djangoapps/courseware/tests/helpers.py | 3 +- .../features/course_duration_limits/access.py | 4 +- .../tests/test_access.py | 106 ++++++++++++++++++ 4 files changed, 111 insertions(+), 8 deletions(-) create mode 100644 openedx/features/course_duration_limits/tests/test_access.py diff --git a/common/djangoapps/student/models.py b/common/djangoapps/student/models.py index 9c5387a2c8..14e6e44b29 100644 --- a/common/djangoapps/student/models.py +++ b/common/djangoapps/student/models.py @@ -1728,7 +1728,7 @@ class CourseEnrollment(models.Model): if self.dynamic_upgrade_deadline is not None: # When course modes expire they aren't found any more and None would be returned. # Replicate that behavior here by returning None if the personalized deadline is in the past. - if datetime.now(UTC) >= self.dynamic_upgrade_deadline: + if self.dynamic_upgrade_deadline <= datetime.now(UTC): log.debug('Schedules: Returning None since dynamic upgrade deadline has already passed.') return None @@ -1775,15 +1775,13 @@ class CourseEnrollment(models.Model): 'Schedules: Pulling upgrade deadline for CourseEnrollment %d from Schedule %d.', self.id, self.schedule.id ) - upgrade_deadline = self.schedule.upgrade_deadline + return self.schedule.upgrade_deadline except ObjectDoesNotExist: # NOTE: Schedule has a one-to-one mapping with CourseEnrollment. If no schedule is associated # with this enrollment, Django will raise an exception rather than return None. log.debug('Schedules: No schedule exists for CourseEnrollment %d.', self.id) return None - return upgrade_deadline - @cached_property def course_upgrade_deadline(self): """ diff --git a/lms/djangoapps/courseware/tests/helpers.py b/lms/djangoapps/courseware/tests/helpers.py index a9de6f056a..e8472a01a5 100644 --- a/lms/djangoapps/courseware/tests/helpers.py +++ b/lms/djangoapps/courseware/tests/helpers.py @@ -355,7 +355,7 @@ def _create_mock_json_request(user, data, method='POST'): return request -def get_expiration_banner_text(user, course): +def get_expiration_banner_text(user, course, language='en-us'): """ Get text for banner that messages user course expiration date for different tests that depend on it. @@ -367,7 +367,6 @@ def get_expiration_banner_text(user, course): if upgrade_deadline is None or now() < upgrade_deadline: upgrade_deadline = enrollment.course_upgrade_deadline - language = get_language() 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() diff --git a/openedx/features/course_duration_limits/access.py b/openedx/features/course_duration_limits/access.py index 49e8020351..64a9e65108 100644 --- a/openedx/features/course_duration_limits/access.py +++ b/openedx/features/course_duration_limits/access.py @@ -70,7 +70,7 @@ def get_user_course_expiration_date(user, course): return None enrollment = CourseEnrollment.get_enrollment(user, course.id) - if enrollment is None or enrollment.mode != 'audit': + if enrollment is None or enrollment.mode != CourseMode.AUDIT: return None try: @@ -140,7 +140,7 @@ def register_course_expired_message(request, course): upgrade_deadline = enrollment.upgrade_deadline now = timezone.now() course_upgrade_deadline = enrollment.course_upgrade_deadline - if (not upgrade_deadline) or (now > upgrade_deadline): + if (not upgrade_deadline) or (upgrade_deadline < now): upgrade_deadline = course_upgrade_deadline expiration_message = _('{strong_open}Audit Access Expires {expiration_date}{strong_close}' diff --git a/openedx/features/course_duration_limits/tests/test_access.py b/openedx/features/course_duration_limits/tests/test_access.py new file mode 100644 index 0000000000..a57833ce71 --- /dev/null +++ b/openedx/features/course_duration_limits/tests/test_access.py @@ -0,0 +1,106 @@ +"""Tests of openedx.features.course_duration_limits.access""" + +from datetime import datetime, timedelta +import itertools + +from course_modes.models import CourseMode +from course_modes.tests.factories import CourseModeFactory +from django.test import RequestFactory +from django.utils import timezone +from courseware.models import DynamicUpgradeDeadlineConfiguration +from mock import patch +from openedx.core.djangoapps.schedules.tests.factories import ScheduleFactory +from openedx.core.djangolib.testing.utils import CacheIsolationTestCase +from openedx.features.course_duration_limits.access import ( + register_course_expired_message, + get_user_course_expiration_date, +) +from openedx.features.course_duration_limits.models import CourseDurationLimitConfig +from pytz import UTC +from student.tests.factories import CourseEnrollmentFactory +from util.date_utils import strftime_localized +import ddt + + +@ddt.ddt +class TestAccess(CacheIsolationTestCase): + """Tests of openedx.features.course_duration_limits.access""" + def setUp(self): + super(TestAccess, self).setUp() + + CourseDurationLimitConfig.objects.create(enabled=True, enabled_as_of=datetime(2018, 1, 1, tzinfo=UTC)) + DynamicUpgradeDeadlineConfiguration.objects.create(enabled=True) + + @patch('openedx.features.course_duration_limits.access.PageLevelMessages') + @ddt.data( + *itertools.product( + ['en-us', 'es-419'], + itertools.product([None, -2, -1, 1, 2], repeat=2), + ) + ) + @ddt.unpack + def test_register_course_expired_message(self, language, offsets, mock_messages): + now = timezone.now() + schedule_offset, course_offset = offsets + + if schedule_offset is not None: + schedule_upgrade_deadline = now + timedelta(days=schedule_offset) + else: + schedule_upgrade_deadline = None + + if course_offset is not None: + course_upgrade_deadline = now + timedelta(days=course_offset) + else: + course_upgrade_deadline = None + + def format_date(date): + if language.startswith('es-'): + return strftime_localized(date, '%-d de %b. de %Y').lower() + else: + return strftime_localized(date, '%b. %-d, %Y') + + patch_lang = patch('openedx.features.course_duration_limits.access.get_language', return_value=language) + with patch_lang: + enrollment = CourseEnrollmentFactory.create( + course__start=datetime(2018, 1, 1, tzinfo=UTC), + course__self_paced=True, + ) + CourseModeFactory.create( + course_id=enrollment.course.id, + mode_slug=CourseMode.VERIFIED, + expiration_datetime=course_upgrade_deadline, + ) + CourseModeFactory.create( + course_id=enrollment.course.id, + mode_slug=CourseMode.AUDIT, + ) + ScheduleFactory.create( + enrollment=enrollment, + upgrade_deadline=schedule_upgrade_deadline, + ) + request = RequestFactory().get('/courseware') + request.user = enrollment.user + + duration_limit_upgrade_deadline = get_user_course_expiration_date(enrollment.user, enrollment.course) + self.assertIsNotNone(duration_limit_upgrade_deadline) + + register_course_expired_message(request, enrollment.course) + self.assertEqual( + mock_messages.register_info_message.call_count, + 1 + ) + + message = str(mock_messages.register_info_message.call_args[0][1]) + + self.assertIn(format_date(duration_limit_upgrade_deadline), message) + + soft_upgradeable = schedule_upgrade_deadline is not None and now < schedule_upgrade_deadline + upgradeable = course_upgrade_deadline is None or now < course_upgrade_deadline + has_upgrade_deadline = course_upgrade_deadline is not None + + if upgradeable and soft_upgradeable: + self.assertIn(format_date(schedule_upgrade_deadline), message) + elif upgradeable and has_upgrade_deadline: + self.assertIn(format_date(course_upgrade_deadline), message) + else: + self.assertNotIn("Upgrade by", message)