def test_is_entitlement_regainable(self):
        """
        Test that the entitlement is not expired when created now, and is expired when created20 days
        ago with a policy that sets the expiration period to 14 days
        """
        entitlement = CourseEntitlementFactory.create(enrollment_course_run=self.enrollment)
        assert entitlement.is_entitlement_regainable() is True

        # Create and associate a GeneratedCertificate for a user and course and make sure it isn't regainable
        GeneratedCertificateFactory(
            user=entitlement.user,
            course_id=entitlement.enrollment_course_run.course_id,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        assert entitlement.is_entitlement_regainable() is False

        # Create a date 20 days in the past (greater than the policy expire period of 14 days)
        # and apply it to both the entitlement and the course
        past_datetime = now() - timedelta(days=20)
        entitlement = CourseEntitlementFactory.create(enrollment_course_run=self.enrollment, created=past_datetime)
        self.enrollment.created = past_datetime
        self.course.start = past_datetime

        self.course.save()
        self.enrollment.save()

        assert entitlement.is_entitlement_regainable() is False

        entitlement = CourseEntitlementFactory.create(expired_at=now())

        assert entitlement.is_entitlement_regainable
    def test_unfulfilled_entitlement(self, mock_course_overview, mock_pseudo_session,
                                     mock_course_runs, mock_get_programs):
        """
        When a learner has an unfulfilled entitlement, their course dashboard should have:
            - a hidden 'View Course' button
            - the text 'In order to view the course you must select a session:'
            - an unhidden course-entitlement-selection-container
            - a related programs message
        """
        program = ProgramFactory()
        CourseEntitlementFactory.create(user=self.user, course_uuid=program['courses'][0]['uuid'])
        mock_get_programs.return_value = [program]
        course_key = CourseKey.from_string('course-v1:FAKE+FA1-MA1.X+3T2017')
        mock_course_overview.return_value = CourseOverviewFactory.create(start=self.TOMORROW, id=course_key)
        mock_course_runs.return_value = [
            {
                'key': six.text_type(course_key),
                'enrollment_end': str(self.TOMORROW),
                'pacing_type': 'instructor_paced',
                'type': 'verified',
                'status': 'published'
            }
        ]
        mock_pseudo_session.return_value = {
            'key': six.text_type(course_key),
            'type': 'verified'
        }
        response = self.client.get(self.path)
        self.assertIn('class="course-target-link enter-course hidden"', response.content)
        self.assertIn('You must select a session to access the course.', response.content)
        self.assertIn('<div class="course-entitlement-selection-container ">', response.content)
        self.assertIn('Related Programs:', response.content)

        # If an entitlement has already been redeemed by the user for a course run, do not let the run be selectable
        enrollment = CourseEnrollmentFactory(
            user=self.user, course_id=six.text_type(mock_course_overview.return_value.id), mode=CourseMode.VERIFIED
        )
        CourseEntitlementFactory.create(
            user=self.user, course_uuid=program['courses'][0]['uuid'], enrollment_course_run=enrollment
        )

        mock_course_runs.return_value = [
            {
                'key': 'course-v1:edX+toy+2012_Fall',
                'enrollment_end': str(self.TOMORROW),
                'pacing_type': 'instructor_paced',
                'type': 'verified',
                'status': 'published'
            }
        ]
        response = self.client.get(self.path)
        # There should be two entitlements on the course page, one prompting for a mandatory session, but no
        # select option for the courses as there is only the single course run which has already been redeemed
        self.assertEqual(response.content.count('<li class="course-item">'), 2)
        self.assertIn('You must select a session to access the course.', response.content)
        self.assertNotIn('To access the course, select a session.', response.content)
Exemple #3
0
    def test_delete_and_revoke_entitlement(self):
        course_entitlement = CourseEntitlementFactory()
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(course_entitlement.uuid)])

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 204
        course_entitlement.refresh_from_db()
        assert course_entitlement.expired_at is not None
    def test_no_tasks_if_no_work(self, mock_task):
        """
        Verify that we never try to spin off a task if there are no database rows.
        """
        call_command('expire_old_entitlements', commit=True)
        self.assertEqual(mock_task.call_count, 0)

        # Now confirm that the above test wasn't a fluke and we will create a task if there is work
        CourseEntitlementFactory.create()
        call_command('expire_old_entitlements', commit=True)
        self.assertEqual(mock_task.call_count, 1)
    def test_no_commit(self, mock_task):
        """
        Verify that relevant tasks are only enqueued when the commit option is passed.
        """
        CourseEntitlementFactory.create()

        call_command('expire_old_entitlements')
        self.assertEqual(mock_task.call_count, 0)

        call_command('expire_old_entitlements', commit=True)
        self.assertEqual(mock_task.call_count, 1)
Exemple #6
0
    def test_staff_get_only_staff_entitlements(self):
        CourseEntitlementFactory.create_batch(2)
        entitlement = CourseEntitlementFactory.create(user=self.user)

        response = self.client.get(
            self.entitlements_list_url,
            content_type='application/json',
        )
        assert response.status_code == 200

        results = response.data.get('results', [])
        assert results == CourseEntitlementSerializer([entitlement], many=True).data
Exemple #7
0
    def test_non_staff_get_select_entitlements(self):
        not_staff_user = UserFactory()
        self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)
        CourseEntitlementFactory.create_batch(2)
        entitlement = CourseEntitlementFactory.create(user=not_staff_user)
        response = self.client.get(
            self.entitlements_list_url,
            content_type='application/json',
        )
        assert response.status_code == 200

        results = response.data.get('results', [])  # pylint: disable=no-member
        assert results == CourseEntitlementSerializer([entitlement], many=True).data
Exemple #8
0
    def test_get_user_entitlements(self):
        user2 = UserFactory()
        CourseEntitlementFactory.create()
        entitlement_user2 = CourseEntitlementFactory.create(user=user2)
        url = reverse('entitlements_api:v1:entitlements-list')
        url += '?user={username}'.format(username=user2.username)
        response = self.client.get(
            url,
            content_type='application/json',
        )
        assert response.status_code == 200

        results = response.data.get('results', [])
        assert results == CourseEntitlementSerializer([entitlement_user2], many=True).data
Exemple #9
0
    def test_get_entitlement_by_uuid(self):
        entitlement = CourseEntitlementFactory.create()
        CourseEntitlementFactory.create_batch(2)

        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(entitlement.uuid)])

        response = self.client.get(
            url,
            content_type='application/json',
        )
        assert response.status_code == 200

        results = response.data
        assert results == CourseEntitlementSerializer(entitlement).data and results.get('expired_at') is None
    def test_pagination(self, mock_task):
        """
        Verify that we chunk up our requests to celery.
        """
        for _ in range(5):
            CourseEntitlementFactory.create()

        call_command('expire_old_entitlements', commit=True, batch_size=2)

        args_list = mock_task.call_args_list
        self.assertEqual(len(args_list), 3)
        self.assertEqual(args_list[0][0], (1, 3))
        self.assertEqual(args_list[1][0], (3, 5))
        self.assertEqual(args_list[2][0], (5, 6))
Exemple #11
0
 def test_get_entitlements(self, search_string_type):
     CourseEntitlementFactory.create(mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid)
     url = self.url + getattr(self.student, search_string_type)
     response = self.client.get(url)
     self.assertEqual(response.status_code, 200)
     data = json.loads(response.content)
     self.assertEqual(len(data), 1)
     self.assertDictContainsSubset({
         'user': self.student.username,
         'course_uuid': unicode(self.course_uuid),
         'enrollment_course_run': None,
         'mode': CourseMode.VERIFIED,
         'support_details': []
     }, data[0])
    def test_unenroll_entitlement_with_audit_course_enrollment(self, mock_refund, mock_get_course_uuid):
        """
        Test that entitlement is not refunded if un-enroll is called on audit course un-enroll.
        """
        self.enrollment.mode = CourseMode.AUDIT
        self.enrollment.user = self.user
        self.enrollment.save()
        entitlement = CourseEntitlementFactory.create(user=self.user)
        mock_get_course_uuid.return_value = entitlement.course_uuid
        CourseEnrollment.unenroll(self.user, self.course.id)

        assert not mock_refund.called
        entitlement.refresh_from_db()
        assert entitlement.expired_at is None

        self.enrollment.mode = CourseMode.VERIFIED
        self.enrollment.is_active = True
        self.enrollment.save()
        entitlement.enrollment_course_run = self.enrollment
        entitlement.save()
        CourseEnrollment.unenroll(self.user, self.course.id)

        assert mock_refund.called
        entitlement.refresh_from_db()
        assert entitlement.expired_at < now()
    def test_course_run_missing_overview_not_fulfillable(self):
        entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)

        assert not is_course_run_entitlement_fulfillable(
            CourseKey.from_string('course-v1:edx+FakeCourse+3T2017'),
            entitlement
        )
Exemple #14
0
    def test_check_for_existing_entitlement_and_enroll(self, mock_get_course_uuid):
        course = CourseFactory()
        CourseModeFactory(
            course_id=course.id,
            mode_slug=CourseMode.VERIFIED,
            # This must be in the future to ensure it is returned by downstream code.
            expiration_datetime=now() + timedelta(days=1)
        )
        entitlement = CourseEntitlementFactory.create(
            mode=CourseMode.VERIFIED,
            user=self.user,
        )
        mock_get_course_uuid.return_value = entitlement.course_uuid

        assert not CourseEnrollment.is_enrolled(user=self.user, course_key=course.id)

        CourseEntitlement.check_for_existing_entitlement_and_enroll(
            user=self.user,
            course_run_key=course.id,
        )

        assert CourseEnrollment.is_enrolled(user=self.user, course_key=course.id)

        entitlement.refresh_from_db()
        assert entitlement.enrollment_course_run
Exemple #15
0
    def test_get_expired_entitlement_by_uuid(self):
        past_datetime = now() - timedelta(days=365 * 2)
        entitlement = CourseEntitlementFactory(created=past_datetime)
        CourseEntitlementFactory.create_batch(2)

        CourseEntitlementFactory()
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(entitlement.uuid)])

        response = self.client.get(
            url,
            content_type='application/json',
        )
        assert response.status_code == 200

        results = response.data  # pylint: disable=no-member
        assert results.get('expired_at')
    def test_reinstate_unrefundable_entitlement(self):
        """ Verify that a no longer refundable entitlement does not become refundable when support reinstates it. """
        enrollment = CourseEnrollmentFactory(user=self.user, is_active=True)
        expired_entitlement = CourseEntitlementFactory.create(
            user=self.user, enrollment_course_run=enrollment, expired_at=datetime.now()
        )
        assert expired_entitlement.is_entitlement_refundable() is False
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(expired_entitlement.uuid)])

        update_data = {
            'expired_at': None,
            'enrollment_course_run': None,
            'support_details': [
                {
                    'unenrolled_run': str(enrollment.course.id),
                    'action': CourseEntitlementSupportDetail.REISSUE,
                    'comments': 'Severe illness.'
                }
            ]
        }

        response = self.client.patch(
            url,
            data=json.dumps(update_data),
            content_type='application/json'
        )
        assert response.status_code == 200

        reinstated_entitlement = CourseEntitlement.objects.get(
            uuid=expired_entitlement.uuid
        )
        assert reinstated_entitlement.refund_locked is True
        assert reinstated_entitlement.is_entitlement_refundable() is False
Exemple #17
0
    def test_user_already_enrolled_in_unpaid_mode(self, mock_get_course_runs):
        course_entitlement = CourseEntitlementFactory.create(user=self.user, mode=CourseMode.VERIFIED)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(
            self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
            args=[str(course_entitlement.uuid)]
        )

        CourseEnrollment.enroll(self.user, self.course.id, mode=CourseMode.AUDIT)
        data = {
            'course_run_id': str(self.course.id)
        }
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)
        (enrolled_mode, is_active) = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id)
        assert is_active and (enrolled_mode == course_entitlement.mode)
        assert course_entitlement.enrollment_course_run is not None
Exemple #18
0
 def test_ecommerce_successful_refund(self):
     httpretty.register_uri(
         httpretty.POST,
         settings.ECOMMERCE_API_URL + 'refunds/',
         status=201,
         body='[1]',
         content_type='application/json'
     )
     httpretty.register_uri(
         httpretty.PUT,
         settings.ECOMMERCE_API_URL + 'refunds/1/process/',
         status=200,
         body=json.dumps({
             "id": 9,
             "created": "2017-12-21T18:23:49.468298Z",
             "modified": "2017-12-21T18:24:02.741426Z",
             "total_credit_excl_tax": "100.00",
             "currency": "USD",
             "status": "Complete",
             "order": 15,
             "user": 5
         }),
         content_type='application/json'
     )
     course_entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)
     refund_success = refund_entitlement(course_entitlement)
     assert refund_success
Exemple #19
0
    def test_ecommerce_refund_not_verified_notification_for_entitlement(self, mock_send_notification):
        """
        Note that we are currently notifying Support whenever a refund require approval for entitlements as
        Entitlements are only available in paid modes. This test should be updated if this logic changes
        in the future.

        PROFESSIONAL mode is used here although we never auto approve PROFESSIONAL refunds right now
        """
        httpretty.register_uri(
            httpretty.POST,
            settings.ECOMMERCE_API_URL + 'refunds/',
            status=201,
            body='[1]',
            content_type='application/json'
        )
        httpretty.register_uri(
            httpretty.PUT,
            settings.ECOMMERCE_API_URL + 'refunds/1/process/',
            status=400,
            body='{}',
            content_type='application/json'
        )
        course_entitlement = CourseEntitlementFactory.create(mode=CourseMode.PROFESSIONAL)
        refund_success = refund_entitlement(course_entitlement)
        assert mock_send_notification.is_called
        call_args = list(mock_send_notification.call_args)
        assert call_args[0] == (course_entitlement.user, [1])
        assert refund_success
Exemple #20
0
    def test_user_can_unenroll(self, mock_get_course_runs):
        course_entitlement = CourseEntitlementFactory.create(user=self.user, mode=CourseMode.VERIFIED)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(
            self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
            args=[str(course_entitlement.uuid)]
        )
        assert course_entitlement.enrollment_course_run is None

        data = {
            'course_run_id': str(self.course.id)
        }
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 204

        course_entitlement.refresh_from_db()
        assert not CourseEnrollment.is_enrolled(self.user, self.course.id)
        assert course_entitlement.enrollment_course_run is None
Exemple #21
0
    def test_reinstate_entitlement(self):
        enrollment = CourseEnrollmentFactory(user=self.user, is_active=True)
        expired_entitlement = CourseEntitlementFactory.create(
            user=self.user, enrollment_course_run=enrollment, expired_at=datetime.now()
        )
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(expired_entitlement.uuid)])

        update_data = {
            'expired_at': None,
            'enrollment_course_run': None,
            'support_details': [
                {
                    'unenrolled_run': str(enrollment.course.id),
                    'action': 'REISSUE',
                    'comments': 'Severe illness.'
                }
            ]
        }

        response = self.client.patch(
            url,
            data=json.dumps(update_data),
            content_type='application/json'
        )
        assert response.status_code == 200

        results = response.data
        reinstated_entitlement = CourseEntitlement.objects.get(
            uuid=expired_entitlement.uuid
        )
        assert results == CourseEntitlementSerializer(reinstated_entitlement).data
Exemple #22
0
 def test_get_days_until_expiration(self):
     """
     Test that the expiration period is always less than or equal to the policy expiration
     """
     entitlement = CourseEntitlementFactory.create(enrollment_course_run=self.enrollment)
     # This will always either be 1 less than the expiration_period_days because the get_days_until_expiration
     # method will have had at least some time pass between object creation in setUp and this method execution,
     # or the exact same as the original expiration_period_days if somehow no time has passed
     assert entitlement.get_days_until_expiration() <= entitlement.policy.expiration_period.days
Exemple #23
0
    def test_is_entitlement_redeemable(self):
        """
        Test that the entitlement is not expired when created now, and is expired when created 2 years
        ago with a policy that sets the expiration period to 450 days
        """

        entitlement = CourseEntitlementFactory.create()

        assert entitlement.is_entitlement_redeemable() is True

        # Create a date 2 years in the past (greater than the policy expire period of 450 days)
        past_datetime = now() - timedelta(days=365 * 2)
        entitlement.created = past_datetime
        entitlement.save()

        assert entitlement.is_entitlement_redeemable() is False

        entitlement = CourseEntitlementFactory.create(expired_at=now())

        assert entitlement.is_entitlement_refundable() is False
Exemple #24
0
    def test_course_run_not_fullfillable_enroll_period_ended(self):
        course_overview = self.create_course(
            start_from_now=-3,
            end_from_now=2,
            enrollment_start_from_now=-2,
            enrollment_end_from_now=-1
        )

        entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)

        assert not is_course_run_entitlement_fulfillable(course_overview.id, entitlement)
Exemple #25
0
 def test_no_ecommerce_connection_and_failure(self):
     httpretty.register_uri(
         httpretty.POST,
         settings.ECOMMERCE_API_URL + 'refunds/',
         status=404,
         body='{}',
         content_type='application/json'
     )
     course_entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)
     refund_success = refund_entitlement(course_entitlement)
     assert not refund_success
Exemple #26
0
    def test_staff_user_required_for_delete(self):
        not_staff_user = UserFactory()
        self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)

        course_entitlement = CourseEntitlementFactory.create()
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(course_entitlement.uuid)])

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 403
Exemple #27
0
    def test_is_entitlement_refundable(self):
        """
        Test that the entitlement is refundable when created now, and is not refundable when created 70 days
        ago with a policy that sets the expiration period to 60 days. Also test that if the entitlement is spent
        and greater than 14 days it is no longer refundable.
        """
        entitlement = CourseEntitlementFactory.create()
        assert entitlement.is_entitlement_refundable() is True

        # If there is no order_number make sure the entitlement is not refundable
        entitlement.order_number = None
        assert entitlement.is_entitlement_refundable() is False

        # Create a date 70 days in the past (greater than the policy refund expire period of 60 days)
        past_datetime = now() - timedelta(days=70)
        entitlement = CourseEntitlementFactory.create(created=past_datetime)

        assert entitlement.is_entitlement_refundable() is False

        entitlement = CourseEntitlementFactory.create(enrollment_course_run=self.enrollment)
        # Create a date 20 days in the past (less than the policy refund expire period of 60 days)
        # but more than the policy regain period of 14 days and also the course start
        past_datetime = now() - timedelta(days=20)
        entitlement.created = past_datetime
        self.enrollment.created = past_datetime
        self.course.start = past_datetime
        entitlement.save()
        self.course.save()
        self.enrollment.save()

        assert entitlement.is_entitlement_refundable() is False

        # Removing the entitlement being redeemed, make sure that the entitlement is refundable
        entitlement.enrollment_course_run = None

        assert entitlement.is_entitlement_refundable() is True

        entitlement = CourseEntitlementFactory.create(expired_at=now())

        assert entitlement.is_entitlement_refundable() is False
Exemple #28
0
    def test_course_run_fullfillable_user_enrolled(self):
        course_overview = self.create_course(
            start_from_now=-3,
            end_from_now=2,
            enrollment_start_from_now=-2,
            enrollment_end_from_now=1
        )

        entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)
        # Enroll User in the Course, but do not update the entitlement
        CourseEnrollmentFactory.create(user=entitlement.user, course_id=course_overview.id)

        assert is_course_run_entitlement_fulfillable(course_overview.id, entitlement)
    def test_course_run_not_fulfillable_no_start_date(self):
        course_overview = self.create_course(
            start_from_now=-2,
            end_from_now=2,
            enrollment_start_from_now=-1,
            enrollment_end_from_now=1
        )
        course_overview.start = None
        course_overview.save()

        entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)

        assert not is_course_run_entitlement_fulfillable(course_overview.id, entitlement)
Exemple #30
0
    def test_staff_user_required_for_delete(self):
        not_staff_user = UserFactory()
        self.client.login(username=not_staff_user.username,
                          password=TEST_PASSWORD)

        course_entitlement = CourseEntitlementFactory.create()
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH,
                      args=[str(course_entitlement.uuid)])

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 403
Exemple #31
0
 def test_create_entitlement(self):
     CourseEntitlementFactory.create(
         mode=CourseMode.VERIFIED, user=self.student, course_uuid=self.course_uuid, expired_at=datetime.now()
     )
     url = self.url + self.student.username
     response = self.client.post(
         url,
         data=json.dumps({
             'course_uuid': unicode(self.course_uuid),
             'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW,
             'mode': CourseMode.VERIFIED
         }),
         content_type='application/json',
     )
     self.assertEqual(response.status_code, 201)
     data = json.loads(response.content)
     self.assertEqual(len(data['support_details']), 1)
     self.assertDictContainsSubset({
         'support_user': self.user.username,
         'reason': CourseEntitlementSupportDetail.LEARNER_REQUEST_NEW,
         'comments': None,
         'unenrolled_run': None
     }, data['support_details'][0])
Exemple #32
0
 def test_email_settings_fulfilled_entitlement(self, mock_email_feature,
                                               mock_course_overview):
     """
     Assert that the Email Settings action is shown when the user has a fulfilled entitlement.
     """
     mock_email_feature.return_value = True
     mock_course_overview.return_value = CourseOverviewFactory(
         start=self.TOMORROW, self_paced=True, enrollment_end=self.TOMORROW)
     course_enrollment = CourseEnrollmentFactory(user=self.user)
     CourseEntitlementFactory(user=self.user,
                              enrollment_course_run=course_enrollment)
     response = self.client.get(self.path)
     self.assertEqual(
         pq(response.content)(self.EMAIL_SETTINGS_ELEMENT_ID).length, 1)
Exemple #33
0
    def test_is_entitlement_regainable(self):
        """
        Test that the entitlement is not expired when created now, and is expired when created20 days
        ago with a policy that sets the expiration period to 14 days
        """
        entitlement = CourseEntitlementFactory.create(
            enrollment_course_run=self.enrollment)
        assert entitlement.is_entitlement_regainable() is True

        # Create and associate a GeneratedCertificate for a user and course and make sure it isn't regainable
        GeneratedCertificateFactory(
            user=entitlement.user,
            course_id=entitlement.enrollment_course_run.course_id,
            mode=MODES.verified,
            status=CertificateStatuses.downloadable,
        )

        assert entitlement.is_entitlement_regainable() is False

        # Create a date 20 days in the past (greater than the policy expire period of 14 days)
        # and apply it to both the entitlement and the course
        past_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(
            days=20)
        entitlement = CourseEntitlementFactory.create(
            enrollment_course_run=self.enrollment, created=past_datetime)
        self.enrollment.created = past_datetime
        self.course.start = past_datetime

        self.course.save()
        self.enrollment.save()

        assert entitlement.is_entitlement_regainable() is False

        entitlement = CourseEntitlementFactory.create(
            expired_at=datetime.now())

        assert entitlement.is_entitlement_regainable
Exemple #34
0
    def test_course_run_fulfillable_enrollment_ended_already_enrolled(self):
        course_overview = self.create_course(
            start_from_now=-3,
            end_from_now=2,
            enrollment_start_from_now=-2,
            enrollment_end_from_now=-1,
        )

        entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)
        # Enroll User in the Course, but do not update the entitlement
        CourseEnrollmentFactory.create(user=entitlement.user,
                                       course_id=course_overview.id)

        assert is_course_run_entitlement_fulfillable(course_overview.id,
                                                     entitlement)
Exemple #35
0
    def test_revoke_unenroll_entitlement(self):
        course_entitlement = CourseEntitlementFactory()
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(course_entitlement.uuid)])

        enrollment = CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id)

        course_entitlement.refresh_from_db()
        course_entitlement.enrollment_course_run = enrollment
        course_entitlement.save()

        assert course_entitlement.enrollment_course_run is not None

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 204

        course_entitlement.refresh_from_db()
        assert course_entitlement.expired_at is not None
        assert course_entitlement.enrollment_course_run is None
Exemple #36
0
    def test_staff_get_expired_entitlements(self):
        past_datetime = now() - timedelta(days=365 * 2)
        entitlements = CourseEntitlementFactory.create_batch(2, created=past_datetime, user=self.user)

        # Set the first entitlement to be at a time that it isn't expired
        entitlements[0].created = now()
        entitlements[0].save()

        response = self.client.get(
            self.entitlements_list_url,
            content_type='application/json',
        )
        assert response.status_code == 200
        results = response.data.get('results', [])  # pylint: disable=no-member
        # Make sure that the first result isn't expired, and the second one is also not for staff users
        assert results[0].get('expired_at') is None and results[1].get('expired_at') is None
Exemple #37
0
    def test_check_for_no_entitlement_and_do_not_enroll(
            self, mock_get_course_uuid):
        course = CourseFactory()
        CourseModeFactory(
            course_id=course.id,
            mode_slug=CourseMode.VERIFIED,
            # This must be in the future to ensure it is returned by downstream code.
            expiration_datetime=now() + timedelta(days=1))
        entitlement = CourseEntitlementFactory.create(
            mode=CourseMode.VERIFIED,
            user=self.user,
        )
        mock_get_course_uuid.return_value = None

        assert not CourseEnrollment.is_enrolled(user=self.user,
                                                course_key=course.id)

        CourseEntitlement.check_for_existing_entitlement_and_enroll(
            user=self.user,
            course_run_key=course.id,
        )

        assert not CourseEnrollment.is_enrolled(user=self.user,
                                                course_key=course.id)

        entitlement.refresh_from_db()
        assert entitlement.enrollment_course_run is None

        new_course = CourseFactory()
        CourseModeFactory(
            course_id=new_course.id,
            mode_slug=CourseMode.VERIFIED,
            # This must be in the future to ensure it is returned by downstream code.
            expiration_datetime=now() + timedelta(days=1))

        # Return invalid uuid so that no entitlement returned for this new course
        mock_get_course_uuid.return_value = uuid4().hex

        try:
            CourseEntitlement.check_for_existing_entitlement_and_enroll(
                user=self.user,
                course_run_key=new_course.id,
            )
            assert not CourseEnrollment.is_enrolled(user=self.user,
                                                    course_key=new_course.id)
        except AttributeError as error:
            self.fail(error.message)
Exemple #38
0
    def test_revoke_unenroll_entitlement(self, mock_course_uuid):
        enrollment = CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id, is_active=True)
        course_entitlement = CourseEntitlementFactory.create(user=self.user, enrollment_course_run=enrollment)
        mock_course_uuid.return_value = course_entitlement.course_uuid
        url = reverse(self.ENTITLEMENTS_DETAILS_PATH, args=[str(course_entitlement.uuid)])

        assert course_entitlement.enrollment_course_run is not None

        response = self.client.delete(
            url,
            content_type='application/json',
        )
        assert response.status_code == 204

        course_entitlement.refresh_from_db()
        assert course_entitlement.expired_at is not None
        assert course_entitlement.enrollment_course_run is None
Exemple #39
0
 def test_fulfilled_expired_entitlement(self, mock_course_key,
                                        mock_course_overview,
                                        mock_course_runs,
                                        mock_get_programs):
     """
     When a learner has a fulfilled entitlement that is expired, their course dashboard should have:
         - exactly one course item, meaning it:
             - has an entitlement card
         - Message that the learner can no longer change sessions
         - a related programs message
     """
     mocked_course_overview = CourseOverviewFactory(
         start=self.TOMORROW, self_paced=True, enrollment_end=self.TOMORROW)
     mock_course_overview.return_value = mocked_course_overview
     mock_course_key.return_value = mocked_course_overview.id
     course_enrollment = CourseEnrollmentFactory(
         user=self.user,
         course_id=unicode(mocked_course_overview.id),
         created=self.THREE_YEARS_AGO)
     mock_course_runs.return_value = [{
         'key':
         str(mocked_course_overview.id),
         'enrollment_end':
         str(mocked_course_overview.enrollment_end),
         'pacing_type':
         'self_paced',
         'type':
         'verified',
         'status':
         'published'
     }]
     entitlement = CourseEntitlementFactory(
         user=self.user,
         enrollment_course_run=course_enrollment,
         created=self.THREE_YEARS_AGO)
     program = ProgramFactory()
     program['courses'][0]['course_runs'] = [{
         'key':
         unicode(mocked_course_overview.id)
     }]
     program['courses'][0]['uuid'] = entitlement.course_uuid
     mock_get_programs.return_value = [program]
     response = self.client.get(self.path)
     self.assertEqual(response.content.count('<li class="course-item">'), 1)
     self.assertIn('You can no longer change sessions.', response.content)
     self.assertIn('Related Programs:', response.content)
    def test_unpublished_sessions_for_entitlement(self, mock_get_edx_api_data):
        """
        Test unpublished course runs are not part of visible session entitlements when the user
        is not enrolled.
        """
        catalog_course_run = CourseRunFactory.create(status=COURSE_UNPUBLISHED)
        catalog_course = CourseFactory(course_runs=[catalog_course_run])
        mock_get_edx_api_data.return_value = catalog_course
        course_key = CourseKey.from_string(catalog_course_run.get('key'))
        course_overview = CourseOverviewFactory.create(id=course_key, start=self.tomorrow)
        CourseModeFactory.create(mode_slug=CourseMode.VERIFIED, min_price=100, course_id=course_overview.id)
        entitlement = CourseEntitlementFactory(
            user=self.user, mode=CourseMode.VERIFIED
        )

        session_entitlements = get_visible_sessions_for_entitlement(entitlement)
        self.assertEqual(session_entitlements, [])
    def test_is_entitlement_redeemable(self):
        """
        Test that the entitlement is not expired when created now, and is expired when created 2 years
        ago with a policy that sets the expiration period to 450 days
        """

        entitlement = CourseEntitlementFactory.create()

        assert entitlement.is_entitlement_redeemable() is True

        # Create a date 2 years in the past (greater than the policy expire period of 450 days)
        past_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(
            days=365 * 2)
        entitlement.created = past_datetime
        entitlement.save()

        assert entitlement.is_entitlement_redeemable() is False
Exemple #42
0
    def test_user_can_enroll(self, mock_get_course_runs):
        course_entitlement = CourseEntitlementFactory.create(user=self.user)
        mock_get_course_runs.return_value = self.return_values
        url = reverse(self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
                      args=[str(course_entitlement.uuid)])
        assert course_entitlement.enrollment_course_run is None

        data = {'course_run_id': str(self.course.id)}
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)
        assert course_entitlement.enrollment_course_run is not None
    def test_data(self):
        entitlement = CourseEntitlementFactory()
        request = RequestFactory().get('')
        serializer = CourseEntitlementSerializer(entitlement,
                                                 context={'request': request})

        expected = {
            'user': entitlement.user.username,
            'uuid': str(entitlement.uuid),
            'expired_at': entitlement.expired_at,
            'course_uuid': str(entitlement.course_uuid),
            'mode': entitlement.mode,
            'order_number': entitlement.order_number,
            'created': entitlement.created.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
            'modified': entitlement.modified.strftime('%Y-%m-%dT%H:%M:%S.%fZ'),
        }

        assert serializer.data == expected
Exemple #44
0
    def setUp(self):
        super(TestRevokeEntitlementSignal, self).setUp()

        # Ensure the E-Commerce service user exists
        UserFactory(username=settings.ECOMMERCE_SERVICE_WORKER_USERNAME,
                    is_staff=True)

        self.requester = UserFactory(username="******")
        self.student = UserFactory(
            username="******",
            email="*****@*****.**",
        )
        self.course_entitlement = CourseEntitlementFactory(
            user=self.student, mode=CourseMode.VERIFIED)

        self.config = CommerceConfiguration.current()
        self.config.enable_automatic_refund_approval = True
        self.config.save()
    def test_actually_expired(self):
        """
        Integration test with CourseEntitlement to make sure we are calling the
        correct API.
        """
        # Create an actual old entitlement
        past_days = CourseEntitlementPolicy.DEFAULT_EXPIRATION_PERIOD_DAYS
        past_datetime = datetime.now(tz=pytz.UTC) - timedelta(days=past_days)
        entitlement = CourseEntitlementFactory.create(created=past_datetime)

        # Sanity check
        self.assertIsNone(entitlement.expired_at)

        # Run enforcement
        tasks.expire_old_entitlements.delay([entitlement]).get()
        entitlement.refresh_from_db()

        self.assertIsNotNone(entitlement.expired_at)
Exemple #46
0
    def test_email_settings_fulfilled_entitlement(self, mock_email_feature, mock_get_course_runs):
        """
        Assert that the Email Settings action is shown when the user has a fulfilled entitlement.
        """
        mock_email_feature.return_value = True
        course_overview = CourseOverviewFactory(
            start=self.TOMORROW, self_paced=True, enrollment_end=self.TOMORROW
        )
        course_enrollment = CourseEnrollmentFactory(user=self.user, course_id=course_overview.id)
        entitlement = CourseEntitlementFactory(user=self.user, enrollment_course_run=course_enrollment)
        course_runs = [{
            'key': six.text_type(course_overview.id),
            'uuid': entitlement.course_uuid
        }]
        mock_get_course_runs.return_value = course_runs

        response = self.client.get(self.path)
        self.assertEqual(pq(response.content)(self.EMAIL_SETTINGS_ELEMENT_ID).length, 1)
Exemple #47
0
    def test_user_can_revoke_and_no_refund_available(
            self,
            mock_get_course_runs,
            mock_refund_entitlement,
            mock_is_refundable
    ):
        course_entitlement = CourseEntitlementFactory.create(user=self.user)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(
            self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
            args=[str(course_entitlement.uuid)]
        )
        assert course_entitlement.enrollment_course_run is None

        data = {
            'course_run_id': str(self.course.id)
        }
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)

        # Unenroll with Revoke for refund
        with patch('lms.djangoapps.commerce.signals.handle_refund_entitlement') as mock_refund_handler:
            REFUND_ENTITLEMENT.connect(mock_refund_handler)

            revoke_url = url + '?is_refund=true'
            response = self.client.delete(
                revoke_url,
                content_type='application/json',
            )
            assert response.status_code == 400

            course_entitlement.refresh_from_db()
            assert not mock_refund_handler.called
            assert CourseEnrollment.is_enrolled(self.user, self.course.id)
            assert course_entitlement.enrollment_course_run is not None
            assert course_entitlement.expired_at is None
    def test_get_visible_sessions_for_entitlement(self, mock_get_edx_api_data):
        """
        Test retrieval of visible session entitlements.
        """
        catalog_course_run = CourseRunFactory.create()
        catalog_course = CourseFactory(course_runs=[catalog_course_run])
        mock_get_edx_api_data.return_value = catalog_course
        course_key = CourseKey.from_string(catalog_course_run.get('key'))
        course_overview = CourseOverviewFactory.create(id=course_key, start=self.tomorrow)
        CourseModeFactory.create(mode_slug=CourseMode.VERIFIED, min_price=100, course_id=course_overview.id)
        course_enrollment = CourseEnrollmentFactory(
            user=self.user, course_id=unicode(course_overview.id), mode=CourseMode.VERIFIED
        )
        entitlement = CourseEntitlementFactory(
            user=self.user, enrollment_course_run=course_enrollment, mode=CourseMode.VERIFIED
        )

        session_entitlements = get_visible_sessions_for_entitlement(entitlement)
        self.assertEqual(session_entitlements, [catalog_course_run])
Exemple #49
0
 def test_fulfilled_entitlement(self, mock_course_key, mock_course_overview,
                                mock_course_runs, mock_get_programs):
     """
     When a learner has a fulfilled entitlement, their course dashboard should have:
         - exactly one course item, meaning it:
             - has an entitlement card
             - does NOT have a course card referencing the selected session
         - an unhidden Change or Leave Session button
         - a related programs message
     """
     mocked_course_overview = CourseOverviewFactory(
         start=self.TOMORROW, self_paced=True, enrollment_end=self.TOMORROW)
     mock_course_overview.return_value = mocked_course_overview
     mock_course_key.return_value = mocked_course_overview.id
     course_enrollment = CourseEnrollmentFactory(
         user=self.user, course_id=unicode(mocked_course_overview.id))
     mock_course_runs.return_value = [{
         'key':
         str(mocked_course_overview.id),
         'enrollment_end':
         str(mocked_course_overview.enrollment_end),
         'pacing_type':
         'self_paced',
         'type':
         'verified',
         'status':
         'published'
     }]
     entitlement = CourseEntitlementFactory(
         user=self.user, enrollment_course_run=course_enrollment)
     program = ProgramFactory()
     program['courses'][0]['course_runs'] = [{
         'key':
         unicode(mocked_course_overview.id)
     }]
     program['courses'][0]['uuid'] = entitlement.course_uuid
     mock_get_programs.return_value = [program]
     response = self.client.get(self.path)
     self.assertEqual(response.content.count('<li class="course-item">'), 1)
     self.assertIn('<button class="change-session btn-link "',
                   response.content)
     self.assertIn('Related Programs:', response.content)
Exemple #50
0
 def test_ecommerce_refund_failed_process_notification_sent(
         self, mock_send_notification):
     httpretty.register_uri(httpretty.POST,
                            settings.ECOMMERCE_API_URL + 'refunds/',
                            status=201,
                            body='[1]',
                            content_type='application/json')
     httpretty.register_uri(httpretty.PUT,
                            settings.ECOMMERCE_API_URL +
                            'refunds/1/process/',
                            status=400,
                            body='{}',
                            content_type='application/json')
     course_entitlement = CourseEntitlementFactory.create(
         mode=CourseMode.VERIFIED)
     refund_success = refund_entitlement(course_entitlement)
     assert mock_send_notification.is_called
     call_args = list(mock_send_notification.call_args)
     assert call_args[0] == (course_entitlement.user, [1])
     assert refund_success
Exemple #51
0
    def test_get_user_expired_entitlements(self):
        past_datetime = now() - timedelta(days=365 * 2)
        not_staff_user = UserFactory()
        self.client.login(username=not_staff_user.username, password=TEST_PASSWORD)
        entitlement_user2 = CourseEntitlementFactory.create_batch(2, user=not_staff_user, created=past_datetime)
        url = reverse('entitlements_api:v1:entitlements-list')
        url += '?user={username}'.format(username=not_staff_user.username)

        # Set the first entitlement to be at a time that it isn't expired
        entitlement_user2[0].created = now()
        entitlement_user2[0].save()

        response = self.client.get(
            url,
            content_type='application/json',
        )
        assert response.status_code == 200

        results = response.data.get('results', [])  # pylint: disable=no-member
        assert results[0].get('expired_at') is None and results[1].get('expired_at')
Exemple #52
0
 def test_unfulfilled_expired_entitlement(self, mock_course_overview,
                                          mock_course_runs):
     """
     When a learner has an unfulfilled, expired entitlement, a card should NOT appear on the dashboard.
     This use case represents either an entitlement that the user waited too long to fulfill, or an entitlement
     for which they received a refund.
     """
     CourseEntitlementFactory(user=self.user,
                              created=self.THREE_YEARS_AGO,
                              expired_at=datetime.datetime.now())
     mock_course_overview.return_value = CourseOverviewFactory(
         start=self.TOMORROW)
     mock_course_runs.return_value = [{
         'key': 'course-v1:FAKE+FA1-MA1.X+3T2017',
         'enrollment_end': str(self.TOMORROW),
         'pacing_type': 'instructor_paced',
         'type': 'verified'
     }]
     response = self.client.get(self.path)
     self.assertEqual(response.content.count('<li class="course-item">'), 0)
Exemple #53
0
    def test_user_is_not_unenrolled_on_failed_refund(
            self,
            mock_get_course_runs,
            mock_refund_entitlement,
            mock_is_refundable
    ):
        course_entitlement = CourseEntitlementFactory.create(user=self.user, mode=CourseMode.VERIFIED)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(
            self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
            args=[str(course_entitlement.uuid)]
        )
        assert course_entitlement.enrollment_course_run is None

        # Enroll the User
        data = {
            'course_run_id': str(self.course.id)
        }
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)

        # Unenroll with Revoke for refund
        revoke_url = url + '?is_refund=true'
        response = self.client.delete(
            revoke_url,
            content_type='application/json',
        )
        assert response.status_code == 500

        course_entitlement.refresh_from_db()
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)
        assert course_entitlement.enrollment_course_run is not None
        assert course_entitlement.expired_at is None
Exemple #54
0
    def test_user_cannot_enroll_in_unknown_course_run_id(
            self, mock_get_course_runs):
        fake_course_str = str(self.course.id) + 'fake'
        fake_course_key = CourseKey.from_string(fake_course_str)
        course_entitlement = CourseEntitlementFactory.create(user=self.user)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
                      args=[str(course_entitlement.uuid)])

        data = {'course_run_id': str(fake_course_key)}
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )

        expected_message = 'The Course Run ID is not a match for this Course Entitlement.'
        assert response.status_code == 400
        assert response.data['message'] == expected_message  # pylint: disable=no-member
        assert not CourseEnrollment.is_enrolled(self.user, fake_course_key)
 def test_unfulfilled_expired_entitlement(self, mock_course_overview,
                                          mock_course_runs):
     """
     When a learner has an unfulfilled, expired entitlement, their course dashboard should have:
         - a hidden 'View Course' button
         - a message saying that they can no longer select a session
     """
     CourseEntitlementFactory(user=self.user, created=self.THREE_YEARS_AGO)
     mock_course_overview.return_value = CourseOverviewFactory(
         start=self.TOMORROW)
     mock_course_runs.return_value = [{
         'key': 'course-v1:FAKE+FA1-MA1.X+3T2017',
         'enrollment_end': self.TOMORROW,
         'pacing_type': 'instructor_paced',
         'type': 'verified'
     }]
     response = self.client.get(self.path)
     self.assertIn('class="enter-course hidden"', response.content)
     self.assertIn('You can no longer select a session', response.content)
     self.assertNotIn(
         '<div class="course-entitlement-selection-container ">',
         response.content)
Exemple #56
0
    def test_already_enrolled_course_ended(self, mock_get_course_runs):
        """
        Test that already enrolled user can still select a session while
        course has ended but upgrade deadline is in future.
        """
        course_entitlement = CourseEntitlementFactory.create(
            user=self.user, mode=CourseMode.VERIFIED)
        mock_get_course_runs.return_value = self.return_values

        # Setup enrollment period to be in the past
        utc_now = datetime.now(UTC)
        self.course.start = utc_now - timedelta(days=15)
        self.course.end = utc_now - timedelta(days=1)
        self.course = self.update_course(self.course, self.user.id)
        CourseOverview.update_select_courses([self.course.id],
                                             force_update=True)

        CourseEnrollment.enroll(self.user,
                                self.course.id,
                                mode=CourseMode.AUDIT)

        url = reverse(self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
                      args=[str(course_entitlement.uuid)])

        data = {'course_run_id': str(self.course.id)}
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)
        (enrolled_mode, is_active) = CourseEnrollment.enrollment_mode_for_user(
            self.user, self.course.id)
        assert is_active and (enrolled_mode == course_entitlement.mode)
        assert course_entitlement.enrollment_course_run is not None
    def test_user_can_revoke_and_refund(self, mock_get_course_runs, mock_refund_entitlement):
        course_entitlement = CourseEntitlementFactory.create(user=self.user)
        mock_get_course_runs.return_value = self.return_values

        url = reverse(
            self.ENTITLEMENTS_ENROLLMENT_NAMESPACE,
            args=[str(course_entitlement.uuid)]
        )
        assert course_entitlement.enrollment_course_run is None

        data = {
            'course_run_id': str(self.course.id)
        }
        response = self.client.post(
            url,
            data=json.dumps(data),
            content_type='application/json',
        )
        course_entitlement.refresh_from_db()

        assert response.status_code == 201
        assert CourseEnrollment.is_enrolled(self.user, self.course.id)

        # Unenroll with Revoke for refund
        revoke_url = url + '?is_refund=true'
        response = self.client.delete(
            revoke_url,
            content_type='application/json',
        )
        assert response.status_code == 204

        course_entitlement.refresh_from_db()
        assert mock_refund_entitlement.is_called
        assert (CourseEntitlementSerializer(mock_refund_entitlement.call_args[1]['course_entitlement']).data ==
                CourseEntitlementSerializer(course_entitlement).data)
        assert not CourseEnrollment.is_enrolled(self.user, self.course.id)
        assert course_entitlement.enrollment_course_run is None
        assert course_entitlement.expired_at is not None
Exemple #58
0
 def test_unfulfilled_entitlement(self, mock_course_overview,
                                  mock_course_runs):
     """
     When a learner has an unfulfilled entitlement, their course dashboard should have:
         - a hidden 'View Course' button
         - the text 'In order to view the course you must select a session:'
         - an unhidden course-entitlement-selection-container
     """
     CourseEntitlementFactory(user=self.user)
     mock_course_overview.return_value = CourseOverviewFactory(
         start=self.TOMORROW)
     mock_course_runs.return_value = [{
         'key': 'course-v1:FAKE+FA1-MA1.X+3T2017',
         'enrollment_end': self.TOMORROW,
         'pacing_type': 'instructor_paced',
         'type': 'verified'
     }]
     response = self.client.get(self.path)
     self.assertIn('class="enter-course hidden"', response.content)
     self.assertIn('You must select a session to access the course.',
                   response.content)
     self.assertIn('<div class="course-entitlement-selection-container ">',
                   response.content)
Exemple #59
0
    def test_course_run_missing_overview_not_fulfillable(self):
        entitlement = CourseEntitlementFactory.create(mode=CourseMode.VERIFIED)

        assert not is_course_run_entitlement_fulfillable(
            CourseKey.from_string('course-v1:edx+FakeCourse+3T2017'),
            entitlement)
Exemple #60
0
    def test_expired_at_datetime(self):
        """
        Tests that using the getter method properly updates the expired_at field for an entitlement
        """

        # Verify a brand new entitlement isn't expired and the db row isn't updated
        entitlement = CourseEntitlementFactory.create()
        expired_at_datetime = entitlement.expired_at_datetime
        assert expired_at_datetime is None
        assert entitlement.expired_at is None

        # Verify an entitlement from two years ago is expired and the db row is updated
        past_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(
            days=365 * 2)
        entitlement.created = past_datetime
        entitlement.save()
        expired_at_datetime = entitlement.expired_at_datetime
        assert expired_at_datetime
        assert entitlement.expired_at

        # Verify that a brand new entitlement that has been redeemed is not expired
        entitlement = CourseEntitlementFactory.create(
            enrollment_course_run=self.enrollment)
        assert entitlement.enrollment_course_run
        expired_at_datetime = entitlement.expired_at_datetime
        assert expired_at_datetime is None
        assert entitlement.expired_at is None

        # Verify that an entitlement that has been redeemed but not within 14 days
        # and the course started more than two weeks ago is expired
        past_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(
            days=20)
        entitlement.created = past_datetime
        self.enrollment.created = past_datetime
        self.course.start = past_datetime
        entitlement.save()
        self.course.save()
        self.enrollment.save()
        assert entitlement.enrollment_course_run
        expired_at_datetime = entitlement.expired_at_datetime
        assert expired_at_datetime
        assert entitlement.expired_at

        # Verify that an entitlement that has just been created, but the user has been enrolled in the course for
        # greater than 14 days, and the course started more than 14 days ago is not expired
        entitlement = CourseEntitlementFactory.create(
            enrollment_course_run=self.enrollment)
        past_datetime = datetime.utcnow().replace(tzinfo=pytz.UTC) - timedelta(
            days=20)
        entitlement.created = datetime.utcnow().replace(tzinfo=pytz.UTC)
        self.enrollment.created = past_datetime
        self.course.start = past_datetime
        entitlement.save()
        self.enrollment.save()
        self.course.save()
        assert entitlement.enrollment_course_run
        expired_at_datetime = entitlement.expired_at_datetime
        assert expired_at_datetime is None
        assert entitlement.expired_at is None

        # Verify a date 451 days in the past (1 days after the policy expiration)
        # That is enrolled and started in within the regain period is still expired
        entitlement = CourseEntitlementFactory.create(
            enrollment_course_run=self.enrollment)
        expired_datetime = datetime.utcnow().replace(
            tzinfo=pytz.UTC) - timedelta(days=451)
        entitlement.created = expired_datetime
        now = datetime.now(tz=pytz.UTC)
        self.enrollment.created = now
        self.course.start = now
        entitlement.save()
        self.course.save()
        self.enrollment.save()
        assert entitlement.enrollment_course_run
        expired_at_datetime = entitlement.expired_at_datetime
        assert expired_at_datetime
        assert entitlement.expired_at