def test_evidence_event_sent(self): test_url = get_certificate_url(user_id=self.user.id, course_id=self.course_id) + '?evidence_visit=1' self.recreate_tracker() assertion = BadgeAssertion( user=self.user, course_id=self.course_id, mode='honor', data={ 'image': 'http://www.example.com/image.png', 'json': {'id': 'http://www.example.com/assertion.json'}, 'issuer': 'http://www.example.com/issuer.json', } ) assertion.save() response = self.client.get(test_url) self.assertEqual(response.status_code, 200) assert_event_matches( { 'name': 'edx.badge.assertion.evidence_visited', 'data': { 'course_id': 'testorg/run1/refundable_course', # pylint: disable=no-member 'assertion_id': assertion.id, 'assertion_json_url': 'http://www.example.com/assertion.json', 'assertion_image_url': 'http://www.example.com/image.png', 'user_id': self.user.id, 'issuer': 'http://www.example.com/issuer.json', 'enrollment_mode': 'honor', }, }, self.get_event() )
def test_social_event_sent(self): test_url = '/certificates/badge_share_tracker/{}/social_network/{}/'.format( unicode(self.course.id), self.user.username, ) self.recreate_tracker() response = self.client.get(test_url) self.assertEqual(response.status_code, 302) self.assertEqual(response['Location'], 'http://www.example.com/image.png') assert_event_matches( { 'name': 'edx.badge.assertion.shared', 'data': { 'course_id': 'testorg/run1/trackable_course', 'social_network': 'social_network', # pylint: disable=no-member 'assertion_id': self.assertion.id, 'assertion_json_url': 'http://www.example.com/assertion.json', 'assertion_image_url': 'http://www.example.com/image.png', 'user_id': self.user.id, 'issuer': 'http://www.example.com/issuer.json', 'enrollment_mode': 'honor' }, }, self.get_event() )
def test_evidence_event_sent(self): self._add_course_certificates(count=1, signatory_count=2) cert_url = get_certificate_url( user_id=self.user.id, course_id=self.course_id ) test_url = '{}?evidence_visit=1'.format(cert_url) self.recreate_tracker() assertion = BadgeAssertionFactory.create( user=self.user, course_id=self.course_id, ) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) assert_event_matches( { 'name': 'edx.badge.assertion.evidence_visited', 'data': { 'course_id': 'testorg/run1/refundable_course', 'assertion_id': assertion.id, 'assertion_json_url': 'http://www.example.com/assertion.json', 'assertion_image_url': 'http://www.example.com/image.png', 'user_id': self.user.id, 'issuer': 'http://www.example.com/issuer.json', 'enrollment_mode': 'honor', }, }, self.get_event() )
def test_success(self, course_id): middleware = TrackMiddleware() request = self.create_request( data=self.create_segmentio_event_json(data={'foo': 'bar'}, course_id=course_id), content_type='application/json' ) User.objects.create(pk=USER_ID, username=str(sentinel.username)) middleware.process_request(request) # The middleware normally emits an event, make sure it doesn't in this case. self.assert_no_events_emitted() try: response = segmentio.segmentio_event(request) self.assertEquals(response.status_code, 200) expected_event = { 'accept_language': '', 'referer': '', 'username': str(sentinel.username), 'ip': '', 'session': '', 'event_source': 'mobile', 'event_type': str(sentinel.name), 'name': str(sentinel.name), 'event': {'foo': 'bar'}, 'agent': str(sentinel.user_agent), 'page': None, 'time': datetime.strptime("2014-08-27T16:33:39.215Z", "%Y-%m-%dT%H:%M:%S.%fZ"), 'host': 'testserver', 'context': { 'application': { 'name': 'edx.mobile.android', 'version': '1.0.1', }, 'user_id': USER_ID, 'course_id': course_id, 'org_id': u'foo', 'path': ENDPOINT, 'client': { 'library': { 'name': 'test-app', 'version': 'unknown' }, 'app': { 'version': '1.0.1', }, }, 'received_at': datetime.strptime("2014-08-27T16:33:39.100Z", "%Y-%m-%dT%H:%M:%S.%fZ"), }, } finally: middleware.process_response(request, None) assert_event_matches(expected_event, self.get_event())
def assert_events_match(self, expected_events, actual_events): """ Assert that each item in the expected events sequence matches its counterpart at the same index in the actual events sequence. """ for expected_event, actual_event in zip(expected_events, actual_events): assert_event_matches( expected_event, actual_event, tolerate=EventMatchTolerates.lenient() )
def assert_events_match(self, expected_events, actual_events, in_order=True): """Assert that each actual event matches one of the expected events. Args: expected_events (List): a list of dicts representing the expected events. actual_events (List): a list of dicts that were actually recorded. in_order (bool): if True then the events must be in the same order (defaults to True). """ if in_order: for expected_event, actual_event in zip(expected_events, actual_events): assert_event_matches(expected_event, actual_event, tolerate=EventMatchTolerates.lenient()) else: for expected_event in expected_events: actual_event = next(event for event in actual_events if is_matching_event(expected_event, event)) assert_event_matches(expected_event, actual_event or {}, tolerate=EventMatchTolerates.lenient())
def test_user_track_with_middleware_and_processors(self): self.recreate_tracker() middleware = TrackMiddleware() payload = '{"foo": "bar"}' user_id = 1 request = self.request_factory.get('/event', { 'page': self.url_with_course, 'event_type': sentinel.event_type, 'event': payload }) request.user = User.objects.create(pk=user_id, username=str(sentinel.username)) request.META['REMOTE_ADDR'] = '10.0.0.1' request.META['HTTP_REFERER'] = str(sentinel.referer) request.META['HTTP_ACCEPT_LANGUAGE'] = str(sentinel.accept_language) request.META['HTTP_USER_AGENT'] = str(sentinel.user_agent) request.META['SERVER_NAME'] = 'testserver2' middleware.process_request(request) try: views.user_track(request) expected_event = { 'accept_language': str(sentinel.accept_language), 'referer': str(sentinel.referer), 'username': str(sentinel.username), 'session': '', 'ip': '10.0.0.1', 'event_source': 'browser', 'event_type': str(sentinel.event_type), 'name': str(sentinel.event_type), 'event': payload, 'agent': str(sentinel.user_agent), 'page': self.url_with_course, 'time': FROZEN_TIME, 'host': 'testserver2', 'context': { 'course_id': 'foo/bar/baz', 'org_id': 'foo', 'user_id': user_id, 'path': u'/event' }, } finally: middleware.process_response(request, None) actual_event = self.get_event() assert_event_matches(expected_event, actual_event)
def test_user_track_with_missing_values(self): request = self.request_factory.get('/event') views.user_track(request) actual_event = self.get_event() expected_event = { 'context': { 'course_id': '', 'org_id': '', 'event_source': 'browser', 'page': '', 'username': '******' }, 'data': {}, 'timestamp': FROZEN_TIME, 'name': 'unknown' } assert_event_matches(expected_event, actual_event)
def test_badge_creation_event(self, post): result = { 'json': {'id': 'http://www.example.com/example'}, 'image': 'http://www.example.com/example.png', 'badge': 'test_assertion_slug', 'issuer': 'https://example.com/v1/issuer/issuers/test-issuer', } response = Mock() response.json.return_value = result post.return_value = response self.recreate_tracker() self.handler._create_assertion(self.badge_class, self.user, 'https://example.com/irrefutable_proof') args, kwargs = post.call_args self.assertEqual( args[0], 'https://example.com/v1/issuer/issuers/test-issuer/badges/' + EXAMPLE_SLUG + '/assertions' ) self.check_headers(kwargs['headers']) assertion = BadgeAssertion.objects.get(user=self.user, badge_class__course_id=self.course.location.course_key) self.assertEqual(assertion.data, result) self.assertEqual(assertion.image_url, 'http://www.example.com/example.png') self.assertEqual(assertion.assertion_url, 'http://www.example.com/example') self.assertEqual(kwargs['data'], { 'email': '*****@*****.**', 'evidence': 'https://example.com/irrefutable_proof' }) assert_event_matches({ 'name': 'edx.badge.assertion.created', 'data': { 'user_id': self.user.id, 'course_id': six.text_type(self.course.location.course_key), 'enrollment_mode': 'honor', 'assertion_id': assertion.id, 'badge_name': 'Test Badge', 'badge_slug': 'test_slug', 'issuing_component': 'test_component', 'assertion_image_url': 'http://www.example.com/example.png', 'assertion_json_url': 'http://www.example.com/example', 'issuer': 'https://example.com/v1/issuer/issuers/test-issuer', } }, self.get_event())
def test_video_control_events(self): """ Scenario: Video component is rendered in the LMS in Youtube mode without HTML5 sources Given the course has a Video component in "Youtube" mode And I play the video And I watch 5 seconds of it And I pause the video Then a "load_video" event is emitted And a "play_video" event is emitted And a "pause_video" event is emitted """ def is_video_event(event): """Filter out anything other than the video events of interest""" return event['event_type'] in ('load_video', 'play_video', 'pause_video') captured_events = [] with self.capture_events(is_video_event, number_of_matches=3, captured_events=captured_events): self.navigate_to_video() self.video.click_player_button('play') self.video.wait_for_position('0:05') self.video.click_player_button('pause') for idx, video_event in enumerate(captured_events): self.assert_payload_contains_ids(video_event) if idx == 0: assert_event_matches({'event_type': 'load_video'}, video_event) elif idx == 1: assert_event_matches({'event_type': 'play_video'}, video_event) self.assert_valid_control_event_at_time(video_event, 0) elif idx == 2: assert_event_matches({'event_type': 'pause_video'}, video_event) self.assert_valid_control_event_at_time(video_event, self.video.seconds)
def test_badge_creation_event(self, post): result = { 'json': {'id': 'http://www.example.com/example'}, 'image': 'http://www.example.com/example.png', 'slug': 'test_assertion_slug', 'issuer': 'https://example.com/v1/issuer/issuers/test-issuer', } response = Mock() response.json.return_value = result post.return_value = response self.recreate_tracker() self.handler.create_assertion(self.user, 'honor') args, kwargs = post.call_args self.assertEqual( args[0], 'https://example.com/v1/issuer/issuers/test-issuer/badges/' 'edxcourse_testtest_run_honor_fc5519b/assertions' ) self.check_headers(kwargs['headers']) assertion = BadgeAssertion.objects.get(user=self.user, course_id=self.course.location.course_key) self.assertEqual(assertion.data, result) self.assertEqual(assertion.image_url, 'http://www.example.com/example.png') self.assertEqual(kwargs['data'], { 'email': '*****@*****.**', 'evidence': 'https://edx.org/certificates/user/2/course/edX/course_test/test_run?evidence_visit=1' }) assert_event_matches({ 'name': 'edx.badge.assertion.created', 'data': { 'user_id': self.user.id, 'course_id': unicode(self.course.location.course_key), 'enrollment_mode': 'honor', 'assertion_id': assertion.id, 'assertion_image_url': 'http://www.example.com/example.png', 'assertion_json_url': 'http://www.example.com/example', 'issuer': 'https://example.com/v1/issuer/issuers/test-issuer', } }, self.get_event())
def test_certificate_evidence_event_emitted(self): self.client.logout() self._add_course_certificates(count=1, signatory_count=2) self.recreate_tracker() test_url = get_certificate_url( user_id=self.user.id, course_id=unicode(self.course.id) ) response = self.client.get(test_url) self.assertEqual(response.status_code, 200) actual_event = self.get_event() self.assertEqual(actual_event['name'], 'edx.certificate.evidence_visited') assert_event_matches( { 'user_id': self.user.id, 'certificate_id': unicode(self.cert.verify_uuid), 'enrollment_mode': self.cert.mode, 'certificate_url': test_url, 'course_id': unicode(self.course.id), 'social_network': CertificateSocialNetworks.linkedin }, actual_event['data'] )
def test_user_track_with_empty_event(self): request = self.request_factory.get('/event', { 'page': self.url_with_course, 'event_type': sentinel.event_type, 'event': '' }) views.user_track(request) actual_event = self.get_event() expected_event = { 'context': { 'course_id': 'foo/bar/baz', 'org_id': 'foo', 'event_source': 'browser', 'page': self.url_with_course, 'username': '******' }, 'data': {}, 'timestamp': FROZEN_TIME, 'name': str(sentinel.event_type) } assert_event_matches(expected_event, actual_event)
def assert_mock_tracker_call_matches(self, expected_event): self.assertEqual(len(self.mock_tracker.send.mock_calls), 1) actual_event = self.mock_tracker.send.mock_calls[0][1][0] assert_event_matches(expected_event, actual_event)
def test_video_control_events(self, event_type, action): """ Scenario: Video component with pre-roll emits events correctly Given the course has a Video component in "Youtube" mode with pre-roll enabled And I click on the video poster And the pre-roll video start playing And I watch (5 seconds/5 seconds/to the end of) it And I click (skip/do not show again) video button Then a "edx.video.bumper.loaded" event is emitted And a "edx.video.bumper.played" event is emitted And a "edx.video.bumper.skipped/dismissed/stopped" event is emitted And a "load_video" event is emitted And a "play_video" event is emitted """ def is_video_event(event): """Filter out anything other than the video events of interest""" return event['event_type'] in ( 'edx.video.bumper.loaded', 'edx.video.bumper.played', 'edx.video.bumper.skipped', 'edx.video.bumper.dismissed', 'edx.video.bumper.stopped', 'load_video', 'play_video', 'pause_video' ) and self.video.state != 'buffering' captured_events = [] self.add_bumper() with self.capture_events(is_video_event, number_of_matches=5, captured_events=captured_events): self.navigate_to_video_no_render() self.video.click_on_poster() self.video.wait_for_video_bumper_render() sources, duration = self.video.sources[0], self.video.duration action(self) # Filter subsequent events that appear due to bufferisation: edx.video.bumper.played # As bumper does not emit pause event, we filter subsequent edx.video.bumper.played events from # the list, except first. filtered_events = [] for video_event in captured_events: is_played_event = video_event['event_type'] == 'edx.video.bumper.played' appears_again = filtered_events and video_event['event_type'] == filtered_events[-1]['event_type'] if is_played_event and appears_again: continue filtered_events.append(video_event) for idx, video_event in enumerate(filtered_events): if idx < 3: self.assert_bumper_payload_contains_ids(video_event, sources, duration) else: self.assert_payload_contains_ids(video_event) if idx == 0: assert_event_matches({'event_type': 'edx.video.bumper.loaded'}, video_event) elif idx == 1: assert_event_matches({'event_type': 'edx.video.bumper.played'}, video_event) self.assert_valid_control_event_at_time(video_event, 0) elif idx == 2: assert_event_matches({'event_type': event_type}, video_event) elif idx == 3: assert_event_matches({'event_type': 'load_video'}, video_event) elif idx == 4: assert_event_matches({'event_type': 'play_video'}, video_event) self.assert_valid_control_event_at_time(video_event, 0)
def test_success(self, course_id): middleware = TrackMiddleware() request = self.create_request(data=self.create_segmentio_event_json( data={'foo': 'bar'}, course_id=course_id), content_type='application/json') User.objects.create(pk=USER_ID, username=str(sentinel.username)) middleware.process_request(request) # The middleware normally emits an event, make sure it doesn't in this case. self.assert_no_events_emitted() try: response = segmentio.segmentio_event(request) self.assertEquals(response.status_code, 200) expected_event = { 'accept_language': '', 'referer': '', 'username': str(sentinel.username), 'ip': '', 'session': '', 'event_source': 'mobile', 'event_type': str(sentinel.name), 'name': str(sentinel.name), 'event': { 'foo': 'bar' }, 'agent': str(sentinel.user_agent), 'page': None, 'time': datetime.strptime("2014-08-27T16:33:39.215Z", "%Y-%m-%dT%H:%M:%S.%fZ"), 'host': 'testserver', 'context': { 'application': { 'name': 'edx.mobile.android', 'version': '1.0.1', }, 'user_id': USER_ID, 'course_id': course_id, 'org_id': u'foo', 'path': ENDPOINT, 'client': { 'library': { 'name': 'test-app', 'version': 'unknown' }, 'app': { 'version': '1.0.1', }, }, 'received_at': datetime.strptime("2014-08-27T16:33:39.100Z", "%Y-%m-%dT%H:%M:%S.%fZ"), }, } finally: middleware.process_response(request, None) assert_event_matches(expected_event, self.get_event())
def test_video_control_events(self, event_type, action): """ Scenario: Video component with pre-roll emits events correctly Given the course has a Video component in "Youtube" mode with pre-roll enabled And I click on the video poster And the pre-roll video start playing And I watch (5 seconds/5 seconds/to the end of) it And I click (skip/do not show again) video button Then a "edx.video.bumper.loaded" event is emitted And a "edx.video.bumper.played" event is emitted And a "edx.video.bumper.skipped/dismissed/stopped" event is emitted And a "load_video" event is emitted And a "play_video" event is emitted """ def is_video_event(event): """Filter out anything other than the video events of interest""" return event['event_type'] in ( 'edx.video.bumper.loaded', 'edx.video.bumper.played', 'edx.video.bumper.skipped', 'edx.video.bumper.dismissed', 'edx.video.bumper.stopped', 'load_video', 'play_video', 'pause_video') and self.video.state != 'buffering' captured_events = [] self.add_bumper() with self.capture_events(is_video_event, number_of_matches=5, captured_events=captured_events): self.navigate_to_video_no_render() self.video.click_on_poster() self.video.wait_for_video_bumper_render() sources, duration = self.video.sources[0], self.video.duration action(self) # Filter subsequent events that appear due to bufferisation: edx.video.bumper.played # As bumper does not emit pause event, we filter subsequent edx.video.bumper.played events from # the list, except first. filtered_events = [] for video_event in captured_events: is_played_event = video_event[ 'event_type'] == 'edx.video.bumper.played' appears_again = filtered_events and video_event[ 'event_type'] == filtered_events[-1]['event_type'] if is_played_event and appears_again: continue filtered_events.append(video_event) for idx, video_event in enumerate(filtered_events): if idx < 3: self.assert_bumper_payload_contains_ids( video_event, sources, duration) else: self.assert_payload_contains_ids(video_event) if idx == 0: assert_event_matches({'event_type': 'edx.video.bumper.loaded'}, video_event) elif idx == 1: assert_event_matches({'event_type': 'edx.video.bumper.played'}, video_event) self.assert_valid_control_event_at_time(video_event, 0) elif idx == 2: assert_event_matches({'event_type': event_type}, video_event) elif idx == 3: assert_event_matches({'event_type': 'load_video'}, video_event) elif idx == 4: assert_event_matches({'event_type': 'play_video'}, video_event) self.assert_valid_control_event_at_time(video_event, 0)
def test_previous_builds(self, requested_skip_interval, expected_skip_interval, seek_type_key, seek_type, expected_seek_type, name, expected_name, platform, version, ): """ Test backwards compatibility of previous app builds iOS version 1.0.02: Incorrectly emits the skip back 30 seconds as +30 instead of -30. Android version 1.0.02: Skip and slide were both being returned as a skip. Skip or slide is determined by checking if the skip time is == -30 Additionally, for both of the above mentioned versions, edx.video.seeked was sent instead of edx.video.position.changed """ course_id = 'foo/bar/baz' middleware = TrackMiddleware() input_payload = { "code": "mobile", "new_time": 89.699177437, "old_time": 119.699177437, seek_type_key: seek_type, "requested_skip_interval": requested_skip_interval, 'module_id': 'i4x://foo/bar/baz/some_module', } request = self.create_request( data=self.create_segmentio_event_json( name=name, data=input_payload, context={ 'open_in_browser_url': 'https://testserver/courses/foo/bar/baz/courseware/Week_1/Activity/2', 'course_id': course_id, 'application': { 'name': platform, 'version': version, 'component': 'videoplayer' } }, ), content_type='application/json' ) User.objects.create(pk=USER_ID, username=str(sentinel.username)) middleware.process_request(request) try: response = segmentio.segmentio_event(request) self.assertEquals(response.status_code, 200) expected_event = { 'accept_language': '', 'referer': '', 'username': str(sentinel.username), 'ip': '', 'session': '', 'event_source': 'mobile', 'event_type': "seek_video", 'name': expected_name, 'agent': str(sentinel.user_agent), 'page': 'https://testserver/courses/foo/bar/baz/courseware/Week_1/Activity', 'time': datetime.strptime("2014-08-27T16:33:39.215Z", "%Y-%m-%dT%H:%M:%S.%fZ"), 'host': 'testserver', 'context': { 'user_id': USER_ID, 'course_id': course_id, 'org_id': 'foo', 'path': ENDPOINT, 'client': { 'library': { 'name': 'test-app', 'version': 'unknown' }, 'app': { 'version': '1.0.1', }, }, 'application': { 'name': platform, 'version': version, 'component': 'videoplayer' }, 'received_at': datetime.strptime("2014-08-27T16:33:39.100Z", "%Y-%m-%dT%H:%M:%S.%fZ"), }, 'event': { "code": "mobile", "new_time": 89.699177437, "old_time": 119.699177437, "type": expected_seek_type, "requested_skip_interval": expected_skip_interval, 'id': 'i4x-foo-bar-baz-some_module', } } finally: middleware.process_response(request, None) actual_event = self.get_event() assert_event_matches(expected_event, actual_event)
def test_video_event(self, name, event_type): course_id = 'foo/bar/baz' middleware = TrackMiddleware() input_payload = { 'current_time': 132.134456, 'module_id': 'i4x://foo/bar/baz/some_module', 'code': 'mobile' } if name == 'edx.video.loaded': # We use the same expected payload for all of these types of events, but the load video event is the only # one that is not actually expected to contain a "current time" field. So we remove it from the expected # event here. del input_payload['current_time'] request = self.create_request( data=self.create_segmentio_event_json( name=name, data=input_payload, context={ 'open_in_browser_url': 'https://testserver/courses/foo/bar/baz/courseware/Week_1/Activity/2', 'course_id': course_id, 'application': { 'name': 'edx.mobileapp.android', 'version': '29', 'component': 'videoplayer' } }), content_type='application/json' ) User.objects.create(pk=USER_ID, username=str(sentinel.username)) middleware.process_request(request) try: response = segmentio.segmentio_event(request) self.assertEquals(response.status_code, 200) expected_event = { 'accept_language': '', 'referer': '', 'username': str(sentinel.username), 'ip': '', 'session': '', 'event_source': 'mobile', 'event_type': event_type, 'name': name, 'agent': str(sentinel.user_agent), 'page': 'https://testserver/courses/foo/bar/baz/courseware/Week_1/Activity', 'time': datetime.strptime("2014-08-27T16:33:39.215Z", "%Y-%m-%dT%H:%M:%S.%fZ"), 'host': 'testserver', 'context': { 'user_id': USER_ID, 'course_id': course_id, 'org_id': 'foo', 'path': ENDPOINT, 'client': { 'library': { 'name': 'test-app', 'version': 'unknown' }, 'app': { 'version': '1.0.1', }, }, 'application': { 'name': 'edx.mobileapp.android', 'version': '29', 'component': 'videoplayer' }, 'received_at': datetime.strptime("2014-08-27T16:33:39.100Z", "%Y-%m-%dT%H:%M:%S.%fZ"), }, 'event': { 'currentTime': 132.134456, 'id': 'i4x-foo-bar-baz-some_module', 'code': 'mobile' } } if name == 'edx.video.loaded': # We use the same expected payload for all of these types of events, but the load video event is the # only one that is not actually expected to contain a "current time" field. So we remove it from the # expected event here. del expected_event['event']['currentTime'] finally: middleware.process_response(request, None) actual_event = self.get_event() assert_event_matches(expected_event, actual_event)
def test_previous_builds( self, requested_skip_interval, expected_skip_interval, seek_type_key, seek_type, expected_seek_type, name, expected_name, platform, version, ): """ Test backwards compatibility of previous app builds iOS version 1.0.02: Incorrectly emits the skip back 30 seconds as +30 instead of -30. Android version 1.0.02: Skip and slide were both being returned as a skip. Skip or slide is determined by checking if the skip time is == -30 Additionally, for both of the above mentioned versions, edx.video.seeked was sent instead of edx.video.position.changed """ course_id = 'foo/bar/baz' middleware = TrackMiddleware() input_payload = { "code": "mobile", "new_time": 89.699177437, "old_time": 119.699177437, seek_type_key: seek_type, "requested_skip_interval": requested_skip_interval, 'module_id': 'i4x://foo/bar/baz/some_module', } request = self.create_request(data=self.create_segmentio_event_json( name=name, data=input_payload, context={ 'open_in_browser_url': 'https://testserver/courses/foo/bar/baz/courseware/Week_1/Activity/2', 'course_id': course_id, 'application': { 'name': platform, 'version': version, 'component': 'videoplayer' } }, ), content_type='application/json') middleware.process_request(request) try: response = segmentio.segmentio_event(request) self.assertEqual(response.status_code, 200) expected_event = { 'accept_language': '', 'referer': '', 'username': str(sentinel.username), 'ip': '', 'session': '', 'event_source': 'mobile', 'event_type': "seek_video", 'name': expected_name, 'agent': str(sentinel.user_agent), 'page': 'https://testserver/courses/foo/bar/baz/courseware/Week_1/Activity', 'time': parser.parse("2014-08-27T16:33:39.215Z"), 'host': 'testserver', 'context': { 'user_id': SEGMENTIO_TEST_USER_ID, 'course_id': course_id, 'org_id': 'foo', 'path': SEGMENTIO_TEST_ENDPOINT, 'client': { 'library': { 'name': 'test-app', 'version': 'unknown' }, 'app': { 'version': '1.0.1', }, }, 'application': { 'name': platform, 'version': version, 'component': 'videoplayer' }, 'received_at': parser.parse("2014-08-27T16:33:39.100Z"), }, 'event': { "code": "mobile", "new_time": 89.699177437, "old_time": 119.699177437, "type": expected_seek_type, "requested_skip_interval": expected_skip_interval, 'id': 'i4x-foo-bar-baz-some_module', } } finally: middleware.process_response(request, None) actual_event = self.get_event() assert_event_matches(expected_event, actual_event)