예제 #1
0
    def test_enrollment_headers(self):
        """ Test that the enrollment module 'EnrollmentFulfillmentModule' is
        sending enrollment request over to the LMS with proper headers.
        """
        # Create a dummy data for the enrollment request.
        data = {
            'user': '******',
            'is_active': True,
            'mode': 'honor',
            'course_details': {
                'course_id': self.course_id
            },
            'enrollment_attributes': []
        }

        # Create a dummy user and attach the tracking_context
        user = UserFactory()
        user.tracking_context = {'lms_user_id': '1', 'lms_client_id': '123.123', 'lms_ip': '11.22.33.44'}

        # Now call the enrollment api to send POST request to LMS and verify
        # that the header of the request being sent contains the analytics
        # header 'x-edx-ga-client-id'.
        # This will raise the exception 'ConnectionError' because the LMS is
        # not available for ecommerce tests.
        try:
            # pylint: disable=protected-access
            EnrollmentFulfillmentModule()._post_to_enrollment_api(data=data, user=user)
        except ConnectionError as exp:
            # Check that the enrollment request object has the analytics header
            # 'x-edx-ga-client-id' and 'x-forwarded-for'.
            self.assertEqual(exp.request.headers.get('x-edx-ga-client-id'), '123.123')
            self.assertEqual(exp.request.headers.get('x-forwarded-for'), '11.22.33.44')
    def test_enrollment_headers(self):
        """ Test that the enrollment module 'EnrollmentFulfillmentModule' is
        sending enrollment request over to the LMS with proper headers.
        """
        # Create a dummy data for the enrollment request.
        data = {
            'user': '******',
            'is_active': True,
            'mode': 'honor',
            'course_details': {
                'course_id': self.course_id
            },
            'enrollment_attributes': []
        }

        # Create a dummy user and attach the tracking_context
        user = UserFactory()
        user.tracking_context = {'lms_user_id': '1', 'lms_client_id': '123.123', 'lms_ip': '11.22.33.44'}

        # Now call the enrollment api to send POST request to LMS and verify
        # that the header of the request being sent contains the analytics
        # header 'x-edx-ga-client-id'.
        # This will raise the exception 'ConnectionError' because the LMS is
        # not available for ecommerce tests.
        try:
            # pylint: disable=protected-access
            EnrollmentFulfillmentModule()._post_to_enrollment_api(data=data, user=user)
        except ConnectionError as exp:
            # Check that the enrollment request object has the analytics header
            # 'x-edx-ga-client-id' and 'x-forwarded-for'.
            self.assertEqual(exp.request.headers.get('x-edx-ga-client-id'), '123.123')
            self.assertEqual(exp.request.headers.get('x-forwarded-for'), '11.22.33.44')
예제 #3
0
 def setUp(self):
     self.product = create_product()
     self.reviewer = UserFactory()
     self.voter = UserFactory()
     self.review = self.product.reviews.create(title='This is nice',
                                               score=3,
                                               body="This is the body",
                                               user=self.reviewer)
예제 #4
0
    def test_is_available_to_different_users(self):
        users, order = [UserFactory(), UserFactory()], OrderFactory()
        for user in users:
            is_voucher_available_to_user, __ = self.voucher.is_available_to_user(
                user=user)
            self.assertTrue(is_voucher_available_to_user)

            self.voucher.record_usage(order, user)
            is_voucher_available_to_user, __ = self.voucher.is_available_to_user(
                user=user)
            self.assertFalse(is_voucher_available_to_user)
예제 #5
0
    def test_zero_dollar_refund(self, mock_revoke_line):
        """
        Given an order and order lines which total $0 and are not refunded, Refund.create_with_lines
        should create and approve a Refund with corresponding RefundLines.
        """
        httpretty.register_uri(httpretty.POST,
                               get_lms_enrollment_api_url(),
                               status=200,
                               body='{}',
                               content_type='application/json')

        order = self.create_order(user=UserFactory(), free=True)

        # Verify that the order totals $0.
        self.assertEqual(order.total_excl_tax, 0)

        refund = Refund.create_with_lines(order, list(order.lines.all()))

        # Verify that refund lines are not revoked.
        self.assertFalse(mock_revoke_line.called)

        # Verify that the refund has been successfully approved.
        self.assertEqual(refund.status, REFUND.COMPLETE)
        self.assertEqual(set([line.status for line in refund.lines.all()]),
                         {REFUND_LINE.COMPLETE})
예제 #6
0
    def test_notify_purchaser_course_entielement(self, mock_task):
        """ Verify the notification is scheduled if the site has notifications enabled
        and the refund is for a course entitlement.
        """
        site_configuration = self.site.siteconfiguration
        site_configuration.send_refund_notifications = True

        user = UserFactory()

        course_entitlement = create_or_update_course_entitlement(
            'verified', 100, self.partner, '111-222-333-444',
            'Course Entitlement')
        basket = create_basket(site=self.site, owner=user, empty=True)
        basket.add_product(course_entitlement, 1)

        order = create_order(number=1, basket=basket, user=user)
        order_url = get_receipt_page_url(site_configuration, order.number)

        refund = Refund.create_with_lines(order, order.lines.all())

        with LogCapture(REFUND_MODEL_LOGGER_NAME) as l:
            refund._notify_purchaser()  # pylint: disable=protected-access

        msg = 'Course refund notification scheduled for Refund [{}].'.format(
            refund.id)
        l.check((REFUND_MODEL_LOGGER_NAME, 'INFO', msg))

        amount = format_currency(order.currency, 100)
        mock_task.assert_called_once_with(user.email,
                                          refund.id,
                                          amount,
                                          course_entitlement.title,
                                          order.number,
                                          order_url,
                                          site_code=self.partner.short_code)
예제 #7
0
 def setUp(self):
     super(CouponFulfillmentModuleTest, self).setUp()
     coupon = self.create_coupon()
     user = UserFactory()
     basket = BasketFactory()
     basket.add_product(coupon, 1)
     self.order = factories.create_order(number=1, basket=basket, user=user)
예제 #8
0
 def test_is_available_to_same_user_multiple_times(self):
     user, order = UserFactory(), OrderFactory()
     for i in range(10):
         self.voucher.record_usage(order, user)
         is_voucher_available_to_user, __ = self.voucher.is_available_to_user(
             user=user)
         self.assertTrue(is_voucher_available_to_user)
예제 #9
0
    def test_notify_purchaser(self, mock_task):
        """ Verify the notification is scheduled if the site has notifications enabled
        and the refund is for a course seat.
        """
        site_configuration = self.site.siteconfiguration
        site_configuration.send_refund_notifications = True

        user = UserFactory()

        course = CourseFactory()
        price = Decimal(100.00)
        product = course.create_or_update_seat('verified', True, price, self.partner)

        basket = create_basket(empty=True)
        basket.site = self.site
        basket.add_product(product)

        order = create_order(basket=basket, user=user)
        order_url = get_receipt_page_url(site_configuration, order.number)

        refund = Refund.create_with_lines(order, order.lines.all())

        with LogCapture(REFUND_MODEL_LOGGER_NAME) as l:
            refund._notify_purchaser()  # pylint: disable=protected-access

        msg = 'Course refund notification scheduled for Refund [{}].'.format(refund.id)
        l.check(
            (REFUND_MODEL_LOGGER_NAME, 'INFO', msg)
        )

        amount = format_currency(order.currency, price)
        mock_task.assert_called_once_with(
            user.email, refund.id, amount, course.name, order.number, order_url, site_code=self.partner.short_code
        )
예제 #10
0
    def test_post_checkout_callback(self):
        """
        When the post_checkout signal is emitted, the receiver should attempt
        to fulfill the newly-placed order and send receipt email.
        """
        httpretty.register_uri(
            httpretty.GET, get_lms_url('api/credit/v1/providers/ASU'),
            body='{"display_name": "Hogwarts"}',
            content_type="application/json"
        )
        toggle_switch('ENABLE_NOTIFICATIONS', True)
        user = UserFactory()
        course = Course.objects.create(id='edX/DemoX/Demo_Course', name='Demo Course')
        partner = self.create_partner('edx')
        seat = course.create_or_update_seat('credit', False, 50, partner, 'ASU', None, 2)

        basket = BasketFactory()
        basket.add_product(seat, 1)
        order = factories.create_order(number=1, basket=basket, user=user)
        send_course_purchase_email(None, order=order)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Order Receipt')
        self.assertEqual(
            mail.outbox[0].body,
            '\nPayment confirmation for: {course_title}'
            '\n\nDear {full_name},'
            '\n\nThank you for purchasing {credit_hours} credit hours from {credit_provider} for {course_title}. '
            'A charge will appear on your credit or debit card statement with a company name of "{platform_name}".'
            '\n\nTo receive your course credit, you must also request credit at the {credit_provider} website. '
            'For a link to request credit from {credit_provider}, or to see the status of your credit request, '
            'go to your {platform_name} dashboard.'
            '\n\nTo explore other credit-eligible courses, visit the {platform_name} website. '
            'We add new courses frequently!'
            '\n\nTo view your payment information, visit the following website.'
            '\n{receipt_url}'
            '\n\nThank you. We hope you enjoyed your course!'
            '\nThe {platform_name} team'
            '\n\nYou received this message because you purchased credit hours for {course_title}, '
            'an {platform_name} course.\n'.format(
                course_title=order.lines.first().product.title,
                full_name=user.get_full_name(),
                credit_hours=2,
                credit_provider='Hogwarts',
                platform_name=settings.PLATFORM_NAME,
                receipt_url=get_lms_url('/commerce/checkout/receipt/?basket_id={}'.format(order.basket.id))
            )
        )
예제 #11
0
    def test_post_checkout_callback(self):
        """
        When the post_checkout signal is emitted, the receiver should attempt
        to fulfill the newly-placed order and send receipt email.
        """
        httpretty.register_uri(httpretty.GET,
                               get_lms_url('api/credit/v1/providers/ASU'),
                               body='{"display_name": "Hogwarts"}',
                               content_type="application/json")
        toggle_switch('ENABLE_NOTIFICATIONS', True)
        user = UserFactory()
        course = Course.objects.create(id='edX/DemoX/Demo_Course',
                                       name='Demo Course')
        seat = course.create_or_update_seat('credit', False, 50, self.partner,
                                            'ASU', None, 2)

        basket = BasketFactory()
        basket.add_product(seat, 1)
        order = factories.create_order(number=1, basket=basket, user=user)
        send_course_purchase_email(None, order=order)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Order Receipt')
        self.assertEqual(
            mail.outbox[0].body, '\nPayment confirmation for: {course_title}'
            '\n\nDear {full_name},'
            '\n\nThank you for purchasing {credit_hours} credit hours from {credit_provider} for {course_title}. '
            'A charge will appear on your credit or debit card statement with a company name of "{platform_name}".'
            '\n\nTo receive your course credit, you must also request credit at the {credit_provider} website. '
            'For a link to request credit from {credit_provider}, or to see the status of your credit request, '
            'go to your {platform_name} dashboard.'
            '\n\nTo explore other credit-eligible courses, visit the {platform_name} website. '
            'We add new courses frequently!'
            '\n\nTo view your payment information, visit the following website.'
            '\n{receipt_url}'
            '\n\nThank you. We hope you enjoyed your course!'
            '\nThe {platform_name} team'
            '\n\nYou received this message because you purchased credit hours for {course_title}, '
            'an {platform_name} course.\n'.format(
                course_title=order.lines.first().product.title,
                full_name=user.get_full_name(),
                credit_hours=2,
                credit_provider='Hogwarts',
                platform_name=settings.PLATFORM_NAME,
                receipt_url=get_lms_url(
                    '/commerce/checkout/receipt/?orderNum={}'.format(
                        order.number))))
예제 #12
0
 def setUp(self):
     self.product = create_product()
     self.reviewer = UserFactory()
     self.data = {
         'title': '  This product is lovely',
         'body': 'I really like this cheese',
         'score': 0,
         'name': 'JR Hartley',
         'email': '*****@*****.**'
     }
예제 #13
0
    def test_post_checkout_callback(self):
        """
        When the post_checkout signal is emitted, the receiver should attempt
        to fulfill the newly-placed order and send receipt email.
        """
        httpretty.register_uri(
            httpretty.GET, get_lms_url('api/credit/v1/providers/ASU'),
            body='{"display_name": "Hogwarts"}',
            content_type="application/json"
        )
        toggle_switch('ENABLE_NOTIFICATIONS', True)
        user = UserFactory()
        course = Course.objects.create(id='edX/DemoX/Demo_Course', name='Demo Course')
        partner = self.create_partner('edx')
        seat = course.create_or_update_seat('credit', False, 50, partner, 'ASU', None, 2)

        basket = BasketFactory()
        basket.add_product(seat, 1)
        order = factories.create_order(number=1, basket=basket, user=user)
        send_course_purchase_email(None, order=order)
        self.assertEqual(len(mail.outbox), 1)
        self.assertEqual(mail.outbox[0].subject, 'Order Receipt')
        self.assertEqual(
            mail.outbox[0].body,
            '\nReceipt Confirmation for: {course_name}'
            '\n\nHi {full_name},\n\n'
            'Thank you for purchasing {credit_hour} credit hours from {provider_name} for {course_name}.'
            ' The charge below will appear on your next credit or debit card statement with a '
            'company name of {platform_name}.\n\nYou can see the status the status of your credit request or '
            'complete the credit request process on your {platform_name} dashboard\nTo browse other '
            'credit-eligible courses visit the edX website. More courses are added all the time.\n\n'
            'Thank you and congratulation on your achievement. We hope you enjoy the course!\n\n'
            'To view receipt please visit the link below'
            '\n\n{receipt_url}\n\n'
            '{platform_name} team\n\nThe edX team\n'.format(
                course_name=order.lines.first().product.title,
                full_name=user.get_full_name(),
                credit_hour=2,
                provider_name='Hogwarts',
                platform_name=settings.PLATFORM_NAME,
                receipt_url=get_lms_url('/commerce/checkout/receipt/?basket_id={}'.format(order.basket.id))
            )
        )
예제 #14
0
    def test_is_available_to_a_user_once(self):
        user, order = UserFactory(), OrderFactory()
        is_voucher_available_to_user, __ = self.voucher.is_available_to_user(
            user=user)
        self.assertTrue(is_voucher_available_to_user)

        self.voucher.record_usage(order, user)
        is_voucher_available_to_user, __ = self.voucher.is_available_to_user(
            user=user)
        self.assertFalse(is_voucher_available_to_user)
예제 #15
0
 def setUp(self):
     super(EnrollmentCodeFulfillmentModuleTests, self).setUp()
     toggle_switch(ENROLLMENT_CODE_SWITCH, True)
     course = CourseFactory()
     course.create_or_update_seat('verified', True, 50, self.partner, create_enrollment_code=True)
     enrollment_code = Product.objects.get(product_class__name=ENROLLMENT_CODE_PRODUCT_CLASS_NAME)
     user = UserFactory()
     basket = BasketFactory(owner=user, site=self.site)
     basket.add_product(enrollment_code, self.QUANTITY)
     self.order = create_order(number=1, basket=basket, user=user)
예제 #16
0
    def setUp(self):
        super(EnrollmentFulfillmentModuleTests, self).setUp()

        self.user = UserFactory()
        self.course = CourseFactory(id=self.course_id, name='Demo Course', site=self.site)

        self.seat = self.course.create_or_update_seat(self.certificate_type, False, 100, self.partner, self.provider)

        basket = BasketFactory(owner=self.user, site=self.site)
        basket.add_product(self.seat, 1)
        self.order = create_order(number=1, basket=basket, user=self.user)
예제 #17
0
    def test_has_permission_same_user(self):
        """ If the request.data['username'] matches request.user, return True. """
        user = UserFactory()

        # Normal users can create their own refunds
        request = self._get_request(data={'username': user.username},
                                    user=user)
        self.assertTrue(self.permissions_class.has_permission(request, None))

        # Normal users CANNOT create refunds for other users
        request = self._get_request(data={'username': '******'}, user=user)
        self.assertFalse(self.permissions_class.has_permission(request, None))
예제 #18
0
    def setUp(self):
        super(EnrollmentFulfillmentModuleTests, self).setUp()

        self.user = UserFactory()
        self.user.tracking_context = {
            'ga_client_id': 'test-client-id',
            'lms_user_id': 'test-user-id',
            'lms_ip': '127.0.0.1'
        }
        self.user.save()
        self.course = CourseFactory(id=self.course_id,
                                    name='Demo Course',
                                    site=self.site)

        self.seat = self.course.create_or_update_seat(self.certificate_type,
                                                      False, 100, self.partner,
                                                      self.provider)

        basket = BasketFactory(owner=self.user, site=self.site)
        basket.add_product(self.seat, 1)
        self.order = create_order(number=1, basket=basket, user=self.user)
예제 #19
0
    def test_create_with_lines(self, multiple_lines):
        """
        Given an order and order lines that have not been refunded, Refund.create_with_lines
        should create a Refund with corresponding RefundLines.
        """
        order = self.create_order(user=UserFactory(), multiple_lines=multiple_lines)

        with LogCapture(LOGGER_NAME) as l:
            refund = Refund.create_with_lines(order, list(order.lines.all()))

            self.assert_refund_creation_logged(l, refund, order)

        self.assert_refund_matches_order(refund, order)
예제 #20
0
    def test_create_with_lines_with_existing_refund(self):
        """
        Refund.create_with_lines should not create RefundLines for order lines
        which have already been refunded.
        """
        order = self.create_order(user=UserFactory())
        line = order.lines.first()
        RefundLineFactory(order_line=line)

        with LogCapture(LOGGER_NAME) as l:
            refund = Refund.create_with_lines(order, [line])
            self.assertEqual(refund, None)

            l.check()
예제 #21
0
 def setUp(self):
     super(DonationsFromCheckoutTestFulfillmentModuleTest, self).setUp()
     donation_class = ProductClass.objects.get(
         name=DONATIONS_FROM_CHECKOUT_TESTS_PRODUCT_TYPE_NAME,
         track_stock=False)
     donation = factories.create_product(product_class=donation_class,
                                         title='Test product')
     user = UserFactory()
     basket = BasketFactory(owner=user, site=self.site)
     factories.create_stockrecord(donation,
                                  num_in_stock=2,
                                  price_excl_tax=10)
     basket.add_product(donation, 1)
     self.order = create_order(number=1, basket=basket, user=user)
예제 #22
0
    def setUp(self):
        super(SailthruTests, self).setUp()
        self.request_factory = RequestFactory()
        self.request = self.request_factory.get("foo")
        self.request.COOKIES['sailthru_bid'] = 'cookie_bid'
        self.request.site = self.site
        self.user = UserFactory.create(username='******', email=TEST_EMAIL)

        toggle_switch('sailthru_enable', True)

        # create some test course objects
        self.course_id = 'edX/toy/2012_Fall'
        self.course_url = 'http://lms.testserver.fake/courses/edX/toy/2012_Fall/info'
        self.course = Course.objects.create(id=self.course_id, name='Demo Course')
예제 #23
0
    def setUp(self):
        super(SailthruTests, self).setUp()
        self.request_factory = RequestFactory()
        self.request = self.request_factory.get("foo")
        self.request.COOKIES['sailthru_bid'] = 'cookie_bid'
        self.request.site = self.site
        self.user = UserFactory.create(username='******', email=TEST_EMAIL)

        toggle_switch('sailthru_enable', True)

        # create some test course objects
        self.course_id = 'edX/toy/2012_Fall'
        self.course_url = 'http://lms.testserver.fake/courses/edX/toy/2012_Fall/info'
        self.course = Course.objects.create(id=self.course_id,
                                            name='Demo Course')
예제 #24
0
    def setUp(self):
        super(SailthruSignalTests, self).setUp()
        self.request.COOKIES['sailthru_bid'] = CAMPAIGN_COOKIE
        self.user = UserFactory.create(username='******', email=TEST_EMAIL)

        toggle_switch('sailthru_enable', True)
        self.site_configuration.enable_sailthru = True
        self.site_configuration.save()

        # create some test course objects
        self.course_id = 'edX/toy/2012_Fall'
        self.course_url = 'http://lms.testserver.fake/courses/edX/toy/2012_Fall/info'
        self.course = Course.objects.create(id=self.course_id, name='Demo Course')

        self.basket_attribute_type, __ = BasketAttributeType.objects.get_or_create(name=SAILTHRU_CAMPAIGN)
예제 #25
0
    def test_create_with_lines_with_existing_refund(self, refund_status, refund_created):
        """
        Refund.create_with_lines should not create RefundLines for order lines
        which have already been refunded.
        """
        order = self.create_order(user=UserFactory())
        line = order.lines.first()
        RefundLineFactory(order_line=line, status=refund_status)

        with LogCapture(LOGGER_NAME) as l:
            refund = Refund.create_with_lines(order, [line])
            self.assertEqual(isinstance(refund, Refund), refund_created)
            if refund_created:
                self.assert_refund_creation_logged(l, refund, order)
            else:
                l.check()
예제 #26
0
    def test_has_permission_superuser(self):
        """ Return True if request.user is a superuser. """
        user = UserFactory(is_superuser=True)

        # Data is required, even if you're a superuser.
        request = self._get_request(user=user)
        self.assertFalse(self.permissions_class.has_permission(request, None))

        # Superusers can create their own refunds
        request = self._get_request(data={'username': user.username},
                                    user=user)
        self.assertTrue(self.permissions_class.has_permission(request, None))

        # Superusers can create refunds for other users
        request = self._get_request(data={'username': '******'}, user=user)
        self.assertTrue(self.permissions_class.has_permission(request, None))
예제 #27
0
    def setUp(self):
        super(SailthruSignalTests, self).setUp()
        self.request_factory = RequestFactory()
        self.request = self.request_factory.get("foo")
        self.request.COOKIES['sailthru_bid'] = CAMPAIGN_COOKIE
        self.request.site = self.site
        self.user = UserFactory.create(username='******', email=TEST_EMAIL)

        toggle_switch('sailthru_enable', True)

        # create some test course objects
        self.course_id = 'edX/toy/2012_Fall'
        self.course_url = 'http://lms.testserver.fake/courses/edX/toy/2012_Fall/info'
        self.course = Course.objects.create(id=self.course_id, name='Demo Course')

        self.basket_attribute_type, __ = BasketAttributeType.objects.get_or_create(name=SAILTHRU_CAMPAIGN)
예제 #28
0
 def setUp(self):
     super(EntitlementFulfillmentModuleTests, self).setUp()
     self.user = UserFactory()
     self.course_entitlement = create_or_update_course_entitlement(
         'verified', 100, self.partner, '111-222-333-444',
         'Course Entitlement')
     basket = BasketFactory(owner=self.user, site=self.site)
     basket.add_product(self.course_entitlement, 1)
     self.entitlement_option = Option.objects.get(name='Course Entitlement')
     self.order = create_order(number=1, basket=basket, user=self.user)
     self.logger_name = 'ecommerce.extensions.fulfillment.modules'
     self.return_data = {
         "user": "******",
         "course_uuid": "3b3123b8-d34b-44d8-9bbb-a12676e97123",
         "uuid": "111-222-333",
         "mode": "verified",
         "expired_at": "None"
     }
예제 #29
0
    def test_create_with_lines(self, multiple_lines):
        """
        Given an order and order lines that have not been refunded, Refund.create_with_lines
        should create a Refund with corresponding RefundLines.
        """
        order = self.create_order(user=UserFactory(),
                                  multiple_lines=multiple_lines)

        with LogCapture(LOGGER_NAME) as l:
            refund = Refund.create_with_lines(order, list(order.lines.all()))

            l.check((
                LOGGER_NAME, 'INFO',
                'refund_created: amount="{}", currency="{}", order_number="{}", '
                'refund_id="{}", user_id="{}"'.format(
                    refund.total_credit_excl_tax, refund.currency,
                    order.number, refund.id, refund.user.id)))

        self.assert_refund_matches_order(refund, order)
예제 #30
0
    def handle(self, *args, **options):
        orders = options['orders']
        sku = options['sku']

        try:
            stock_record = StockRecord.objects.get(partner_sku=sku)
            product = stock_record.product
            partner = stock_record.partner
        except StockRecord.DoesNotExist:
            msg = 'No StockRecord for partner_sku {} exists.'.format(sku)
            logger.exception(msg)
            raise CommandError(msg)

        try:
            site = partner.siteconfiguration.site
        except SiteConfiguration.DoesNotExist:
            msg = 'No Site Configuration exists for partner {}!'.format(
                partner.id)
            logger.exception(msg)
            raise CommandError(msg)

        user = UserFactory()

        for __ in range(orders):
            basket = create_basket(user, product, site)

            shipping_method = Free()
            shipping_charge = shipping_method.calculate(basket)
            total = OrderTotalCalculator().calculate(basket, shipping_charge)
            number = OrderNumberGenerator().order_number(basket)
            with transaction.atomic():
                OrderCreator().place_order(order_number=number,
                                           user=user,
                                           basket=basket,
                                           shipping_address=None,
                                           shipping_method=shipping_method,
                                           shipping_charge=shipping_charge,
                                           billing_address=None,
                                           total=total)

                basket.set_as_submitted()
예제 #31
0
    def setUp(self):
        user = UserFactory()
        self.product_class = factories.ProductClassFactory(
            name='Seat', requires_shipping=False, track_stock=False
        )

        self.course = factories.ProductFactory(
            structure='parent', upc='001', title='EdX DemoX Course', product_class=self.product_class
        )
        self.seat = factories.ProductFactory(
            structure='child',
            upc='002',
            title='Seat in EdX DemoX Course with Honor Certificate',
            product_class=None,
            parent=self.course
        )
        for stock_record in self.seat.stockrecords.all():
            stock_record.price_currency = 'USD'
            stock_record.save()

        basket = BasketFactory()
        basket.add_product(self.seat, 1)
        self.order = factories.create_order(number=1, basket=basket, user=user)
예제 #32
0
    def setUp(self):
        super(EdxOrderPlacementMixinTests, self).setUp()

        self.user = UserFactory()
        self.order = self.create_order(status=ORDER.OPEN)
예제 #33
0
class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin,
                                  PaymentEventsMixin, RefundTestMixin,
                                  TestCase):
    """
    Tests validating generic behaviors of the EdxOrderPlacementMixin.
    """
    def setUp(self):
        super(EdxOrderPlacementMixinTests, self).setUp()

        self.user = UserFactory()
        self.order = self.create_order(status=ORDER.OPEN)

    def test_handle_payment_logging(self, __):
        """
        Ensure that we emit a log entry upon receipt of a payment notification, and create Source and PaymentEvent
        objects.
        """
        user = factories.UserFactory()
        basket = factories.create_basket()
        basket.owner = user
        basket.save()

        mixin = EdxOrderPlacementMixin()
        mixin.payment_processor = DummyProcessor(self.site)
        processor_name = DummyProcessor.NAME
        total = basket.total_incl_tax
        reference = basket.id

        with LogCapture(LOGGER_NAME) as l:
            mixin.handle_payment({}, basket)
            l.check((
                LOGGER_NAME, 'INFO',
                'payment_received: amount="{}", basket_id="{}", currency="{}", '
                'processor_name="{}", reference="{}", user_id="{}"'.format(
                    total, basket.id, basket.currency, processor_name,
                    reference, user.id)))

        # pylint: disable=protected-access

        # Validate a payment Source was created
        source_type = SourceType.objects.get(code=processor_name)
        label = user.username
        self.assert_basket_matches_source(basket, mixin._payment_sources[-1],
                                          source_type, reference, label)

        # Validate the PaymentEvent was created
        paid_type = PaymentEventType.objects.get(code='paid')
        self.assert_valid_payment_event_fields(mixin._payment_events[-1],
                                               total, paid_type,
                                               processor_name, reference)

    def test_handle_successful_order(self, mock_track):
        """
        Ensure that tracking events are fired with correct content when order
        placement event handling is invoked.
        """
        tracking_context = {
            'lms_user_id': 'test-user-id',
            'lms_client_id': 'test-client-id',
            'lms_ip': '127.0.0.1'
        }
        self.user.tracking_context = tracking_context
        self.user.save()

        with LogCapture(LOGGER_NAME) as l:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            # ensure event is being tracked
            self.assertTrue(mock_track.called)
            # ensure event data is correct
            self.assert_correct_event(mock_track, self.order,
                                      tracking_context['lms_user_id'],
                                      tracking_context['lms_client_id'],
                                      tracking_context['lms_ip'],
                                      self.order.number, self.order.currency,
                                      self.order.total_excl_tax)
            l.check((
                LOGGER_NAME, 'INFO',
                'order_placed: amount="{}", basket_id="{}", contains_coupon="{}", currency="{}",'
                ' order_number="{}", user_id="{}"'.format(
                    self.order.total_excl_tax, self.order.basket.id,
                    self.order.contains_coupon, self.order.currency,
                    self.order.number, self.order.user.id)))

    def test_handle_successful_free_order(self, mock_track):
        """Verify that tracking events are not emitted for free orders."""
        order = self.create_order(free=True, status=ORDER.OPEN)
        EdxOrderPlacementMixin().handle_successful_order(order)

        # Verify that no event was emitted.
        self.assertFalse(mock_track.called)

    def test_handle_successful_order_no_context(self, mock_track):
        """
        Ensure that expected values are substituted when no tracking_context
        was available.
        """
        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure event is being tracked
        self.assertTrue(mock_track.called)
        # ensure event data is correct
        self.assert_correct_event(mock_track, self.order,
                                  'ecommerce-{}'.format(self.user.id), None,
                                  None, self.order.number, self.order.currency,
                                  self.order.total_excl_tax)

    def test_handle_successful_order_no_segment_key(self, mock_track):
        """
        Ensure that tracking events do not fire when there is no Segment key
        configured.
        """
        self.site.siteconfiguration.segment_key = None
        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure no event was fired
        self.assertFalse(mock_track.called)

    def test_handle_successful_order_segment_error(self, mock_track):
        """
        Ensure that exceptions raised while emitting tracking events are
        logged, but do not otherwise interrupt program flow.
        """
        with patch('ecommerce.extensions.analytics.utils.logger.exception'
                   ) as mock_log_exc:
            mock_track.side_effect = Exception("clunk")
            EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure that analytics.track was called, but the exception was caught
        self.assertTrue(mock_track.called)
        # ensure we logged a warning.
        self.assertTrue(
            mock_log_exc.called_with(
                "Failed to emit tracking event upon order placement."))

    def test_handle_successful_async_order(self, __):
        """
        Verify that a Waffle Sample can be used to control async order fulfillment.
        """
        sample, created = Sample.objects.get_or_create(
            name='async_order_fulfillment',
            defaults={
                'percent':
                100.0,
                'note':
                'Determines what percentage of orders are fulfilled asynchronously.',
            })

        if not created:
            sample.percent = 100.0
            sample.save()

        with patch('ecommerce.extensions.checkout.mixins.fulfill_order.delay'
                   ) as mock_delay:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            self.assertTrue(mock_delay.called)
            mock_delay.assert_called_once_with(
                self.order.number, site_code=self.partner.short_code)

    def test_place_free_order(self, __):
        """ Verify an order is placed and the basket is submitted. """
        basket = BasketFactory(owner=self.user, site=self.site)
        basket.add_product(ProductFactory(stockrecords__price_excl_tax=0))
        order = EdxOrderPlacementMixin().place_free_order(basket)

        self.assertIsNotNone(order)
        self.assertEqual(basket.status, Basket.SUBMITTED)

    def test_non_free_basket_order(self, __):
        """ Verify an error is raised for non-free basket. """
        basket = BasketFactory(owner=self.user, site=self.site)
        basket.add_product(ProductFactory(stockrecords__price_excl_tax=10))

        with self.assertRaises(BasketNotFreeError):
            EdxOrderPlacementMixin().place_free_order(basket)

    def test_send_confirmation_message(self, __):
        """
        Verify the send confirmation message override functions as expected
        """
        request = RequestFactory()
        user = self.create_user()
        user.email = '*****@*****.**'
        request.user = user
        site_from_email = '*****@*****.**'
        site_configuration = SiteConfigurationFactory(
            partner__name='Tester', from_email=site_from_email)
        request.site = site_configuration.site
        order = factories.create_order()
        order.user = user
        mixin = EdxOrderPlacementMixin()
        mixin.request = request

        # Happy path
        mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
        self.assertEqual(mail.outbox[0].from_email, site_from_email)
        mail.outbox = []

        # Invalid code path (graceful exit)
        mixin.send_confirmation_message(order, 'INVALID_CODE', request.site)
        self.assertEqual(len(mail.outbox), 0)

        # Invalid messages container path (graceful exit)
        with patch(
                'ecommerce.extensions.checkout.mixins.CommunicationEventType.objects.get'
        ) as mock_get:
            mock_event_type = Mock()
            mock_event_type.get_messages.return_value = {}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED',
                                            request.site)
            self.assertEqual(len(mail.outbox), 0)

            mock_event_type.get_messages.return_value = {'body': None}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED',
                                            request.site)
            self.assertEqual(len(mail.outbox), 0)
예제 #34
0
 def setUp(self):
     user = UserFactory()
     product = create_product()
     self.alert = ProductAlert.objects.create(user=user, product=product)
예제 #35
0
    def setUp(self):
        super(EdxOrderPlacementMixinTests, self).setUp()

        self.user = UserFactory()
        self.order = self.create_order(status=ORDER.OPEN)
예제 #36
0
class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, RefundTestMixin, TestCase):
    """
    Tests validating generic behaviors of the EdxOrderPlacementMixin.
    """

    def setUp(self):
        super(EdxOrderPlacementMixinTests, self).setUp()

        self.user = UserFactory()
        self.order = self.create_order(status=ORDER.OPEN)

    def test_handle_payment_logging(self, __):
        """
        Ensure that we emit a log entry upon receipt of a payment notification.
        """
        amount = Decimal('9.99')
        basket_id = 'test-basket-id'
        currency = 'USD'
        processor_name = 'test-processor-name'
        reference = 'test-reference'
        user_id = '1'

        mock_source = Mock(currency=currency)
        mock_payment_event = Mock(
            amount=amount,
            processor_name=processor_name,
            reference=reference
        )
        mock_handle_processor_response = Mock(return_value=(mock_source, mock_payment_event))
        mock_payment_processor = Mock(handle_processor_response=mock_handle_processor_response)

        with patch('ecommerce.extensions.checkout.mixins.EdxOrderPlacementMixin.payment_processor',
                   mock_payment_processor):
            mock_basket = Mock(id=basket_id, owner=Mock(id=user_id))
            with LogCapture(LOGGER_NAME) as l:
                EdxOrderPlacementMixin().handle_payment(Mock(), mock_basket)
                l.check(
                    (
                        LOGGER_NAME,
                        'INFO',
                        'payment_received: amount="{}", basket_id="{}", currency="{}", '
                        'processor_name="{}", reference="{}", user_id="{}"'.format(
                            amount,
                            basket_id,
                            currency,
                            processor_name,
                            reference,
                            user_id
                        )
                    )
                )

    def test_handle_successful_order(self, mock_track):
        """
        Ensure that tracking events are fired with correct content when order
        placement event handling is invoked.
        """
        tracking_context = {'lms_user_id': 'test-user-id', 'lms_client_id': 'test-client-id', 'lms_ip': '127.0.0.1'}
        self.user.tracking_context = tracking_context
        self.user.save()

        with LogCapture(LOGGER_NAME) as l:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            # ensure event is being tracked
            self.assertTrue(mock_track.called)
            # ensure event data is correct
            self.assert_correct_event(
                mock_track,
                self.order,
                tracking_context['lms_user_id'],
                tracking_context['lms_client_id'],
                tracking_context['lms_ip'],
                self.order.number,
                self.order.currency,
                self.order.total_excl_tax
            )
            l.check(
                (
                    LOGGER_NAME,
                    'INFO',
                    'order_placed: amount="{}", basket_id="{}", currency="{}", order_number="{}", user_id="{}"'.format(
                        self.order.total_excl_tax,
                        self.order.basket.id,
                        self.order.currency,
                        self.order.number,
                        self.order.user.id
                    )
                )
            )

    def test_handle_successful_free_order(self, mock_track):
        """Verify that tracking events are not emitted for free orders."""
        order = self.create_order(free=True, status=ORDER.OPEN)
        EdxOrderPlacementMixin().handle_successful_order(order)

        # Verify that no event was emitted.
        self.assertFalse(mock_track.called)

    def test_handle_successful_order_no_context(self, mock_track):
        """
        Ensure that expected values are substituted when no tracking_context
        was available.
        """
        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure event is being tracked
        self.assertTrue(mock_track.called)
        # ensure event data is correct
        self.assert_correct_event(
            mock_track,
            self.order,
            'ecommerce-{}'.format(self.user.id),
            None,
            None,
            self.order.number,
            self.order.currency,
            self.order.total_excl_tax
        )

    @override_settings(SEGMENT_KEY=None)
    def test_handle_successful_order_no_segment_key(self, mock_track):
        """
        Ensure that tracking events do not fire when there is no Segment key
        configured.
        """
        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure no event was fired
        self.assertFalse(mock_track.called)

    def test_handle_successful_order_segment_error(self, mock_track):
        """
        Ensure that exceptions raised while emitting tracking events are
        logged, but do not otherwise interrupt program flow.
        """
        with patch('ecommerce.extensions.analytics.utils.logger.exception') as mock_log_exc:
            mock_track.side_effect = Exception("clunk")
            EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure that analytics.track was called, but the exception was caught
        self.assertTrue(mock_track.called)
        # ensure we logged a warning.
        self.assertTrue(mock_log_exc.called_with("Failed to emit tracking event upon order placement."))
예제 #37
0
class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, RefundTestMixin, TestCase):
    """
    Tests validating generic behaviors of the EdxOrderPlacementMixin.
    """

    def setUp(self):
        super(EdxOrderPlacementMixinTests, self).setUp()

        self.user = UserFactory()
        self.order = self.create_order(status=ORDER.OPEN)

    def test_handle_payment_logging(self, __):
        """
        Ensure that we emit a log entry upon receipt of a payment notification.
        """
        amount = Decimal('9.99')
        basket_id = 'test-basket-id'
        currency = 'USD'
        processor_name = 'test-processor-name'
        reference = 'test-reference'
        user_id = '1'

        mock_source = Mock(currency=currency)
        mock_payment_event = Mock(
            amount=amount,
            processor_name=processor_name,
            reference=reference
        )
        mock_handle_processor_response = Mock(return_value=(mock_source, mock_payment_event))
        mock_payment_processor = Mock(handle_processor_response=mock_handle_processor_response)

        with patch('ecommerce.extensions.checkout.mixins.EdxOrderPlacementMixin.payment_processor',
                   mock_payment_processor):
            mock_basket = Mock(id=basket_id, owner=Mock(id=user_id))
            with LogCapture(LOGGER_NAME) as l:
                EdxOrderPlacementMixin().handle_payment(Mock(), mock_basket)
                l.check(
                    (
                        LOGGER_NAME,
                        'INFO',
                        'payment_received: amount="{}", basket_id="{}", currency="{}", '
                        'processor_name="{}", reference="{}", user_id="{}"'.format(
                            amount,
                            basket_id,
                            currency,
                            processor_name,
                            reference,
                            user_id
                        )
                    )
                )

    def test_handle_successful_order(self, mock_track):
        """
        Ensure that tracking events are fired with correct content when order
        placement event handling is invoked.
        """
        tracking_context = {'lms_user_id': 'test-user-id', 'lms_client_id': 'test-client-id', 'lms_ip': '127.0.0.1'}
        self.user.tracking_context = tracking_context
        self.user.save()

        with LogCapture(LOGGER_NAME) as l:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            # ensure event is being tracked
            self.assertTrue(mock_track.called)
            # ensure event data is correct
            self.assert_correct_event(
                mock_track,
                self.order,
                tracking_context['lms_user_id'],
                tracking_context['lms_client_id'],
                tracking_context['lms_ip'],
                self.order.number,
                self.order.currency,
                self.order.total_excl_tax
            )
            l.check(
                (
                    LOGGER_NAME,
                    'INFO',
                    'order_placed: amount="{}", basket_id="{}", contains_coupon="{}", currency="{}",'
                    ' order_number="{}", user_id="{}"'.format(
                        self.order.total_excl_tax,
                        self.order.basket.id,
                        self.order.contains_coupon,
                        self.order.currency,
                        self.order.number,
                        self.order.user.id
                    )
                )
            )

    def test_handle_successful_free_order(self, mock_track):
        """Verify that tracking events are not emitted for free orders."""
        order = self.create_order(free=True, status=ORDER.OPEN)
        EdxOrderPlacementMixin().handle_successful_order(order)

        # Verify that no event was emitted.
        self.assertFalse(mock_track.called)

    def test_handle_successful_order_no_context(self, mock_track):
        """
        Ensure that expected values are substituted when no tracking_context
        was available.
        """
        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure event is being tracked
        self.assertTrue(mock_track.called)
        # ensure event data is correct
        self.assert_correct_event(
            mock_track,
            self.order,
            'ecommerce-{}'.format(self.user.id),
            None,
            None,
            self.order.number,
            self.order.currency,
            self.order.total_excl_tax
        )

    def test_handle_successful_order_no_segment_key(self, mock_track):
        """
        Ensure that tracking events do not fire when there is no Segment key
        configured.
        """
        self.site.siteconfiguration.segment_key = None
        EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure no event was fired
        self.assertFalse(mock_track.called)

    def test_handle_successful_order_segment_error(self, mock_track):
        """
        Ensure that exceptions raised while emitting tracking events are
        logged, but do not otherwise interrupt program flow.
        """
        with patch('ecommerce.extensions.analytics.utils.logger.exception') as mock_log_exc:
            mock_track.side_effect = Exception("clunk")
            EdxOrderPlacementMixin().handle_successful_order(self.order)
        # ensure that analytics.track was called, but the exception was caught
        self.assertTrue(mock_track.called)
        # ensure we logged a warning.
        self.assertTrue(mock_log_exc.called_with("Failed to emit tracking event upon order placement."))

    def test_handle_successful_async_order(self, __):
        """
        Verify that a Waffle Sample can be used to control async order fulfillment.
        """
        sample, created = Sample.objects.get_or_create(
            name='async_order_fulfillment',
            defaults={
                'percent': 100.0,
                'note': 'Determines what percentage of orders are fulfilled asynchronously.',
            }
        )

        if not created:
            sample.percent = 100.0
            sample.save()

        with patch('ecommerce.extensions.checkout.mixins.fulfill_order.delay') as mock_delay:
            EdxOrderPlacementMixin().handle_successful_order(self.order)
            self.assertTrue(mock_delay.called)

    def test_place_free_order(self, __):
        """ Verify an order is placed and the basket is submitted. """
        basket = BasketFactory(owner=self.user, site=self.site)
        basket.add_product(ProductFactory(stockrecords__price_excl_tax=0))
        order = EdxOrderPlacementMixin().place_free_order(basket)

        self.assertIsNotNone(order)
        self.assertEqual(basket.status, Basket.SUBMITTED)

    def test_non_free_basket_order(self, __):
        """ Verify an error is raised for non-free basket. """
        basket = BasketFactory(owner=self.user, site=self.site)
        basket.add_product(ProductFactory(stockrecords__price_excl_tax=10))

        with self.assertRaises(BasketNotFreeError):
            EdxOrderPlacementMixin().place_free_order(basket)

    def test_send_confirmation_message(self, __):
        """
        Verify the send confirmation message override functions as expected
        """
        request = RequestFactory()
        user = self.create_user()
        user.email = '*****@*****.**'
        request.user = user
        site_from_email = '*****@*****.**'
        site_configuration = SiteConfigurationFactory(partner__name='Tester', from_email=site_from_email)
        request.site = site_configuration.site
        order = factories.create_order()
        order.user = user
        mixin = EdxOrderPlacementMixin()
        mixin.request = request

        # Happy path
        mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
        self.assertEqual(mail.outbox[0].from_email, site_from_email)
        mail.outbox = []

        # Invalid code path (graceful exit)
        mixin.send_confirmation_message(order, 'INVALID_CODE', request.site)
        self.assertEqual(len(mail.outbox), 0)

        # Invalid messages container path (graceful exit)
        with patch('ecommerce.extensions.checkout.mixins.CommunicationEventType.objects.get') as mock_get:
            mock_event_type = Mock()
            mock_event_type.get_messages.return_value = {}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
            self.assertEqual(len(mail.outbox), 0)

            mock_event_type.get_messages.return_value = {'body': None}
            mock_get.return_value = mock_event_type
            mixin.send_confirmation_message(order, 'ORDER_PLACED', request.site)
            self.assertEqual(len(mail.outbox), 0)