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 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)
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)
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})
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)
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)
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)
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 )
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)) ) )
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))))
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': '*****@*****.**' }
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)) ) )
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)
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)
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)
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))
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)
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)
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()
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)
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')
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)
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()
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))
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)
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" }
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)
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()
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)
def setUp(self): super(EdxOrderPlacementMixinTests, self).setUp() self.user = UserFactory() self.order = self.create_order(status=ORDER.OPEN)
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)
def setUp(self): user = UserFactory() product = create_product() self.alert = ProductAlert.objects.create(user=user, product=product)
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."))
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)