"""Test for the Stream model.""" import datetime from unittest.mock import call, patch from owsresponse import status from notifications.config import FANOUT_QUEUE_URL from notifications.constants.stream import PARTICIPANT_FEED_GROUP from notifications.models import stream @patch('notifications.models.stream.stream_client') def test_add_activity(mock_stream_client): """Test adding an activty to a feed.""" feed_name = 'label_track_placement' feed_id = 'vendor_123' payload = { 'actor': 'The Orchard Activity Detector', 'verb': 'Placed', 'object': 'Track', 'target': 'Playlist', 'track_id': 123, } result = stream.add_activity(feed_name, feed_id, payload) mock_stream_client.feed.assert_called_with(feed_name, feed_id) assert result.status == status.CREATED assert result.message == payload @patch('notifications.models.stream.stream_client') def test_subscribe(mock_stream_client): """Test subscribing to a feed.""" user_feed_name = 'user_email_notifications' user_id = 'alw:123' feed_name = 'label_track_placement' feed_id = 'vendor_123' result = stream.subscribe(user_feed_name, user_id, feed_name, feed_id) mock_stream_client.feed.assert_called_with(user_feed_name, 'alw_123') assert result.status == status.OK assert result.message == {'feed_name': feed_name, 'feed_id': feed_id} @patch('notifications.models.stream.stream_client') def test_subscribe_with_user_feed_id(mock_stream_client): """Test subscribing to a feed when a user feed id is specified.""" user_feed_name = 'user_email_notifications' user_id = 'alw:123' feed_name = 'label_track_placement' feed_id = 'vendor_123' user_feed_id = 'john-doe@gmail.com' result = stream.subscribe(user_feed_name, user_id, feed_name, feed_id, user_feed_id) mock_stream_client.feed.assert_called_with( user_feed_name, 'johnDASHSYMBOLdoeATSYMBOLgmailDOTSYMBOLcom' ) assert result.status == status.OK assert result.message == {'feed_name': feed_name, 'feed_id': feed_id} @patch('notifications.models.stream.stream_client') def test_unsubscribe(mock_stream_client): """Test unsubscribing from a feed.""" user_feed_name = 'user_email_notifications' user_id = 'alw:123' feed_name = 'label_track_placement' feed_id = 'vendor_123' result = stream.unsubscribe(user_feed_name, user_id, feed_name, feed_id) mock_stream_client.feed.assert_called_with(user_feed_name, 'alw_123') assert result.status == status.OK assert result.message == {'feed_name': feed_name, 'feed_id': feed_id} @patch('notifications.models.stream.stream_client') def test_unsubscribe_with_user_feed_id(mock_stream_client): """Test unsubscribing from a feed when a user feed id is specified.""" user_feed_name = 'user_email_notifications' user_id = 'alw:123' feed_name = 'label_track_placement' feed_id = 'vendor_123' user_feed_id = 'john-doe@gmail.com' result = stream.unsubscribe(user_feed_name, user_id, feed_name, feed_id, user_feed_id) mock_stream_client.feed.assert_called_with( user_feed_name, 'johnDASHSYMBOLdoeATSYMBOLgmailDOTSYMBOLcom' ) assert result.status == status.OK assert result.message == {'feed_name': feed_name, 'feed_id': feed_id} @patch('notifications.models.stream.stream_client') def test_get_user_notifications_call(mock_stream_client): """Test that the get user notifications function makes the right call.""" user_id = 'alw:123' user_feed_name = 'user_email_notifications' stream.get_user_notifications(user_id, user_feed_name) mock_stream_client.feed.assert_called_with(user_feed_name, 'alw_123') @patch('notifications.models.stream.stream_client') def test_get_user_notifications_call_with_user_feed_id(mock_stream_client): """Test that the get user notifications function makes the right call.""" user_id = 'alw:123' user_feed_name = 'user_email_notifications' user_feed_id = 'john-doe@gmail.com' stream.get_user_notifications(user_id, user_feed_name, user_feed_id) mock_stream_client.feed.assert_called_with( user_feed_name, 'johnDASHSYMBOLdoeATSYMBOLgmailDOTSYMBOLcom' ) def test_getstream_event(): """Test event processing PLATFORM-2580.""" event = { 'metadata': { 'track': { 'id': 29982747, 'isrc': 'QM6MZ1917908', 'subaccount_id': None, 'vendor_id': 26536, 'name': 'Boy With Luv (with Halsey)', }, 'day_streams': 7842, 'percent_diff': 19, 'dsp': 'spotify', 'region': 'Taiwan, Province Of China', }, 'time': datetime.datetime(2020, 11, 20), 'sources': ['participant'], 'profile_id': 79632, 'profile_type': 'LabelProfile', } event_type = 'trending_tracks' event_actor = 29982747 event_ids = [29982747, 'spotify', 'Taiwan, Province Of China'] result = stream._getstream_activity(event, event_type, event_actor, event_ids) assert result == { 'event': { 'actor': 29982747, 'verb': 'trending_tracks', 'foreign_id': 'trending_tracks:29982747:spotify:Taiwan Province Of China:LabelProfile_79632', # noqa:E501 'object': { 'track': { 'id': 29982747, 'isrc': 'QM6MZ1917908', 'subaccount_id': None, 'vendor_id': 26536, 'name': 'Boy With Luv (with Halsey)', }, 'day_streams': 7842, 'percent_diff': 19, 'dsp': 'spotify', 'region': 'Taiwan, Province Of China', 'activity_sources': ['participant'], }, 'time': datetime.datetime(2020, 11, 20, 0, 0), }, 'feed_group': 'user_mobile_push', 'feed_id': 'LabelProfile_79632', } @patch('notifications.models.stream._add_activities') def test_add_trending_track(mock_add_activities): """Test formatting events into GetStream activities.""" stream.add_trending_track_activity( [ { 'profile_id': 12345, 'profile_type': 'InsightsProfile', 'time': datetime.datetime(2019, 6, 15), 'sources': ['sound_recording'], 'metadata': { 'dsp': 'spotify', 'region': 'USA', 'percent_diff': 100, 'day_streams': 1000, 'track': {'id': 1234, 'isrc': 'xyz', 'label_id': 5678}, }, } ] ) assert mock_add_activities.call_args_list == [ call( [ { 'event': { 'actor': 1234, 'verb': 'trending_tracks', 'foreign_id': 'trending_tracks:1234:spotify:USA:InsightsProfile_12345', 'object': { 'activity_sources': ['sound_recording'], 'track': {'id': 1234, 'isrc': 'xyz', 'label_id': 5678}, 'percent_diff': 100, 'day_streams': 1000, 'region': 'USA', 'dsp': 'spotify', }, 'time': datetime.datetime(2019, 6, 15, 0, 0), }, 'feed_group': 'user_mobile_push', 'feed_id': 'InsightsProfile_12345', } ] ) ] @patch('notifications.models.stream._add_activities') def test_add_playlist_placement(mock_add_activities): """Test adding playlist placement to GetStream feed.""" stream.add_playlist_placement_activity( [ { 'profile_id': 12345, 'profile_type': 'InsightsProfile', 'time': datetime.datetime(2019, 6, 15, 12, 0, 0), 'sources': ['sound_recording'], 'metadata': { 'sound_recording': {'id': 'xyz'}, 'playlist': {'name': 'Best Music 2020', 'dsp': 'spotify', 'id': 'qwerty'}, }, } ] ) assert mock_add_activities.call_args_list == [ call( [ { 'event': { 'actor': 'xyz', 'verb': 'playlist_placements', 'foreign_id': 'playlist_placements:qwerty:spotify:xyz:InsightsProfile_12345', # noqa:E501 'object': { 'activity_sources': ['sound_recording'], 'sound_recording': {'id': 'xyz'}, 'playlist': { 'name': 'Best Music 2020', 'dsp': 'spotify', 'id': 'qwerty', }, }, 'time': datetime.datetime(2019, 6, 15, 12, 0), }, 'feed_group': 'user_mobile_push', 'feed_id': 'InsightsProfile_12345', } ] ) ] @patch('notifications.models.stream._add_activities') def test_add_social_spike(mock_add_activities): """Test adding social spike events.""" stream.add_social_spike_activity( [ { 'profile_id': 12345, 'profile_type': 'InsightsProfile', 'time': datetime.datetime(2019, 6, 15), 'sources': ['participant'], 'metadata': {'id': 'xyz', 'name': 'Heavy Metal Harmonica', 'network': 'twitter'}, } ] ) assert mock_add_activities.call_args_list == [ call( [ { 'event': { 'actor': 'xyz', 'verb': 'social_spike_twitter', 'foreign_id': 'social_spike_twitter:xyz:InsightsProfile_12345', 'object': { 'id': 'xyz', 'name': 'Heavy Metal Harmonica', 'network': 'twitter', 'activity_sources': ['participant'], }, 'time': datetime.datetime(2019, 6, 15, 0, 0), }, 'feed_group': 'user_mobile_push', 'feed_id': 'InsightsProfile_12345', } ] ) ] @patch('notifications.models.stream.sqs_send_messages') def test_add_activities_sqs(mock_sqs): """Test add activities using sqs.""" mock_sqs.return_value = (1, 0) activities = [ { 'event': { 'foreign_id': 'abc1', 'time': datetime.datetime.strptime('2019-06-15', '%Y-%m-%d'), }, 'feed_group': 'test', 'feed_id': '123', } ] result = stream._add_activities(activities) assert mock_sqs.call_args_list == [ call( FANOUT_QUEUE_URL, [ { 'event': {'foreign_id': 'abc1', 'time': '2019-06-15T00:00:00'}, 'feed_group': 'test', 'feed_id': '123', } ], ) ] assert result.message == {'submitted': 1, 'added': 1} assert result.status == 201 @patch('notifications.models.stream.sqs_send_messages') def test_add_activities_sqs_failed(mock_sqs): """Test sqs failues.""" mock_sqs.return_value = (0, 1) activities = [ { 'event': { 'foreign_id': 'abc1', 'time': datetime.datetime.strptime('2019-06-15', '%Y-%m-%d'), }, 'feed_group': 'test', 'feed_id': '123', } ] raised = False try: stream._add_activities(activities) except Exception as e: assert str(e) == 'failures when adding activities to sqs fanout queue' raised = True assert raised def test_add_activities_sqs_empty(): """Test add no activities using sqs.""" activities = [] result = stream._add_activities(activities) assert result.message == {'submitted': 0, 'added': 0} assert result.status == 200 @patch('notifications.models.stream.stream_client') def test_add_activities_empty_list(mock_stream_client): """Test adding no activities.""" activities = [] result = stream._add_activities(activities) assert result.message == {'submitted': 0, 'added': 0} assert result.status == 200 @patch('notifications.models.stream.stream_client') def test_subscribe_entity(mock_stream_client): """Test profile follow linkages in model.""" profile_type = 'InsightsProfile' profile_id = 555 entity_type = 'GlobalParticipant' entity_id = 19 return_value = stream.subscribe_entity( profile_type, profile_id, entity_type, entity_id, PARTICIPANT_FEED_GROUP ) assert return_value is None mock_stream_client.feed.assert_called_with(stream.PROFILE_FEED_GROUP, 'InsightsProfile_555') mock_stream_client.feed.return_value.follow.assert_called_with( PARTICIPANT_FEED_GROUP, 'GlobalParticipant_19' ) @patch('notifications.models.stream.stream_client') def test_unsubscribe_entity(mock_stream_client): """Test profile unfollow participant linkages in model.""" profile_type = 'InsightsProfile' profile_id = '555' entity_type = 'GlobalParticipant' entity_id = '19' return_value = stream.unsubscribe_entity( profile_type, profile_id, entity_type, entity_id, PARTICIPANT_FEED_GROUP ) assert return_value is None mock_stream_client.feed.assert_called_with(stream.PROFILE_FEED_GROUP, 'InsightsProfile_555') mock_stream_client.feed.return_value.unfollow.assert_called_with( PARTICIPANT_FEED_GROUP, 'GlobalParticipant_19' ) def test_get_user_notifications_output_notifications_feed(): """Test output of the get user notifications function.""" stream_response = { 'results': [ { 'activities': [ {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '1'} ] }, { 'activities': [ {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '2'} ] }, ] } class StreamFeed: def get(self, limit=10): return stream_response class StreamClient: def feed(self, feed_id, feed_name): return StreamFeed() stream_client = StreamClient() with patch('notifications.models.stream.stream_client', new=stream_client): user_id = 'alw:123' user_feed_name = 'user_email_notifications' result = stream.get_user_notifications(user_id, user_feed_name) assert result.status == status.OK assert result.message['items'] == [ {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '1'}, {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '2'}, ] def test_get_user_notifications_output_flat_feed(): """Test output of the get user notifications function.""" stream_response = { 'results': [ {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '1'}, {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '2'}, ] } class StreamFeed: def get(self, limit=10): return stream_response class StreamClient: def feed(self, feed_id, feed_name): return StreamFeed() stream_client = StreamClient() with patch('notifications.models.stream.stream_client', new=stream_client): user_id = 'alw:123' user_feed_name = 'user_workstation_notifications' result = stream.get_user_notifications(user_id, user_feed_name) assert result.status == status.OK assert result.message['items'] == [ {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '1'}, {'actor': 'Tester', 'verb': 'Testing', 'object': 'Test', 'target': '2'}, ] def test_get_user_subscriptions(): """Test output of the get user subscriptions function.""" stream_response = { 'results': [ { 'feed_id': 'user_email_notifications:alw_123', 'target_id': 'label_analytics_digest:vendor_123', }, { 'feed_id': 'user_email_notifications:alw_123', 'target_id': 'label_spike_detector:vendor_123', }, ] } class StreamFeed: def following(self, limit=10): return stream_response class StreamClient: def feed(self, feed_id, feed_name): return StreamFeed() stream_client = StreamClient() with patch('notifications.models.stream.stream_client', new=stream_client): user_id = 'alw:123' user_feed_name = 'user_workstation_notifications' result = stream.get_user_subscriptions(user_id, user_feed_name) assert result.status == status.OK assert result.message['items'] == [ 'label_analytics_digest:vendor_123', 'label_spike_detector:vendor_123', ] def test_get_feed_subscribers(): """Test output of the get feed subscribers function.""" stream_response = { 'results': [ { 'feed_id': 'user_email_notifications:alw_123', 'target_id': 'label_analytics_digest:vendor_123', }, { 'feed_id': 'user_email_notifications:alw_456', 'target_id': 'label_analytics_digest:vendor_123', }, ] } class StreamFeed: def followers(self, limit=10): return stream_response class StreamClient: def feed(self, feed_id, feed_name): return StreamFeed() stream_client = StreamClient() with patch('notifications.models.stream.stream_client', new=stream_client): feed_name = 'label_analytics_digest' feed_id = 'vendor_123' result = stream.get_feed_subscribers(feed_name, feed_id) assert result.status == status.OK assert result.message['items'] == [ 'user_email_notifications:alw_123', 'user_email_notifications:alw_456', ]