Commit 35efdd39 by Waheed Ahmed

Use GA clientId from cookie on all analytics events instead of DB.

LEARNER-2596
parent 0e07009e
"""
Middleware for analytics app to parse GA cookie.
"""
from ecommerce.extensions.analytics.utils import get_google_analytics_client_id
class TrackingMiddleware(object):
"""
Middleware that parse `_ga` cookie and save/update in user tracking context.
"""
def process_request(self, request):
user = request.user
if user.is_authenticated():
tracking_context = user.tracking_context or {}
old_client_id = tracking_context.get('ga_client_id')
ga_client_id = get_google_analytics_client_id(request)
if ga_client_id and ga_client_id != old_client_id:
tracking_context['ga_client_id'] = ga_client_id
user.tracking_context = tracking_context
user.save()
from django.test.client import RequestFactory
from ecommerce.extensions.analytics import middleware
from ecommerce.tests.testcases import TestCase
class TrackingMiddlewareTests(TestCase):
""" Test for TrackingMiddleware. """
def setUp(self):
super(TrackingMiddlewareTests, self).setUp()
self.middleware = middleware.TrackingMiddleware()
self.request_factory = RequestFactory()
self.user = self.create_user()
def _assert_ga_client_id(self, ga_client_id):
self.request_factory.cookies['_ga'] = 'GA1.2.{}'.format(ga_client_id)
request = self.request_factory.get('/')
request.user = self.user
self.middleware.process_request(request)
expected_client_id = self.user.tracking_context.get('ga_client_id')
self.assertEqual(ga_client_id, expected_client_id)
def test_process_request(self):
""" Test that middleware save/update GA client id in user tracking context. """
self.assertIsNone(self.user.tracking_context)
self._assert_ga_client_id('test-client-id')
updated_client_id = 'updated-client-id'
self.assertNotEqual(updated_client_id, self.user.tracking_context.get('ga_client_id'))
self._assert_ga_client_id(updated_client_id)
...@@ -51,18 +51,18 @@ class UtilsTest(DiscoveryTestMixin, BasketMixin, TestCase): ...@@ -51,18 +51,18 @@ class UtilsTest(DiscoveryTestMixin, BasketMixin, TestCase):
def test_parse_tracking_context(self): def test_parse_tracking_context(self):
""" The method should parse the tracking context on the User object. """ """ The method should parse the tracking context on the User object. """
tracking_context = { tracking_context = {
'ga_client_id': 'test-client-id',
'lms_user_id': 'foo', 'lms_user_id': 'foo',
'lms_client_id': 'bar',
'lms_ip': '18.0.0.1', 'lms_ip': '18.0.0.1',
} }
user = self.create_user(tracking_context=tracking_context) user = self.create_user(tracking_context=tracking_context)
expected = (tracking_context['lms_user_id'], tracking_context['lms_client_id'], tracking_context['lms_ip']) expected = (tracking_context['lms_user_id'], tracking_context['ga_client_id'], tracking_context['lms_ip'])
self.assertEqual(parse_tracking_context(user), expected) self.assertEqual(parse_tracking_context(user), expected)
# If no LMS user ID is provided, we should create one based on the E-Commerce ID # If no LMS user ID is provided, we should create one based on the E-Commerce ID
del tracking_context['lms_user_id'] del tracking_context['lms_user_id']
user = self.create_user(tracking_context=tracking_context) user = self.create_user(tracking_context=tracking_context)
expected = ('ecommerce-{}'.format(user.id), tracking_context['lms_client_id'], tracking_context['lms_ip']) expected = ('ecommerce-{}'.format(user.id), tracking_context['ga_client_id'], tracking_context['lms_ip'])
self.assertEqual(parse_tracking_context(user), expected) self.assertEqual(parse_tracking_context(user), expected)
def test_track_segment_event_without_segment_key(self): def test_track_segment_event_without_segment_key(self):
...@@ -76,33 +76,29 @@ class UtilsTest(DiscoveryTestMixin, BasketMixin, TestCase): ...@@ -76,33 +76,29 @@ class UtilsTest(DiscoveryTestMixin, BasketMixin, TestCase):
self.assertEqual(track_segment_event(self.site, self.create_user(), 'foo', {}), (False, msg)) self.assertEqual(track_segment_event(self.site, self.create_user(), 'foo', {}), (False, msg))
mock_debug.assert_called_with(msg) mock_debug.assert_called_with(msg)
@ddt.data('1033501218.1368477899', None) def test_track_segment_event(self):
def test_track_segment_event(self, ga_client_id):
""" The function should fire an event to Segment if the site is properly configured. """ """ The function should fire an event to Segment if the site is properly configured. """
properties = {'key': 'value'} properties = {'key': 'value'}
self.site_configuration.segment_key = 'fake-key' self.site_configuration.segment_key = 'fake-key'
self.site_configuration.save() self.site_configuration.save()
user = self.create_user( user = self.create_user(
tracking_context={ tracking_context={
'ga_client_id': 'test-client-id',
'lms_user_id': 'foo', 'lms_user_id': 'foo',
'lms_client_id': 'bar',
'lms_ip': '18.0.0.1', 'lms_ip': '18.0.0.1',
} }
) )
user_tracking_id, lms_client_id, lms_ip = parse_tracking_context(user) user_tracking_id, ga_client_id, lms_ip = parse_tracking_context(user)
context = { context = {
'ip': lms_ip, 'ip': lms_ip,
'Google Analytics': { 'Google Analytics': {
'clientId': ga_client_id if ga_client_id else lms_client_id 'clientId': ga_client_id
} }
} }
event = 'foo' event = 'foo'
with mock.patch.object(Client, 'track') as mock_track: with mock.patch.object(Client, 'track') as mock_track:
if ga_client_id: track_segment_event(self.site, user, event, properties)
track_segment_event(self.site, user, event, properties, ga_client_id=ga_client_id)
else:
track_segment_event(self.site, user, event, properties)
mock_track.assert_called_once_with(user_tracking_id, event, properties, context=context) mock_track.assert_called_once_with(user_tracking_id, event, properties, context=context)
def test_translate_basket_line_for_segment(self): def test_translate_basket_line_for_segment(self):
...@@ -146,13 +142,13 @@ class UtilsTest(DiscoveryTestMixin, BasketMixin, TestCase): ...@@ -146,13 +142,13 @@ class UtilsTest(DiscoveryTestMixin, BasketMixin, TestCase):
def test_get_google_analytics_client_id(self): def test_get_google_analytics_client_id(self):
""" Test that method return's the GA clientId. """ """ Test that method return's the GA clientId. """
client_id = '1033501218.1368477899'
request_factory = RequestFactory()
request_factory.cookies['_ga'] = 'GA1.2.{}'.format(client_id)
request = request_factory.get('/')
expected_client_id = get_google_analytics_client_id(None) expected_client_id = get_google_analytics_client_id(None)
self.assertIsNone(expected_client_id) self.assertIsNone(expected_client_id)
ga_client_id = 'test-client-id'
request_factory = RequestFactory()
request_factory.cookies['_ga'] = 'GA1.2.{}'.format(ga_client_id)
request = request_factory.get('/')
expected_client_id = get_google_analytics_client_id(request) expected_client_id = get_google_analytics_client_id(request)
self.assertEqual(client_id, expected_client_id) self.assertEqual(ga_client_id, expected_client_id)
...@@ -14,7 +14,7 @@ def parse_tracking_context(user): ...@@ -14,7 +14,7 @@ def parse_tracking_context(user):
user (User): An instance of the User model. user (User): An instance of the User model.
Returns: Returns:
Tuple of strings: user_tracking_id, lms_client_id, lms_ip Tuple of strings: user_tracking_id, ga_client_id, lms_ip
""" """
tracking_context = user.tracking_context or {} tracking_context = user.tracking_context or {}
...@@ -26,10 +26,10 @@ def parse_tracking_context(user): ...@@ -26,10 +26,10 @@ def parse_tracking_context(user):
# at some point. # at some point.
user_tracking_id = 'ecommerce-{}'.format(user.id) user_tracking_id = 'ecommerce-{}'.format(user.id)
lms_client_id = tracking_context.get('lms_client_id')
lms_ip = tracking_context.get('lms_ip') lms_ip = tracking_context.get('lms_ip')
ga_client_id = tracking_context.get('ga_client_id')
return user_tracking_id, lms_client_id, lms_ip return user_tracking_id, ga_client_id, lms_ip
def silence_exceptions(msg): def silence_exceptions(msg):
...@@ -118,7 +118,7 @@ def prepare_analytics_data(user, segment_key): ...@@ -118,7 +118,7 @@ def prepare_analytics_data(user, segment_key):
return json.dumps(data) return json.dumps(data)
def track_segment_event(site, user, event, properties, ga_client_id=None): def track_segment_event(site, user, event, properties):
""" Fire a tracking event via Segment. """ Fire a tracking event via Segment.
Args: Args:
...@@ -126,7 +126,6 @@ def track_segment_event(site, user, event, properties, ga_client_id=None): ...@@ -126,7 +126,6 @@ def track_segment_event(site, user, event, properties, ga_client_id=None):
user (User): User to which the event should be associated. user (User): User to which the event should be associated.
event (str): Event name. event (str): Event name.
properties (dict): Event properties. properties (dict): Event properties.
ga_client_id (str): Google Analytics clientId.
Returns: Returns:
(success, msg): Tuple indicating the success of enqueuing the event on the message queue. (success, msg): Tuple indicating the success of enqueuing the event on the message queue.
...@@ -139,11 +138,11 @@ def track_segment_event(site, user, event, properties, ga_client_id=None): ...@@ -139,11 +138,11 @@ def track_segment_event(site, user, event, properties, ga_client_id=None):
logger.debug(msg) logger.debug(msg)
return False, msg return False, msg
user_tracking_id, lms_client_id, lms_ip = parse_tracking_context(user) user_tracking_id, ga_client_id, lms_ip = parse_tracking_context(user)
context = { context = {
'ip': lms_ip, 'ip': lms_ip,
'Google Analytics': { 'Google Analytics': {
'clientId': ga_client_id if ga_client_id else lms_client_id 'clientId': ga_client_id
} }
} }
return site.siteconfiguration.segment_client.track(user_tracking_id, event, properties, context=context) return site.siteconfiguration.segment_client.track(user_tracking_id, event, properties, context=context)
......
...@@ -123,11 +123,11 @@ class BasketTests(CatalogMixin, BasketMixin, TestCase): ...@@ -123,11 +123,11 @@ class BasketTests(CatalogMixin, BasketMixin, TestCase):
basket.add_product(seat) basket.add_product(seat)
properties = translate_basket_line_for_segment(basket.lines.first()) properties = translate_basket_line_for_segment(basket.lines.first())
user_tracking_id, lms_client_id, lms_ip = parse_tracking_context(basket.owner) user_tracking_id, ga_client_id, lms_ip = parse_tracking_context(basket.owner)
context = { context = {
'ip': lms_ip, 'ip': lms_ip,
'Google Analytics': { 'Google Analytics': {
'clientId': lms_client_id 'clientId': ga_client_id
} }
} }
......
...@@ -5,11 +5,7 @@ from django.dispatch import receiver ...@@ -5,11 +5,7 @@ from django.dispatch import receiver
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
from ecommerce.courses.utils import mode_for_seat from ecommerce.courses.utils import mode_for_seat
from ecommerce.extensions.analytics.utils import ( from ecommerce.extensions.analytics.utils import silence_exceptions, track_segment_event
get_google_analytics_client_id,
silence_exceptions,
track_segment_event
)
from ecommerce.extensions.checkout.utils import get_credit_provider_details, get_receipt_page_url from ecommerce.extensions.checkout.utils import get_credit_provider_details, get_receipt_page_url
from ecommerce.notifications.notifications import send_notification from ecommerce.notifications.notifications import send_notification
from ecommerce.programs.utils import get_program from ecommerce.programs.utils import get_program
...@@ -83,9 +79,7 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus ...@@ -83,9 +79,7 @@ def track_completed_order(sender, order=None, **kwargs): # pylint: disable=unus
except BasketAttribute.DoesNotExist: except BasketAttribute.DoesNotExist:
logger.info('There is no program or bundle associated with order number %s', order.number) logger.info('There is no program or bundle associated with order number %s', order.number)
ga_client_id = get_google_analytics_client_id(kwargs.get('request')) track_segment_event(order.site, order.user, 'Order Completed', properties)
track_segment_event(order.site, order.user, 'Order Completed', properties, ga_client_id=ga_client_id)
@receiver(post_checkout, dispatch_uid='send_completed_order_email') @receiver(post_checkout, dispatch_uid='send_completed_order_email')
......
...@@ -114,7 +114,7 @@ class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, PaymentEventsMixin, ...@@ -114,7 +114,7 @@ class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, PaymentEventsMixin,
Ensure that tracking events are fired with correct content when order Ensure that tracking events are fired with correct content when order
placement event handling is invoked. 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'} tracking_context = {'ga_client_id': 'test-client-id', 'lms_user_id': 'test-user-id', 'lms_ip': '127.0.0.1'}
self.user.tracking_context = tracking_context self.user.tracking_context = tracking_context
self.user.save() self.user.save()
...@@ -127,7 +127,7 @@ class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, PaymentEventsMixin, ...@@ -127,7 +127,7 @@ class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, PaymentEventsMixin,
mock_track, mock_track,
self.order, self.order,
tracking_context['lms_user_id'], tracking_context['lms_user_id'],
tracking_context['lms_client_id'], tracking_context['ga_client_id'],
tracking_context['lms_ip'], tracking_context['lms_ip'],
self.order.number, self.order.number,
self.order.currency, self.order.currency,
...@@ -272,16 +272,20 @@ class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, PaymentEventsMixin, ...@@ -272,16 +272,20 @@ class EdxOrderPlacementMixinTests(BusinessIntelligenceMixin, PaymentEventsMixin,
""" """
Verify the "Payment Info Entered" Segment event is fired after payment info is validated Verify the "Payment Info Entered" Segment event is fired after payment info is validated
""" """
tracking_context = {'ga_client_id': 'test-client-id', 'lms_user_id': 'test-user-id', 'lms_ip': '127.0.0.1'}
self.user.tracking_context = tracking_context
self.user.save()
basket = create_basket(owner=self.user, site=self.site) basket = create_basket(owner=self.user, site=self.site)
mixin = EdxOrderPlacementMixin() mixin = EdxOrderPlacementMixin()
mixin.payment_processor = DummyProcessor(self.site) mixin.payment_processor = DummyProcessor(self.site)
user_tracking_id, lms_client_id, lms_ip = parse_tracking_context(self.user) user_tracking_id, ga_client_id, lms_ip = parse_tracking_context(self.user)
context = { context = {
'ip': lms_ip, 'ip': lms_ip,
'Google Analytics': { 'Google Analytics': {
'clientId': lms_client_id 'clientId': ga_client_id
} }
} }
......
...@@ -3,7 +3,6 @@ import json ...@@ -3,7 +3,6 @@ import json
import httpretty import httpretty
import mock import mock
from django.core import mail from django.core import mail
from django.test.client import RequestFactory
from oscar.core.loading import get_class, get_model from oscar.core.loading import get_class, get_model
from oscar.test import factories from oscar.test import factories
from oscar.test.newfactories import BasketFactory from oscar.test.newfactories import BasketFactory
...@@ -36,10 +35,6 @@ LOGGER_NAME = 'ecommerce.extensions.checkout.signals' ...@@ -36,10 +35,6 @@ LOGGER_NAME = 'ecommerce.extensions.checkout.signals'
class SignalTests(ProgramTestMixin, CouponMixin, TestCase): class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
def setUp(self): def setUp(self):
super(SignalTests, self).setUp() super(SignalTests, self).setUp()
self.ga_client_id = '1033501218.1368477899'
request_factory = RequestFactory()
request_factory.cookies['_ga'] = 'GA1.2.{}'.format(self.ga_client_id)
self.request = request_factory.get('/')
self.user = self.create_user() self.user = self.create_user()
self.request.user = self.user self.request.user = self.user
toggle_switch('ENABLE_NOTIFICATIONS', True) toggle_switch('ENABLE_NOTIFICATIONS', True)
...@@ -178,20 +173,16 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase): ...@@ -178,20 +173,16 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
with mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') as mock_track: with mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') as mock_track:
order = self.prepare_order('verified') order = self.prepare_order('verified')
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order) properties = self._generate_event_properties(order)
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
# We should be able to fire events even if the product is not related to a course. # We should be able to fire events even if the product is not related to a course.
mock_track.reset_mock() mock_track.reset_mock()
order = create_order() order = create_order()
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order) properties = self._generate_event_properties(order)
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
@mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') @mock.patch('ecommerce.extensions.checkout.signals.track_segment_event')
def test_track_bundle_order(self, mock_track): def test_track_bundle_order(self, mock_track):
...@@ -206,21 +197,17 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase): ...@@ -206,21 +197,17 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
# Tracks a full bundle order # Tracks a full bundle order
with mock.patch('ecommerce.extensions.checkout.signals.get_program', with mock.patch('ecommerce.extensions.checkout.signals.get_program',
mock.Mock(return_value=self.mock_get_program_data(True))): mock.Mock(return_value=self.mock_get_program_data(True))):
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order, bundle_id='test_bundle', fullBundle=True) properties = self._generate_event_properties(order, bundle_id='test_bundle', fullBundle=True)
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
# Tracks a partial bundle order # Tracks a partial bundle order
with mock.patch('ecommerce.extensions.checkout.signals.get_program', with mock.patch('ecommerce.extensions.checkout.signals.get_program',
mock.Mock(return_value=self.mock_get_program_data(False))): mock.Mock(return_value=self.mock_get_program_data(False))):
mock_track.reset_mock() mock_track.reset_mock()
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order, bundle_id='test_bundle') properties = self._generate_event_properties(order, bundle_id='test_bundle')
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
def test_track_completed_discounted_order_with_voucher(self): def test_track_completed_discounted_order_with_voucher(self):
""" An event including coupon information should be sent to Segment""" """ An event including coupon information should be sent to Segment"""
...@@ -237,11 +224,9 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase): ...@@ -237,11 +224,9 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
Applicator().apply(basket, user=basket.owner, request=self.request) Applicator().apply(basket, user=basket.owner, request=self.request)
order = factories.create_order(basket=basket, user=self.user) order = factories.create_order(basket=basket, user=self.user)
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order, voucher) properties = self._generate_event_properties(order, voucher)
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
def test_track_completed_discounted_order_with_voucher_with_offer(self): def test_track_completed_discounted_order_with_voucher_with_offer(self):
with mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') as mock_track: with mock.patch('ecommerce.extensions.checkout.signals.track_segment_event') as mock_track:
...@@ -263,11 +248,9 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase): ...@@ -263,11 +248,9 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
Applicator().apply(basket, user=basket.owner, request=self.request) Applicator().apply(basket, user=basket.owner, request=self.request)
order = factories.create_order(basket=basket, user=self.user) order = factories.create_order(basket=basket, user=self.user)
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order, voucher) properties = self._generate_event_properties(order, voucher)
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
def test_track_completed_discounted_order_with_offer(self): def test_track_completed_discounted_order_with_offer(self):
""" An event including a discount but no coupon should be sent to Segment""" """ An event including a discount but no coupon should be sent to Segment"""
...@@ -287,11 +270,9 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase): ...@@ -287,11 +270,9 @@ class SignalTests(ProgramTestMixin, CouponMixin, TestCase):
Applicator().apply_offers(basket, [site_offer]) Applicator().apply_offers(basket, [site_offer])
order = factories.create_order(basket=basket, user=self.user) order = factories.create_order(basket=basket, user=self.user)
track_completed_order(None, order, request=self.request) track_completed_order(None, order)
properties = self._generate_event_properties(order) properties = self._generate_event_properties(order)
mock_track.assert_called_once_with( mock_track.assert_called_once_with(order.site, order.user, 'Order Completed', properties)
order.site, order.user, 'Order Completed', properties, ga_client_id=self.ga_client_id
)
def test_track_completed_coupon_order(self): def test_track_completed_coupon_order(self):
""" Make sure we do not send GA events for Coupon orders """ """ Make sure we do not send GA events for Coupon orders """
......
...@@ -64,6 +64,10 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -64,6 +64,10 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
super(EnrollmentFulfillmentModuleTests, self).setUp() super(EnrollmentFulfillmentModuleTests, self).setUp()
self.user = UserFactory() 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.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) self.seat = self.course.create_or_update_seat(self.certificate_type, False, 100, self.partner, self.provider)
...@@ -123,11 +127,9 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -123,11 +127,9 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
self.assertEqual(1, len(supported_lines)) self.assertEqual(1, len(supported_lines))
@httpretty.activate @httpretty.activate
@mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context') def test_enrollment_module_fulfill(self):
def test_enrollment_module_fulfill(self, parse_tracking_context):
"""Happy path test to ensure we can properly fulfill enrollments.""" """Happy path test to ensure we can properly fulfill enrollments."""
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
# Attempt to enroll. # Attempt to enroll.
with LogCapture(LOGGER_NAME) as l: with LogCapture(LOGGER_NAME) as l:
EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all())) EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
...@@ -173,23 +175,21 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -173,23 +175,21 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
} }
expected_headers = { expected_headers = {
'X-Edx-Ga-Client-Id': 'GA-123456789', 'X-Edx-Ga-Client-Id': self.user.tracking_context['ga_client_id'],
'X-Forwarded-For': '11.22.33.44', 'X-Forwarded-For': self.user.tracking_context['lms_ip'],
} }
self.assertDictContainsSubset(expected_headers, actual_headers) self.assertDictContainsSubset(expected_headers, actual_headers)
self.assertEqual(expected_body, actual_body) self.assertEqual(expected_body, actual_body)
@httpretty.activate @httpretty.activate
@mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context') def test_enrollment_module_fulfill_order_with_discount_no_voucher(self):
def test_enrollment_module_fulfill_order_with_discount_no_voucher(self, parse_tracking_context):
""" """
Test that components of the Fulfillment Module which trigger on the presence of a voucher do Test that components of the Fulfillment Module which trigger on the presence of a voucher do
not cause failures in cases where a discount does not have a voucher included not cause failures in cases where a discount does not have a voucher included
(such as with a Conditional Offer) (such as with a Conditional Offer)
""" """
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
self.create_seat_and_order(certificate_type='credit', provider='MIT') self.create_seat_and_order(certificate_type='credit', provider='MIT')
self.order.discounts.create() self.order.discounts.create()
__, lines = EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all())) __, lines = EnrollmentFulfillmentModule().fulfill_product(self.order, list(self.order.lines.all()))
...@@ -231,11 +231,9 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -231,11 +231,9 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
self.assertEqual(LINE.FULFILLMENT_SERVER_ERROR, self.order.lines.all()[0].status) self.assertEqual(LINE.FULFILLMENT_SERVER_ERROR, self.order.lines.all()[0].status)
@httpretty.activate @httpretty.activate
@mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context') def test_revoke_product(self):
def test_revoke_product(self, parse_tracking_context):
""" The method should call the Enrollment API to un-enroll the student, and return True. """ """ The method should call the Enrollment API to un-enroll the student, and return True. """
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
line = self.order.lines.first() line = self.order.lines.first()
with LogCapture(LOGGER_NAME) as l: with LogCapture(LOGGER_NAME) as l:
...@@ -271,8 +269,8 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -271,8 +269,8 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
} }
expected_headers = { expected_headers = {
'X-Edx-Ga-Client-Id': 'GA-123456789', 'X-Edx-Ga-Client-Id': self.user.tracking_context['ga_client_id'],
'X-Forwarded-For': '11.22.33.44', 'X-Forwarded-For': self.user.tracking_context['lms_ip'],
} }
self.assertDictContainsSubset(expected_headers, actual_headers) self.assertDictContainsSubset(expected_headers, actual_headers)
...@@ -402,10 +400,6 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -402,10 +400,6 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
'enrollment_attributes': [] '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 # Now call the enrollment api to send POST request to LMS and verify
# that the header of the request being sent contains the analytics # that the header of the request being sent contains the analytics
# header 'x-edx-ga-client-id'. # header 'x-edx-ga-client-id'.
...@@ -413,12 +407,12 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -413,12 +407,12 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
# not available for ecommerce tests. # not available for ecommerce tests.
try: try:
# pylint: disable=protected-access # pylint: disable=protected-access
EnrollmentFulfillmentModule()._post_to_enrollment_api(data=data, user=user) EnrollmentFulfillmentModule()._post_to_enrollment_api(data=data, user=self.user)
except ConnectionError as exp: except ConnectionError as exp:
# Check that the enrollment request object has the analytics header # Check that the enrollment request object has the analytics header
# 'x-edx-ga-client-id' and 'x-forwarded-for'. # '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-edx-ga-client-id'), self.user.tracking_context['ga_client_id'])
self.assertEqual(exp.request.headers.get('x-forwarded-for'), '11.22.33.44') self.assertEqual(exp.request.headers.get('x-forwarded-for'), self.user.tracking_context['lms_ip'])
def test_voucher_usage(self): def test_voucher_usage(self):
""" """
...@@ -428,13 +422,11 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful ...@@ -428,13 +422,11 @@ class EnrollmentFulfillmentModuleTests(ProgramTestMixin, DiscoveryTestMixin, Ful
self.assertEqual(self.order.basket.total_excl_tax, 0.00) self.assertEqual(self.order.basket.total_excl_tax, 0.00)
@httpretty.activate @httpretty.activate
@mock.patch('ecommerce.extensions.fulfillment.modules.parse_tracking_context') def test_voucher_usage_with_program(self):
def test_voucher_usage_with_program(self, parse_tracking_context):
""" """
Test that using a voucher with a program basket results in a fulfilled order. Test that using a voucher with a program basket results in a fulfilled order.
""" """
httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON) httpretty.register_uri(httpretty.POST, get_lms_enrollment_api_url(), status=200, body='{}', content_type=JSON)
parse_tracking_context.return_value = ('user_123', 'GA-123456789', '11.22.33.44')
self.create_seat_and_order(certificate_type='credit', provider='MIT') self.create_seat_and_order(certificate_type='credit', provider='MIT')
program_uuid = uuid.uuid4() program_uuid = uuid.uuid4()
self.mock_program_detail_endpoint(program_uuid, self.site_configuration.discovery_api_url) self.mock_program_detail_endpoint(program_uuid, self.site_configuration.discovery_api_url)
......
...@@ -27,7 +27,7 @@ class RefundTrackingTests(RefundTestMixin, TestCase): ...@@ -27,7 +27,7 @@ class RefundTrackingTests(RefundTestMixin, TestCase):
expected_context = { expected_context = {
'ip': tracking_context.get('lms_ip'), 'ip': tracking_context.get('lms_ip'),
'Google Analytics': { 'Google Analytics': {
'clientId': tracking_context.get('lms_client_id') 'clientId': tracking_context.get('ga_client_id')
} }
} }
self.assertEqual(kwargs['context'], expected_context) self.assertEqual(kwargs['context'], expected_context)
...@@ -44,7 +44,7 @@ class RefundTrackingTests(RefundTestMixin, TestCase): ...@@ -44,7 +44,7 @@ class RefundTrackingTests(RefundTestMixin, TestCase):
def test_successful_refund_tracking(self, mock_track): def test_successful_refund_tracking(self, mock_track):
"""Verify that a successfully placed refund is tracked when Segment is enabled.""" """Verify that a successfully placed refund is tracked when Segment is enabled."""
tracking_context = {'lms_user_id': 'test-user-id', 'lms_client_id': 'test-client-id', 'lms_ip': '127.0.0.1'} tracking_context = {'ga_client_id': 'test-client-id', 'lms_user_id': 'test-user-id', 'lms_ip': '127.0.0.1'}
self.refund.user.tracking_context = tracking_context self.refund.user.tracking_context = tracking_context
self.refund.user.save() self.refund.user.save()
self.approve(self.refund) self.approve(self.refund)
......
...@@ -224,6 +224,7 @@ MIDDLEWARE_CLASSES = ( ...@@ -224,6 +224,7 @@ MIDDLEWARE_CLASSES = (
'waffle.middleware.WaffleMiddleware', 'waffle.middleware.WaffleMiddleware',
# NOTE: The overridden BasketMiddleware relies on request.site. This middleware # NOTE: The overridden BasketMiddleware relies on request.site. This middleware
# MUST appear AFTER CurrentSiteMiddleware. # MUST appear AFTER CurrentSiteMiddleware.
'ecommerce.extensions.analytics.middleware.TrackingMiddleware',
'ecommerce.extensions.basket.middleware.BasketMiddleware', 'ecommerce.extensions.basket.middleware.BasketMiddleware',
'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware', 'django.contrib.flatpages.middleware.FlatpageFallbackMiddleware',
'social_django.middleware.SocialAuthExceptionMiddleware', 'social_django.middleware.SocialAuthExceptionMiddleware',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment