"""Unit tests for the tasks.""" from datetime import datetime 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.flows.playlist_placements import config from activity_detector.flows.playlist_placements import tasks @pytest.yield_fixture def mock_session_context(): """Yield session context.""" pth = 'activity_detector.flows.playlist_placements.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=config.env, date_format=config.PLACEMENT_TIME_FORMAT ) 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', 'playlist_placements', 'queries', filename ), 'r' ) query = f.read() f.close() return query @patch('activity_detector.utils.ows_notifications.create_playlist_placement_notification') # noqa:E501 def test_detect_placements(mock_push, mock_session_context): """Test read playlist placement 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.return_value = [ { 'first_added': '2019-06-15 15:35:00', 'isrc': 'USMP1234', 'playlist_id': 'abcd', 'playlist_name': 'Songs for Dogs', 'storename': 'Spotify', 'storeid': 286, 'tracks_list': '[{"id": 123, "vendor_id": 12345}]', # noqa:E501 'playlist_rank': 2 }, { 'first_added': '2019-06-15 16:45:00', 'isrc': 'USMP5678', 'playlist_id': 'wxyz', 'playlist_name': 'Best Bird Calls 2020', 'storename': 'Spotify', 'storeid': 286, 'tracks_list': '[{"id": 456, "vendor_id": 6789, "subaccount_id": 54321}]', # noqa:E501 'playlist_rank': 3 } ] result = tasks.detect_placements(MagicMock(), 'spotify', '2019-06-15 12:00:00') # noqa:E501 assert result == {'failures': 0, 'last_timestamp': '2019-06-15 16:45:00'} assert mock_session_context.execute.call_count == 3 expected_calls = [ call( _query_formatted('insert_placements.sql'), { 'last_timestamp': '2019-06-15 12:00:00', 'dsp': 'spotify' } ), call('commit'), call( _query_formatted('get_placements.sql'), { 'last_timestamp': '2019-06-15 12:00:00', 'dsp': 'spotify' } ) ] mock_session_context.execute.assert_has_calls(expected_calls) assert mock_push.call_count == 2 expected_calls = [ call( '2019-06-15 15:35:00', 'Songs for Dogs', 'abcd', 'spotify', 286, 2, 'USMP1234', [ { 'id': 123, 'vendor_id': 12345 } ] ), call().__bool__(), call( '2019-06-15 16:45:00', 'Best Bird Calls 2020', 'wxyz', 'spotify', 286, 3, 'USMP5678', [ { 'id': 456, 'vendor_id': 6789, 'subaccount_id': 54321 } ] ), call().__bool__() ] mock_push.assert_has_calls(expected_calls) @patch('activity_detector.utils.ows_notifications.create_playlist_placement_notification') # noqa:E501 def test_detect_placements_fail_count(mock_push, mock_session_context): """Test read playlist placement data and notification activity sent 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.return_value = [ { 'first_added': '2019-06-15 15:35:00', 'isrc': 'USMP1234', 'playlist_id': 'abcd', 'playlist_name': 'Songs for Dogs', 'storename': 'Spotify', 'storeid': 286, 'tracks_list': '[{"id": 123, "vendor_id": 12345, "subaccount_id": 6789}]', # noqa:E501 'playlist_rank': 4 } ] result = tasks.detect_placements(MagicMock(), 'spotify', '2019-06-15 12:00:00') # noqa:E501 assert result == {'failures': 1, 'last_timestamp': '2019-06-15 15:35:00'} @patch('activity_detector.utils.ows_notifications.create_playlist_placement_notification') # noqa:E501 def test_detect_placements_no_results(mock_push, mock_session_context): """Test no recent playlist placements. 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 = True mock_session_context.execute.return_value.fetchall.return_value = [] result = tasks.detect_placements(MagicMock(), 'spotify', '2019-06-15 12:00:00') # noqa:E501 assert not mock_push.called assert result == {'failures': 0, 'last_timestamp': None} def test_check_dynamo_status(): """Test fetch last processed timestamp.""" with patch('activity_detector.utils.dynamodb.get_status') as status: last_run = datetime.utcnow() status.return_value = { 'status': 'PROCESSED_NOTIF_SENT', 'last_processed_timestamp': last_run } results = tasks.check_dynamo_status(MagicMock(), 'spotify') assert results == {'last_timestamp': datetime.strftime(last_run, '%Y-%m-%d %H:%M:%S')} @freeze_time('2019-06-15') def test_check_dynamo_status_default(): """Test fetch default last processed timestamp.""" with patch('activity_detector.utils.dynamodb.get_status') as status: status.return_value = None results = tasks.check_dynamo_status(MagicMock(), 'spotify') assert results == {'last_timestamp': '2019-06-14 00:00:00'} @pytest.mark.parametrize( ('status', 'failures', 'last_timestamp'), [ ('PROCESSED', 1, '2019-06-15 12:30:00'), ('PROCESSED_NOTIF_SENT', 0, '2019-06-15 12:30:00'), (None, 0, None) ] ) def test_set_dynamo_status(status, failures, last_timestamp): """Test saving status after flow run. Args: status (str): status set in db, None if not set failures (str): number of failures when sending to ows-notifications last_timestamp (str): time of last placement processed """ with patch('activity_detector.utils.dynamodb.set_status') as set_status: tasks.set_dynamo_status(MagicMock(), 'spotify', last_timestamp, failures) # noqa:E501 if status: assert set_status.call_args == call( 'playlist_placements:spotify', '*', status, datetime.strptime(last_timestamp, '%Y-%m-%d %H:%M:%S') ) else: assert not set_status.called def test_bootstrap(): """Assert bootstrap information in returned as expected.""" results = tasks.bootstrap(MagicMock(), 'Spotify', 'run-id') assert results == {'dsp': 'spotify'} results = tasks.bootstrap(MagicMock(), 'iTunes/Apple', 'run-id') assert results == {'dsp': 'itunes/apple'} def test_bootstrap_failure(): """Test bootstrap missing required param.""" raised = False try: tasks.bootstrap(MagicMock(), None, 'run-id') except AttributeError: raised = True assert raised