"""Unit tests for apple_music_kit models layer.""" from unittest.mock import Mock import pytest import requests from analytics.models import apple_music_kit from analytics.models import utils @pytest.fixture() def playlist_links(): """Return playlist links for testing.""" return [ ('https://itunes.apple.com/us/playlist/' 'idpl.25ccede448f045deb022c4b6ef7eed5b')] def test_square_image(): """Test squaring image if 'bb' only before file format.""" raw_image = ( 'https://example.mzstatic.com/image/thumb/Features127/v4/7a/36/06/' '7a360671-73cf-21d9-c794-65b30398d749/source/{w}x{h}bb.jpg') expected_image = ( 'https://example.mzstatic.com/image/thumb/Features127/v4/' '7a/36/06/7a360671-73cf-21d9-c794-65b30398d749/source/300x300cc.jpg') assert apple_music_kit._square_image(raw_image) == expected_image def test_square_image_with_bb_before_source(): """Test squaring image if 'bb' exists somewhere else.""" raw_image = ( 'https://example.mzstatic.com/image/thumb/Features127/v4/7a/36/06/' '7a360671-73cf-21d9-c794-65bb30398d749/source/{w}x{h}bb.jpg') expected_image = ( 'https://example.mzstatic.com/image/thumb/Features127/v4/7a/36/06/' '7a360671-73cf-21d9-c794-65bb30398d749/source/300x300cc.jpg') assert apple_music_kit._square_image(raw_image) == expected_image @pytest.fixture(params=['playlist', 'station']) def playlist_type(request): """Apple Music playlist type fixture.""" return request.param @pytest.mark.parametrize('curator_name', ['curator_name', '']) def test_get_metadata_from_response(monkeypatch, playlist_type, curator_name): """Pass _get_metadata_from_response helper function.""" # Mocking playlist_id = 'playlist_id' if not curator_name: curator_name = 'Radio Station' playlist_name = 'playlist_name' image_url = 'image_url' artwork = {'url': image_url} data = { 'id': playlist_id, 'attributes': { 'curatorName': curator_name, 'name': playlist_name, 'artwork': artwork } } playlist_link = apple_music_kit._playlist_link_templates[ playlist_type].format(playlist_id) square_image = Mock() square_image_result = square_image.return_value monkeypatch.setattr(apple_music_kit, '_square_image', square_image) expected_result = { playlist_id: { 'playlist_link': playlist_link, 'playlist_author': curator_name, 'playlist_title': playlist_name, 'image': square_image_result}} # Tested function call result = apple_music_kit._get_metadata_from_response(data, playlist_type) # Checks assert result == expected_result @pytest.mark.parametrize( 'response_data, expected_result', [ [{}, {}], [{'data': []}, {}], [{'data': [{'test': 'playlist'}]}, {'test': 'playlist'}], ] ) def test_process_response_data( monkeypatch, playlist_type, response_data, expected_result): """Test _process_response_data helper function.""" metadata = Mock(return_value=expected_result) monkeypatch.setattr( apple_music_kit, '_get_metadata_from_response', metadata) res = apple_music_kit._process_response_data(response_data, playlist_type) assert res == expected_result @pytest.mark.parametrize( 'playlists, found_playlists, expected_result', [ # Empty params [[], {}, []], # Playlist was found [ [{'playlist_id': 1, 'playlist_link': 'playlist_link'}], {1: {'playlist_link': 'playlist_link'}}, [{'playlist_link': 'playlist_link', 'status_code': 200}]], # Playlist wasn't found [ [{'playlist_id': 1, 'playlist_link': 'playlist_link'}], {2: {}}, [{'playlist_link': 'playlist_link', 'status_code': 404}]], ] ) def test_prepare_playlists_metadata( playlists, found_playlists, expected_result): """Test _prepare_playlists_metadata helper function.""" result = apple_music_kit._prepare_playlists_metadata( playlists, found_playlists) assert result == expected_result @pytest.fixture def playlists_metadata_fixture(monkeypatch): """Test data and mocked general functions for get_playlist_metadata.""" result = Mock() monkeypatch.setattr(apple_music_kit, '_obtain_access_token', Mock()) auth_headers = Mock() headers = auth_headers.return_value monkeypatch.setattr(apple_music_kit, '_auth_headers', auth_headers) requests_get = Mock() api_response = requests_get.return_value api_response.status_code = 0 json_value = api_response.json.return_value monkeypatch.setattr(requests, 'get', requests_get) result.requests_get = requests_get result.api_response = api_response result.headers = headers result.json_value = json_value return result @pytest.fixture(params=[ [ {'playlist_id': 'id1', 'type': 'playlist'}, {'playlist_id': 'id2', 'type': 'playlist'} ], [ {'playlist_id': 'id1', 'type': 'station'}, {'playlist_id': 'id2', 'type': 'station'} ] ]) def playlists(request): """Apple Music playlist fixture.""" return request.param @pytest.fixture def playlists_with_status_mock(monkeypatch): """Mock playlists_with_status function.""" failed_result = Mock() monkeypatch.setattr(utils, 'playlists_with_status', failed_result) return failed_result def test_get_playlists_metadata_success( monkeypatch, playlists_metadata_fixture, playlists): """Test get_playlists_metadata function for success response.""" # Mocking playlists_metadata_fixture.api_response.status_code = 200 playlist_ids = ','.join( [playlist['playlist_id'] for playlist in playlists]) playlists_type = playlists[0]['type'] api_url = apple_music_kit._api_playlist_link_templates[ playlists_type].format(ids=playlist_ids) process_response_data = Mock() found_playlists = process_response_data.return_value monkeypatch.setattr( apple_music_kit, '_process_response_data', process_response_data) expected_result = Mock() playlist_medata = Mock() playlist_medata.return_value = expected_result monkeypatch.setattr( apple_music_kit, '_prepare_playlists_metadata', playlist_medata) # Tested function call result = apple_music_kit.get_playlists_metadata( playlists, Mock(), [1, 2], ['theorchard']) # Checks assert result.message == expected_result playlists_metadata_fixture.requests_get.assert_called_once_with( api_url, headers=playlists_metadata_fixture.headers) assert playlists_metadata_fixture.api_response.json.called process_response_data.assert_called_once_with( playlists_metadata_fixture.json_value, playlists_type) playlist_medata.assert_called_once_with(playlists, found_playlists) def test_get_playlists_metadata_response_error( monkeypatch, playlists_metadata_fixture, playlists, mock_sentry): """Test get_playlists_metadata function for error response.""" playlists_metadata_fixture.api_response.status_code = 500 playlists_type = playlists[0]['type'] failed_result = Mock() expected_result = failed_result.return_value monkeypatch.setattr(utils, 'playlists_with_status', failed_result) # Tested function call result = apple_music_kit.get_playlists_metadata( playlists, playlists_type, [1, 2], ['theorchard']) # Checks assert result.message == expected_result def test_get_playlists_metadata_throttled( monkeypatch, playlists_metadata_fixture, playlists, mock_sentry): """Test get_playlists_metadata function for throttled response.""" playlists_metadata_fixture.api_response.status_code = 429 playlist_ids = ','.join( [playlist['playlist_id'] for playlist in playlists]) playlists_type = playlists[0]['type'] api_url = apple_music_kit._api_playlist_link_templates[ playlists_type].format(ids=playlist_ids) failed_result = Mock() expected_result = failed_result.return_value monkeypatch.setattr(utils, 'playlists_with_status', failed_result) # Tested function call result = apple_music_kit.get_playlists_metadata( playlists, playlists_type, [1, 2], ['theorchard']) # Checks assert result.message == expected_result mock_sentry.captureMessage.assert_called_once_with( 'Apple Music API limit was reached.', extra={ 'api_url': api_url, 'playlists_type': playlists_type}) def test_get_playlists_metadata_exception( monkeypatch, playlists_metadata_fixture, playlists, mock_sentry): """Test get_playlists_metadata function on Exception.""" playlist_ids = ','.join( [playlist['playlist_id'] for playlist in playlists]) playlists_type = playlists[0]['type'] api_url = apple_music_kit._api_playlist_link_templates[ playlists_type].format(ids=playlist_ids) playlists_metadata_fixture.requests_get.side_effect = Exception() failed_result = Mock() expected_result = failed_result.return_value monkeypatch.setattr( utils, 'playlists_with_status', failed_result) # Tested function call result = apple_music_kit.get_playlists_metadata( playlists, playlist_type, [1, 2], ['theorchard']) # Checks assert result.message == expected_result playlists_metadata_fixture.requests_get.assert_called_once_with( api_url, headers=playlists_metadata_fixture.headers) mock_sentry.captureException.assert_called_once_with() failed_result.assert_called_once_with(playlists, 500) def test_get_playlists_metadata_timeout_exception( playlists_metadata_fixture, mock_sentry, playlists, playlists_with_status_mock): """Test get_playlists_metadata function on requests timeout Exception.""" playlists_metadata_fixture.requests_get.side_effect = ( requests.exceptions.ReadTimeout()) apple_music_kit.get_playlists_metadata( playlists, playlist_type, [1, 2], ['theorchard']) mock_sentry.captureException.assert_called_once_with() playlists_with_status_mock.assert_called_once_with(playlists, 504)