Пример #1
0
    def test_if_already_enroll(self):
        """
        Test that enrollment api is skipped of user is already enroll is course.
        """
        def create_audit(course_key):
            """Helper function to create a fake enrollment"""
            return Enrollment({"course_details": {"course_id": course_key}})

        def get_student_enrollments():
            """List of existing enrollments"""
            return Enrollments([
                {"course_details": {"course_id": self.line1.course_key}},
                {"course_details": {"course_id": self.line2.course_key}}
            ])

        create_audit_mock = MagicMock(side_effect=create_audit)
        get_enrollments_mock = MagicMock(
            side_effect=get_student_enrollments,
            is_enrolled_in=True
        )
        enrollments_mock = MagicMock(
            create_audit_student_enrollment=create_audit_mock,
            get_student_enrollments=get_enrollments_mock
        )
        edx_api_mock = MagicMock(enrollments=enrollments_mock)
        with patch('ecommerce.api.EdxApi', return_value=edx_api_mock):
            enroll_user_on_success(self.order)

        assert get_enrollments_mock.called is True
        assert not create_audit_mock.called
Пример #2
0
    def test_if_already_enroll(self):
        """
        Test that enrollment api is skipped of user is already enroll is course.
        """
        def create_audit(course_key):
            """Helper function to create a fake enrollment"""
            return Enrollment({"course_details": {"course_id": course_key}})

        def get_student_enrollments():
            """List of existing enrollments"""
            return Enrollments([
                {"course_details": {"course_id": self.line1.course_key}},
                {"course_details": {"course_id": self.line2.course_key}}
            ])

        create_audit_mock = MagicMock(side_effect=create_audit)
        get_enrollments_mock = MagicMock(
            side_effect=get_student_enrollments,
            is_enrolled_in=True
        )
        enrollments_mock = MagicMock(
            create_audit_student_enrollment=create_audit_mock,
            get_student_enrollments=get_enrollments_mock
        )
        edx_api_mock = MagicMock(enrollments=enrollments_mock)
        with patch('ecommerce.api.EdxApi', return_value=edx_api_mock):
            enroll_user_on_success(self.order)

        assert get_enrollments_mock.called is True
        assert not create_audit_mock.called
Пример #3
0
    def test_enroll(self):
        """
        Test that an enrollment is made for each course key attached to the order
        and that the CachedEnrollments are produced.
        """
        def create_audit(course_key):
            """Helper function to create a fake enrollment"""
            return Enrollment({"course_details": {"course_id": course_key}})

        def get_student_enrollments():
            """List of existing enrollments"""
            return Enrollments([])

        create_audit_mock = MagicMock(side_effect=create_audit)
        get_enrollments_mock = MagicMock(side_effect=get_student_enrollments)
        enrollments_mock = MagicMock(
            create_audit_student_enrollment=create_audit_mock,
            get_student_enrollments=get_enrollments_mock
        )
        edx_api_mock = MagicMock(enrollments=enrollments_mock)
        with patch('ecommerce.api.EdxApi', return_value=edx_api_mock):
            enroll_user_on_success(self.order)

        assert get_enrollments_mock.called is True
        assert len(create_audit_mock.call_args_list) == self.order.line_set.count()
        for i, line in enumerate(self.order.line_set.all()):
            assert create_audit_mock.call_args_list[i][0] == (line.course_key, )
        assert CachedEnrollment.objects.count() == self.order.line_set.count()

        for line in self.order.line_set.all():
            enrollment = CachedEnrollment.objects.get(
                user=self.order.user,
                course_run__edx_course_key=line.course_key,
            )
            assert enrollment.data == create_audit(line.course_key).json
Пример #4
0
    def test_enroll(self):
        """
        Test that an enrollment is made for each course key attached to the order
        and that the CachedEnrollments are produced.
        """
        def create_audit(course_key):
            """Helper function to create a fake enrollment"""
            return Enrollment({"course_details": {"course_id": course_key}})

        def get_student_enrollments():
            """List of existing enrollments"""
            return Enrollments([])

        create_audit_mock = MagicMock(side_effect=create_audit)
        get_enrollments_mock = MagicMock(side_effect=get_student_enrollments)
        enrollments_mock = MagicMock(
            create_audit_student_enrollment=create_audit_mock,
            get_student_enrollments=get_enrollments_mock
        )
        edx_api_mock = MagicMock(enrollments=enrollments_mock)
        with patch('ecommerce.api.EdxApi', return_value=edx_api_mock):
            enroll_user_on_success(self.order)

        assert get_enrollments_mock.called is True
        assert len(create_audit_mock.call_args_list) == self.order.line_set.count()
        for i, line in enumerate(self.order.line_set.all()):
            assert create_audit_mock.call_args_list[i][0] == (line.course_key, )
        assert CachedEnrollment.objects.count() == self.order.line_set.count()

        for line in self.order.line_set.all():
            enrollment = CachedEnrollment.objects.get(
                user=self.order.user,
                course_run__edx_course_key=line.course_key,
            )
            assert enrollment.data == create_audit(line.course_key).json
Пример #5
0
    def test_failed(self):
        """
        Test that an exception is raised containing a list of exceptions of the failed enrollments
        """
        def create_audit(course_key):
            """Fail for first course key"""
            if course_key == self.line1.course_key:
                raise Exception("fatal error {}".format(course_key))
            return Enrollment({"course_details": {"course_id": course_key}})

        def get_student_enrollments():
            """List of existing enrollments"""
            return Enrollments([])

        create_audit_mock = MagicMock(side_effect=create_audit)
        get_enrollments_mock = MagicMock(side_effect=get_student_enrollments)
        enrollments_mock = MagicMock(
            create_audit_student_enrollment=create_audit_mock,
            get_student_enrollments=get_enrollments_mock)
        edx_api_mock = MagicMock(enrollments=enrollments_mock)
        with patch('ecommerce.api.EdxApi', return_value=edx_api_mock):
            with self.assertRaises(EcommerceEdxApiException) as ex:
                enroll_user_on_success(self.order)
            assert len(ex.exception.args[0]) == 1
            assert ex.exception.args[0][0].args[0] == 'fatal error {}'.format(
                self.line1.course_key)

        assert len(
            create_audit_mock.call_args_list) == self.order.line_set.count()
        for i, line in enumerate(self.order.line_set.all()):
            assert create_audit_mock.call_args_list[i][0] == (
                line.course_key, )

        assert CachedEnrollment.objects.count() == 1
        enrollment = CachedEnrollment.objects.get(
            user=self.order.user,
            course_run__edx_course_key=self.line2.course_key,
        )
        assert enrollment.data == create_audit(self.line2.course_key).json
Пример #6
0
    def test_failed(self):
        """
        Test that an exception is raised containing a list of exceptions of the failed enrollments
        """
        def create_audit(course_key):
            """Fail for first course key"""
            if course_key == self.line1.course_key:
                raise Exception("fatal error {}".format(course_key))
            return Enrollment({"course_details": {"course_id": course_key}})

        def get_student_enrollments():
            """List of existing enrollments"""
            return Enrollments([])

        create_audit_mock = MagicMock(side_effect=create_audit)
        get_enrollments_mock = MagicMock(side_effect=get_student_enrollments)
        enrollments_mock = MagicMock(
            create_audit_student_enrollment=create_audit_mock,
            get_student_enrollments=get_enrollments_mock
        )
        edx_api_mock = MagicMock(enrollments=enrollments_mock)
        with patch('ecommerce.api.EdxApi', return_value=edx_api_mock):
            with self.assertRaises(EcommerceEdxApiException) as ex:
                enroll_user_on_success(self.order)
            assert len(ex.exception.args[0]) == 1
            assert ex.exception.args[0][0].args[0] == 'fatal error {}'.format(self.line1.course_key)

        assert len(create_audit_mock.call_args_list) == self.order.line_set.count()
        for i, line in enumerate(self.order.line_set.all()):
            assert create_audit_mock.call_args_list[i][0] == (line.course_key, )

        assert CachedEnrollment.objects.count() == 1
        enrollment = CachedEnrollment.objects.get(
            user=self.order.user,
            course_run__edx_course_key=self.line2.course_key,
        )
        assert enrollment.data == create_audit(self.line2.course_key).json
Пример #7
0
    def post(self, request, *args, **kwargs):
        """
        If the course run is part of a financial aid program, create a new unfulfilled Order
        and return information used to submit to CyberSource.

        If the program does not have financial aid, this will return a URL to let the user
        pay for the course on edX.
        """
        user_ip, _ = get_client_ip(request)
        try:
            course_id = request.data['course_id']
        except KeyError:
            raise ValidationError("Missing course_id")

        course_run = get_object_or_404(
            CourseRun,
            course__program__live=True,
            edx_course_key=course_id,
        )
        if course_run.course.program.financial_aid_availability:
            order = create_unfulfilled_order(course_id, request.user)
            dashboard_url = request.build_absolute_uri('/dashboard/')
            if order.total_price_paid == 0:
                # If price is $0, don't bother going to CyberSource, just mark as fulfilled
                order.status = Order.FULFILLED
                order.save_and_log(request.user)
                try:
                    enroll_user_on_success(order)
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when enrolling user in one or more courses for order %s. "
                        "See other errors above for more info.", order)
                    try:
                        MailgunClient().send_individual_email(
                            "Error occurred when enrolling user during $0 checkout",
                            "Error occurred when enrolling user during $0 checkout for {order}. "
                            "Exception: {exception}".format(
                                order=order, exception=traceback.format_exc()),
                            settings.ECOMMERCE_EMAIL,
                        )
                    except:  # pylint: disable=bare-except
                        log.exception(
                            "Error occurred when sending the email to notify support "
                            "of user enrollment error during order %s $0 checkout",
                            order,
                        )

                # This redirects the user to our order success page
                payload = {}
                url = make_dashboard_receipt_url(dashboard_url, course_id,
                                                 'receipt')
                method = 'GET'
            else:
                # This generates a signed payload which is submitted as an HTML form to CyberSource
                payload = generate_cybersource_sa_payload(
                    order, dashboard_url, user_ip)
                url = settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL
                method = 'POST'
        else:
            # This redirects the user to edX to purchase the course there
            payload = {}
            url = urljoin(settings.EDXORG_BASE_URL,
                          '/course_modes/choose/{}/'.format(course_id))
            method = 'GET'

        return Response({
            'payload': payload,
            'url': url,
            'method': method,
        })
Пример #8
0
    def post(self, request, *args, **kwargs):
        """
        Confirmation from CyberSource which fulfills an existing Order.
        """
        # First, save this information in a receipt
        receipt = Receipt.objects.create(data=request.data)

        # Link the order with the receipt if we can parse it
        reference_number = request.data['req_reference_number']
        order = get_new_order_by_reference_number(reference_number)
        receipt.order = order
        receipt.save()

        decision = request.data['decision']
        if order.status == Order.FAILED and decision == CYBERSOURCE_DECISION_CANCEL:
            # This is a duplicate message, ignore since it's already handled
            return Response(status=HTTP_200_OK)
        elif order.status != Order.CREATED:
            raise EcommerceException(
                "Order {} is expected to have status 'created'".format(
                    order.id))

        if decision != CYBERSOURCE_DECISION_ACCEPT:
            order.status = Order.FAILED
            log.warning(
                "Order fulfillment failed: received a decision that wasn't ACCEPT for order %s",
                order,
            )
            if decision != CYBERSOURCE_DECISION_CANCEL:
                try:
                    MailgunClient().send_individual_email(
                        "Order fulfillment failed, decision={decision}".format(
                            decision=decision),
                        "Order fulfillment failed for order {order}".format(
                            order=order, ), settings.ECOMMERCE_EMAIL)
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when sending the email to notify "
                        "about order fulfillment failure for order %s",
                        order,
                    )
        else:
            order.status = Order.FULFILLED
        order.save_and_log(None)

        if order.status == Order.FULFILLED:
            try:
                enroll_user_on_success(order)
            except:  # pylint: disable=bare-except
                log.exception(
                    "Error occurred when enrolling user in one or more courses for order %s. "
                    "See other errors above for more info.", order)
                try:
                    MailgunClient().send_individual_email(
                        "Error occurred when enrolling user during order fulfillment",
                        "Error occurred when enrolling user during order fulfillment for {order}. "
                        "Exception: {exception}".format(
                            order=order, exception=traceback.format_exc()),
                        settings.ECOMMERCE_EMAIL,
                    )
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when sending the email to notify support "
                        "of user enrollment error during order %s fulfillment",
                        order,
                    )
        # The response does not matter to CyberSource
        return Response(status=HTTP_200_OK)
Пример #9
0
    def post(self, request, *args, **kwargs):
        """
        If the course run is part of a financial aid program, create a new unfulfilled Order
        and return information used to submit to CyberSource.

        If the program does not have financial aid, this will return a URL to let the user
        pay for the course on edX.
        """
        try:
            course_id = request.data['course_id']
        except KeyError:
            raise ValidationError("Missing course_id")

        course_run = get_object_or_404(
            CourseRun,
            course__program__live=True,
            edx_course_key=course_id,
        )
        if course_run.course.program.financial_aid_availability:
            order = create_unfulfilled_order(course_id, request.user)
            dashboard_url = request.build_absolute_uri('/dashboard/')
            if order.total_price_paid == 0:
                # If price is $0, don't bother going to CyberSource, just mark as fulfilled
                order.status = Order.FULFILLED
                order.save_and_log(request.user)
                try:
                    enroll_user_on_success(order)
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when enrolling user in one or more courses for order %s. "
                        "See other errors above for more info.",
                        order
                    )
                    try:
                        MailgunClient().send_individual_email(
                            "Error occurred when enrolling user during $0 checkout",
                            "Error occurred when enrolling user during $0 checkout for {order}. "
                            "Exception: {exception}".format(
                                order=order,
                                exception=traceback.format_exc()
                            ),
                            settings.ECOMMERCE_EMAIL,
                        )
                    except:  # pylint: disable=bare-except
                        log.exception(
                            "Error occurred when sending the email to notify support "
                            "of user enrollment error during order %s $0 checkout",
                            order,
                        )

                # This redirects the user to our order success page
                payload = {}
                url = make_dashboard_receipt_url(dashboard_url, course_id, 'receipt')
                method = 'GET'
            else:
                # This generates a signed payload which is submitted as an HTML form to CyberSource
                payload = generate_cybersource_sa_payload(order, dashboard_url)
                url = settings.CYBERSOURCE_SECURE_ACCEPTANCE_URL
                method = 'POST'
        else:
            # This redirects the user to edX to purchase the course there
            payload = {}
            url = urljoin(settings.EDXORG_BASE_URL, '/course_modes/choose/{}/'.format(course_id))
            method = 'GET'

        return Response({
            'payload': payload,
            'url': url,
            'method': method,
        })
Пример #10
0
    def post(self, request, *args, **kwargs):
        """
        Confirmation from CyberSource which fulfills an existing Order.
        """
        # First, save this information in a receipt
        receipt = Receipt.objects.create(data=request.data)

        # Link the order with the receipt if we can parse it
        reference_number = request.data['req_reference_number']
        order = get_new_order_by_reference_number(reference_number)
        receipt.order = order
        receipt.save()

        decision = request.data['decision']
        if order.status == Order.FAILED and decision == CYBERSOURCE_DECISION_CANCEL:
            # This is a duplicate message, ignore since it's already handled
            return Response(status=HTTP_200_OK)
        elif order.status != Order.CREATED:
            raise EcommerceException("Order {} is expected to have status 'created'".format(order.id))

        if decision != CYBERSOURCE_DECISION_ACCEPT:
            order.status = Order.FAILED
            log.warning(
                "Order fulfillment failed: received a decision that wasn't ACCEPT for order %s",
                order,
            )
            if decision != CYBERSOURCE_DECISION_CANCEL:
                try:
                    MailgunClient().send_individual_email(
                        "Order fulfillment failed, decision={decision}".format(
                            decision=decision
                        ),
                        "Order fulfillment failed for order {order}".format(
                            order=order,
                        ),
                        settings.ECOMMERCE_EMAIL
                    )
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when sending the email to notify "
                        "about order fulfillment failure for order %s",
                        order,
                    )
        else:
            order.status = Order.FULFILLED
        order.save_and_log(None)

        if order.status == Order.FULFILLED:
            try:
                enroll_user_on_success(order)
            except:  # pylint: disable=bare-except
                log.exception(
                    "Error occurred when enrolling user in one or more courses for order %s. "
                    "See other errors above for more info.",
                    order
                )
                try:
                    MailgunClient().send_individual_email(
                        "Error occurred when enrolling user during order fulfillment",
                        "Error occurred when enrolling user during order fulfillment for {order}. "
                        "Exception: {exception}".format(
                            order=order,
                            exception=traceback.format_exc()
                        ),
                        settings.ECOMMERCE_EMAIL,
                    )
                except:  # pylint: disable=bare-except
                    log.exception(
                        "Error occurred when sending the email to notify support "
                        "of user enrollment error during order %s fulfillment",
                        order,
                    )
        # The response does not matter to CyberSource
        return Response(status=HTTP_200_OK)