"""Unit tests for the tasks.""" import os from unittest.mock import call from unittest.mock import MagicMock from unittest.mock import patch from freezegun import freeze_time import pytest from activity_detector import base_config from activity_detector.flows.trending_tracks_discrete import tasks @pytest.yield_fixture def mock_session_context(): """Yield session context.""" pth = 'activity_detector.flows.trending_tracks_discrete.tasks.get_session' # noqa:E501 with patch(pth) as gt_session: mock_session_context = \ gt_session.return_value.__enter__.return_value yield mock_session_context def _query_formatted(filename): return _query_file_contents(filename).format(env=base_config.ENVIRONMENT) def _query_file_contents(filename): """Load contents of query file. Args: filename (str): filename to load (path assumed) Returns: str: contents of file """ f = open( os.path.join( 'activity_detector', 'flows', 'trending_tracks_discrete', 'queries', filename ), 'r' ) query = f.read() f.close() return query @patch('activity_detector.utils.ows_notifications.create_trending_track_notification') # noqa:E501 def test_detect_spikes(mock_push, mock_session_context): """Test read spike data and notification activity sent. Args: mock_push (MagicMock): mock of method sending data to ows-notifications mock_session_context (MagicMock): mock of snowflake conn session """ mock_session_context.execute.return_value.fetchall.side_effect = [ [ {'store_id': 1}, {'store_id': 286} ], [ { 'labelid': 123, 'subaccountid': 456, 'track_unique_id': 987, 'isrc': 'xyz', 'storename': 'Spotify', 'countryname': 'USA', 'total_streams': 1000, 'pct_diff': 100, 'spike_score': 5 } ] ] results = tasks.detect_spikes(MagicMock(), '2019-06-15') assert results == {'failures': 0} assert mock_session_context.execute.call_count == 4 assert mock_session_context.execute.call_args_list == [ call( _query_formatted('check_activity.sql'), { 'date': '2019-06-15', 'store_ids': (1, 286) } ), call( _query_formatted('insert_activity.sql'), { 'date': '2019-06-15', 'store_ids': (1, 286), 'min_spike_score': 3, 'min_days_of_data': 30, 'min_streams': 5000, 'max_percent_diff': 1000, 'top_markets_number': 5 } ), call('commit'), call( _query_formatted('get_activity.sql'), { 'date': '2019-06-15' } ) ] assert mock_push.call_count == 1 assert mock_push.call_args_list == [ call( '2019-06-15', 'USA', 'spotify', 100, 1000, 987, 'xyz', 123, 456 ) ] def test_detect_spikes_no_data(mock_session_context): """Test exception if no data exists for date. Args: mock_session_context (MagicMock): mock of snowflake conn session """ mock_session_context.execute.return_value.fetchall.side_effect = [[]] raised = False try: tasks.detect_spikes(MagicMock(), '2019-06-15') except Exception as e: raised = True assert str(e) == 'Missing data for store(s) {1, 286} for 2019-06-15!' assert isinstance(e, Exception) assert raised def test_detect_spikes_partial_data(mock_session_context): """Test exception if data exists for one store on date. Args: mock_session_context (MagicMock): mock for snowflake conn session """ mock_session_context.execute.return_value.fetchall.side_effect = [ [ {'store_id': 286} ] ] raised = False try: tasks.detect_spikes(MagicMock(), '2019-06-15') except Exception as e: raised = True assert str(e) == 'Missing data for store(s) {1} for 2019-06-15!' assert isinstance(e, Exception) assert raised @patch('activity_detector.utils.ows_notifications.create_trending_track_notification') # noqa:E501 def test_detect_spikes_fail_count(mock_push, mock_session_context): """Test read spike data and notification activity sent with failures. Args: mock_push (MagicMock): mock of method sending data to ows-notifications mock_session_context (MagicMock): mock of snowflake conn session """ mock_push.return_value = False mock_session_context.execute.return_value.fetchall.side_effect = [ [ {'store_id': 1}, {'store_id': 286} ], [ { 'labelid': 123, 'subaccountid': 456, 'track_unique_id': 987, 'isrc': 'xyz', 'storename': 'spotify', 'countryname': 'USA', 'total_streams': 1000, 'pct_diff': 100, 'spike_score': 5 } ] ] result = tasks.detect_spikes(MagicMock(), '2019-06-15') assert result == {'failures': 1} @pytest.mark.parametrize( ('should_run', 'response'), [ (True, None), (True, {'status': 'PROCESSED'}), (True, {}), (False, {'status': 'PROCESSED_NOTIF_SENT'}) ] ) def test_check_dynamo_status(should_run, response): """Test dynamodb status checker. Args: should_run (bool): expected output after reading status from db response (dict): response from db """ with patch('activity_detector.utils.dynamodb.get_status') as status: status.return_value = response results = tasks.check_dynamo_status(MagicMock(), '2019-06-15') assert results == {'should_run': should_run} @pytest.mark.parametrize( ('status', 'failures'), [ ('PROCESSED', 1), ('PROCESSED_NOTIF_SENT', 0) ] ) def test_set_synamo_status(status, failures): """Test saving status after flow run. Args: status (str): status set in db failures (str): number of failures when sending to ows-notifications """ with patch('activity_detector.utils.dynamodb.set_status') as set_status: tasks.set_dynamo_status(MagicMock(), '2019-06-15', failures) assert set_status.call_args == call( 'trending_tracks_discrete', '2019-06-15', status) def test_bootstrap(): """Assert bootstrap information in returned as expected.""" results = tasks.bootstrap(MagicMock(), '2019-06-15', 'run-id') assert results == {'target_date': '2019-06-15'} @freeze_time('2019-06-15') def test_bootstrap_defaults(): """Assert default bootstrap information is returned as expected.""" results = tasks.bootstrap(MagicMock(), None, 'run-id') assert results == {'target_date': '2019-06-13'}