"""Spotify API wrapper tests.""" import datetime from gzip import GzipFile from io import BytesIO import itertools from tempfile import TemporaryFile from unittest.mock import MagicMock from unittest.mock import patch import pytest from data_landing_zone.flows.spotify.spotify_api import SpotifyAPI @pytest.yield_fixture def spotify_api_authenticated(): """Yield authenticated SpotifyAPI Wrapper fixture.""" spotify_api = SpotifyAPI('test', 'secret', 'test-licensor', 'test_version') spotify_api._token = 'token' yield spotify_api @pytest.yield_fixture def spotify_api(): """Spotify API Wrapper fixture.""" yield SpotifyAPI('test', 'secret', 'test-licensor', 'test_version') @pytest.yield_fixture def mock_oauth_success(): """Yield Spotify API oauth success response.""" requests_path = ( 'data_landing_zone.flows.spotify.spotify_api.requests') with patch(requests_path) as requests: response_body = {'access_token': 'token', 'expires_in': 28800} response_mock = MagicMock() response_mock.status_code = 200 response_mock.json = MagicMock(return_value=response_body) requests.post = MagicMock(return_value=response_mock) yield requests @pytest.yield_fixture def mock_oauth_failure(): """Yield Spotify API oauth failure response.""" requests_path = ( 'data_landing_zone.flows.spotify.spotify_api.requests') with patch(requests_path) as requests: response_mock = MagicMock() response_mock.status_code = 401 requests.post = MagicMock(return_value=response_mock) yield requests @pytest.yield_fixture def mock_tracks_content(): """Mock Tracks Resource response.""" content = ( b'{"message": "APITrackData", "version": 2, "track_id": "0",' b' "uri": "spotify:track:0", "isrc": "0", "album_code": "0",' b' "album_artist": "Test", "track_artists": "Test",' b'"track_name": "Test", "album_name": "Test"}\n' b'{"message": "APITrackData", "version": 2, "track_id": "1",' b' "uri": "spotify:track:1", "isrc": "1", "album_code": "1",' b' "album_artist": "Test1", "track_artists": "Test1",' b'"track_name": "Test1", "album_name": "Test1"}') out = BytesIO() with GzipFile(fileobj=out, mode='wb') as f: f.write(content) yield out.getvalue() @pytest.fixture def expected_tracks_content(): """Return expected json response for Tracks resource.""" return [ {'message': 'APITrackData', 'version': 2, 'track_id': '0', 'uri': 'spotify:track:0', 'isrc': '0', 'album_code': '0', 'album_artist': 'Test', 'track_artists': 'Test', 'track_name': 'Test', 'album_name': 'Test'}, {'message': 'APITrackData', 'version': 2, 'track_id': '1', 'uri': 'spotify:track:1', 'isrc': '1', 'album_code': '1', 'album_artist': 'Test1', 'track_artists': 'Test1', 'track_name': 'Test1', 'album_name': 'Test1'}] @pytest.yield_fixture def mock_session(mock_tracks_content): """Mock requests session.""" session_path = ( 'data_landing_zone.flows.spotify.spotify_api.Session') def iter_content_mock(chunk_size=128): it = iter(mock_tracks_content) while True: chunk = bytes(itertools.islice(it, chunk_size)) if not chunk: return yield chunk with patch(session_path) as mock_session: session_instance = mock_session.return_value response_mock = MagicMock() response_mock.iter_content = iter_content_mock response_mock.content = mock_tracks_content session_instance.send = MagicMock(return_value=response_mock) yield session_instance def assert_get_to_file( fd, mock_content, mock_session, date, resource_name, licensor_name, country=None): """Test get_*_to_file methods.""" assert_request(country, date, licensor_name, mock_session, resource_name) fd.seek(0) for line in fd: assert line in mock_content def assert_get_to_json( res, mock_content, mock_session, date, resource_name, licensor_name, country=None): """Test get_*_to_json methods.""" assert_request(country, date, licensor_name, mock_session, resource_name) assert res == mock_content def assert_request(country, date, licensor_name, mock_session, resource_name): """Test API request.""" assert mock_session.send.called req = mock_session.send.call_args[0][0] assert licensor_name in req.url assert resource_name in req.url assert date in req.url if country is not None: assert country in req.url def test_version_api_v1(): """Test Spotify API choosing version.""" spotify_api = SpotifyAPI('test', 'secret', 'test-licensor', 'v1') assert spotify_api.version == '' def test_version_api_v2(): """Test Spotify API choosing version.""" spotify_api = SpotifyAPI('test', 'secret', 'test-licensor', 'v2') assert spotify_api.version == 'v2' def test_version_api_default(): """Test Spotify API choosing version.""" spotify_api = SpotifyAPI('test', 'secret', 'test-licensor', 'test_version') assert spotify_api.version == 'v2' def test_token_success(mock_oauth_success, spotify_api): """Test Spotify API token request success scenario.""" spotify_api._token = None token = spotify_api.token assert token == 'token' assert spotify_api._token == 'token' def test_token_failure(mock_oauth_failure, spotify_api): """Test Spotify API token request failure scenario.""" spotify_api._token = None with pytest.raises(Exception): spotify_api.token def test_token_cache(mock_oauth_success, spotify_api): """Should not call Spotify API if token is already cached.""" spotify_api._token = 'cached-token' token = spotify_api.token mock_oauth_success.post.assert_not_called() assert token == 'cached-token' def test_tracks_to_file( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_tracks_to_file.""" with TemporaryFile() as fd: spotify_api_authenticated.get_tracks_to_file(fd, '2017-10-01') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'tracks', spotify_api_authenticated.licensor_name) def test_tracks_to_file_date( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_tracks_to_file with date argument.""" with TemporaryFile() as fd: d = datetime.date(2017, 10, 1) spotify_api_authenticated.get_tracks_to_file(fd, d) assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'tracks', spotify_api_authenticated.licensor_name) def test_tracks_to_json( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_tracks_to_json.""" res = spotify_api_authenticated.get_tracks_to_json('2017-10-01') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'tracks', spotify_api_authenticated.licensor_name) def test_tracks_to_json_date( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_tracks_to_json with date argument.""" d = datetime.date(2017, 10, 1) res = spotify_api_authenticated.get_tracks_to_json(d) assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'tracks', spotify_api_authenticated.licensor_name) def test_users_to_file( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_users_to_file.""" with TemporaryFile() as fd: spotify_api_authenticated.get_users_to_file(fd, '2017-10-01') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'users', spotify_api_authenticated.licensor_name) def test_users_to_file_date( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_users_to_file with date argument.""" with TemporaryFile() as fd: d = datetime.date(2017, 10, 1) spotify_api_authenticated.get_users_to_file(fd, d) assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'users', spotify_api_authenticated.licensor_name) def test_users_to_json( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_users_to_json.""" res = spotify_api_authenticated.get_users_to_json('2017-10-01') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'users', spotify_api_authenticated.licensor_name) def test_users_to_json_date( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_users_to_json with date argument.""" d = datetime.date(2017, 10, 1) res = spotify_api_authenticated.get_users_to_json(d) assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'users', spotify_api_authenticated.licensor_name) def test_streams_to_file( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_streams_to_file.""" with TemporaryFile() as fd: spotify_api_authenticated.get_streams_to_file(fd, '2017-10-01', 'AT') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'streams', spotify_api_authenticated.licensor_name, 'AT') def test_streams_to_file_date( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_streams_to_file with date argument.""" with TemporaryFile() as fd: d = datetime.date(2017, 10, 1) spotify_api_authenticated.get_streams_to_file(fd, d, 'AT') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'streams', spotify_api_authenticated.licensor_name, 'AT') def test_streams_to_json( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_streams_to_json.""" res = spotify_api_authenticated.get_streams_to_json('2017-10-01', 'AT') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'streams', spotify_api_authenticated.licensor_name) def test_streams_to_json_date( mock_session, spotify_api_authenticated, mock_tracks_content, expected_tracks_content): """Test get_streams_to_json with date argument.""" d = datetime.date(2017, 10, 1) res = spotify_api_authenticated.get_streams_to_json(d, 'AT') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'streams', spotify_api_authenticated.licensor_name) def test_aggregated_streams_to_file( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_aggregated_streams_to_file.""" with TemporaryFile() as fd: spotify_api_authenticated \ .get_aggregated_streams_to_file(fd, '2017-10-01') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'aggregatedstreams', spotify_api_authenticated.licensor_name) def test_aggregated_streams_to_file_date( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_aggregated_streams_to_file with date argument.""" with TemporaryFile() as fd: d = datetime.date(2017, 10, 1) spotify_api_authenticated.get_aggregated_streams_to_file(fd, d) assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'aggregatedstreams', spotify_api_authenticated.licensor_name) def test_aggregated_streams_to_json( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_aggregated_streams_to_json.""" res = spotify_api_authenticated \ .get_aggregated_streams_to_json('2017-10-01') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'aggregatedstreams', spotify_api_authenticated.licensor_name) def test_aggregated_streams_to_json_date( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_aggregated_streams_to_json with date argument.""" d = datetime.date(2017, 10, 1) res = spotify_api_authenticated.get_aggregated_streams_to_json(d) assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'aggregatedstreams', spotify_api_authenticated.licensor_name) def test_sub_30_sec_streams_to_file( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_aggregated_streams_to_file.""" with TemporaryFile() as fd: spotify_api_authenticated.get_sub_30_sec_streams_to_file( fd, '2017-10-01', 'AT') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'sub_30_sec_streams', spotify_api_authenticated.licensor_name, 'AT') def test_sub_30_sec_streams_to_file_date( mock_session, spotify_api_authenticated, mock_tracks_content): """Test get_aggregated_streams_to_file with date argument.""" with TemporaryFile() as fd: d = datetime.date(2017, 10, 1) spotify_api_authenticated.get_sub_30_sec_streams_to_file(fd, d, 'AT') assert_get_to_file( fd, mock_tracks_content, mock_session, '2017/10/01', 'sub_30_sec_streams', spotify_api_authenticated.licensor_name, 'AT') def test_sub_30_sec_streams_to_json( mock_session, spotify_api_authenticated, expected_tracks_content): """Test get_sub_30_sec_streams_to_json.""" res = spotify_api_authenticated.get_sub_30_sec_streams_to_json( '2017-10-01', 'AT') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'sub_30_sec_streams', spotify_api_authenticated.licensor_name) def test_sub_30_sec_streams_to_json_date( mock_session, spotify_api_authenticated, mock_tracks_content, expected_tracks_content): """Test get_streams_to_json with date argument.""" d = datetime.date(2017, 10, 1) res = spotify_api_authenticated.get_sub_30_sec_streams_to_json(d, 'AT') assert_get_to_json( res, expected_tracks_content, mock_session, '2017/10/01', 'sub_30_sec_streams', spotify_api_authenticated.licensor_name)