"""Unit tests for sound recording playlists logic layer.""" import datetime from unittest.mock import patch import pytest from oto import response as oto_response from sound_recordings.api import app from sound_recordings.logic import playlists as playlists @pytest.fixture def mock_get_playlists_model_payload(): """Mock payload for sound recording playlists model.""" return [ { "playlist_name": "Totally Stress Free", "playlist_url": "https://open.spotify.com/playlist/37i9dQZF1DWT7XSlwvR1ar", "playlist_image": "https://dhfsk7jl2g0km.cloudfront.net/" "playlist-37i9dQZF1DWT7XSlwvR1ar.jpeg", "store_id": 286, "streams": 43126, }, { "playlist_name": "Feel-Good Indie Rock", "playlist_url": "https://open.spotify.com/" "playlist/37i9dQZF1DX2sUQwD7tbmL", "playlist_image": "https://dhfsk7jl2g0km.cloudfront.net/" "playlist-37i9dQZF1DX2sUQwD7tbmL.jpeg", "store_id": 286, "streams": 41437, }, { "playlist_name": None, "playlist_url": "spotify-personalized-playlist:dailyMix", "playlist_image": None, "store_id": 286, "streams": 10682, }, { "playlist_name": "Indie Workout", "playlist_url": "https://open.spotify.com/playlist/37i9dQZF1DXaRL7xbcDl7X", "playlist_image": "https://dhfsk7jl2g0km.cloudfront.net/" "playlist-37i9dQZF1DXaRL7xbcDl7X.jpeg", "store_id": 286, "streams": 3321, }, { "playlist_name": None, "playlist_url": "https://itunes.apple.com/us/station/idra.1480914824", "playlist_image": None, "store_id": 1, "streams": 29, }, ] @pytest.fixture def mock_get_total_playlist_streams_model_payload(): """Mock payload for sound recording playlists total streams model.""" return 117642 @pytest.fixture def expected_payload(): """Return expected payload from logic.""" return { "isrc": "isrc", "total_playlist_streams": 117642, "sources": [ {"id": 1, "name": "Apple Music", "error": {"code": "unreliable"}}, {"id": 187, "name": "Amazon Music"}, {"id": 286, "name": "Spotify"}, ], "playlists": [ { "playlist_name": "Totally Stress Free", "playlist_url": "https://open.spotify.com/playlist/37i9dQZF1DWT7XSlwvR1ar", "playlist_image": "https://dhfsk7jl2g0km.cloudfront.net/" "playlist-37i9dQZF1DWT7XSlwvR1ar.jpeg", "store_id": 286, "streams": 43126, }, { "playlist_name": "Feel-Good Indie Rock", "playlist_url": "https://open.spotify.com/playlist/37i9dQZF1DX2sUQwD7tbmL", "playlist_image": "https://dhfsk7jl2g0km.cloudfront.net/" "playlist-37i9dQZF1DX2sUQwD7tbmL.jpeg", "store_id": 286, "streams": 41437, }, { "playlist_name": None, "playlist_url": "spotify-personalized-playlist:dailyMix", "playlist_image": None, "store_id": 286, "streams": 10682, }, { "playlist_name": "Indie Workout", "playlist_url": "https://open.spotify.com/playlist/37i9dQZF1DXaRL7xbcDl7X", "playlist_image": "https://dhfsk7jl2g0km.cloudfront.net/" "playlist-37i9dQZF1DXaRL7xbcDl7X.jpeg", "store_id": 286, "streams": 3321, }, { "playlist_name": None, "playlist_url": "https://itunes.apple.com/us/station/idra.1480914824", "playlist_image": None, "store_id": 1, "streams": 29, }, ], } @pytest.fixture def mock_add_outage_error_to_stores(): """Mock add_outage_error_to_stores.""" with patch( "sound_recordings.logic.playlists.add_outage_error_to_stores" ) as add_outage_error_to_stores: add_outage_error_to_stores.return_value = [ {"id": 1, "name": "Apple Music", "error": {"code": "unreliable"}}, {"id": 187, "name": "Amazon Music"}, {"id": 286, "name": "Spotify"}, ] yield add_outage_error_to_stores @pytest.fixture def mock_get_playlists_model_success(mock_get_playlists_model_payload): """Mock successful get_playlists model response.""" with patch("sound_recordings.models.playlists.get_playlists") as get_playlists: get_playlists.return_value = oto_response.Response( mock_get_playlists_model_payload ) with app.test_request_context(): with app.test_request_context(): yield get_playlists @pytest.fixture def mock_get_total_playlist_streams_model_success( mock_get_total_playlist_streams_model_payload, ): """Mock successful get_total_playlist_streams model response.""" with patch( "sound_recordings.models.playlists.get_total_playlist_streams" ) as get_total_playlist_streams: get_total_playlist_streams.return_value = oto_response.Response( mock_get_total_playlist_streams_model_payload ) with app.test_request_context(): with app.test_request_context(): yield get_total_playlist_streams @pytest.fixture def mock_get_max_available_date_success(): """Mock successful get_max_available_date model response.""" with patch( "sound_recordings.models.data_availability.get_max_available_date" ) as get_max_available_date: get_max_available_date.return_value = "2018-07-06" yield get_max_available_date @pytest.fixture def mock_get_max_available_date_failure(): """Mock failure get_max_available_date model response.""" with patch( "sound_recordings.logic.playlists.data_availability \ .get_max_available_date" ) as get_playlists: get_playlists.return_value = None yield get_playlists @pytest.fixture def mock_get_playlists_model_failure(): """Mock failure get_playlists model response.""" with patch("sound_recordings.models.playlists.get_playlists") as get_playlists: get_playlists.return_value = oto_response.Response([]) with app.test_request_context(): yield get_playlists @pytest.fixture def mock_get_total_playlist_streams_model_failure(): """Mock failure get_total_playlist_streams model response.""" with patch( "sound_recordings.models.playlists.get_total_playlist_streams" ) as get_total_playlist_streams: get_total_playlist_streams.return_value = oto_response.Response(0) with app.test_request_context(): yield get_total_playlist_streams @pytest.fixture def mock_store_availability(): """Mock store_availability.""" with patch( "sound_recordings.logic.playlists.store_availability" ) as store_availability: store_availability.get_download_sources.return_value = [ {"id": 1, "name": "Apple Music"}, {"id": 187, "name": "Amazon Music"}, {"id": 286, "name": "Spotify"}, ] store_availability.get_download_store_names.return_value = { 1: "Apple Music", 187: "Amazon Music", 286: "Spotify", } yield store_availability class TestGetPlaylistsWithLabel: """Test get_playlists with label.""" isrc = "isrc" @pytest.fixture def response( self, mock_get_playlists_model_success, mock_get_total_playlist_streams_model_success, mock_get_max_available_date_success, mock_add_outage_error_to_stores, request_context_for_label, mock_permissions_for_label, mock_store_availability, ): """Run get_playlists.""" return playlists.get_playlists( request_context_for_label, self.isrc, ["theorchard"], [], [] ) def test_succeeds(self, response, expected_payload): """Test successful response.""" assert response.status == 200 response_message = response.message response_sources = response_message["sources"] expected_sources = expected_payload["sources"] del response_message["sources"] del expected_payload["sources"] assert response.message == expected_payload assert sorted(response_sources, key=lambda x: x["id"]) == sorted( expected_sources, key=lambda x: x["id"] ) def test_get_playlists_model_is_called( self, response, mock_get_playlists_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test get_playlists_model is called.""" mock_get_playlists_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), 0, 0, ) def test_total_playlist_streams_model_is_called( self, response, mock_get_total_playlist_streams_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test total_playlist_streams_model is called.""" mock_get_total_playlist_streams_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), ) class TestGetPlaylistsWithLabelSevenDaysBack: """Test get_playlists with label.""" isrc = "isrc" start_date = datetime.date(2018, 6, 30) end_date = datetime.date(2018, 7, 6) @pytest.fixture def response( self, mock_get_playlists_model_success, mock_get_total_playlist_streams_model_success, mock_get_max_available_date_success, mock_add_outage_error_to_stores, request_context_for_label, mock_permissions_for_label, mock_store_availability, ): """Run get_playlists.""" return playlists.get_playlists( request_context_for_label, self.isrc, ["theorchard"], [], [], self.start_date, self.end_date, ) def test_succeeds(self, response, expected_payload): """Test successful response.""" assert response.status == 200 response_message = response.message response_sources = response_message["sources"] expected_sources = expected_payload["sources"] del response_message["sources"] del expected_payload["sources"] assert response.message == expected_payload assert sorted(response_sources, key=lambda x: x["id"]) == sorted( expected_sources, key=lambda x: x["id"] ) def test_get_playlists_model_is_called( self, response, mock_get_playlists_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test get_playlists_model is called.""" mock_get_playlists_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], [], [], datetime.date(2018, 6, 30), datetime.date(2018, 7, 6), 0, 0, ) def test_total_playlist_streams_model_is_called( self, response, mock_get_total_playlist_streams_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test total_playlist_streams_model is called.""" mock_get_total_playlist_streams_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], [], [], datetime.date(2018, 6, 30), datetime.date(2018, 7, 6), ) class TestGetPlaylistsWithSubaccount: """Test get_playlists with subaccount.""" isrc = "isrc" @pytest.fixture def response( self, mock_get_playlists_model_success, mock_get_total_playlist_streams_model_success, mock_get_max_available_date_success, mock_add_outage_error_to_stores, mock_permissions_for_subaccount, request_context_for_subaccount, mock_store_availability, ): """Run get_playlists.""" return playlists.get_playlists( request_context_for_subaccount, self.isrc, ["theorchard"], [], [] ) def test_succeeds(self, response, expected_payload): """Test successful response.""" assert response.status == 200 response_message = response.message response_sources = response_message["sources"] expected_sources = expected_payload["sources"] del response_message["sources"] del expected_payload["sources"] assert response.message == expected_payload assert sorted(response_sources, key=lambda x: x["id"]) == sorted( expected_sources, key=lambda x: x["id"] ) def test_get_playlists_model_is_called( self, response, mock_get_playlists_model_success, mock_add_outage_error_to_stores, permissions_for_subaccount, ): """Test get_playlists_model is called.""" mock_get_playlists_model_success.assert_called_once_with( permissions_for_subaccount, self.isrc, ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), 0, 0, ) def test_total_playlist_streams_model_is_called( self, response, mock_get_total_playlist_streams_model_success, mock_add_outage_error_to_stores, permissions_for_subaccount, ): """Test total_playlist_streams_model is called.""" mock_get_total_playlist_streams_model_success.assert_called_once_with( permissions_for_subaccount, self.isrc, ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), ) class TestGetPlaylistsFilteredByCountries: """Test get_playlists filtered by countries.""" isrc = "isrc" countries = ["US", "NO", "SE"] store_ids = [1, 187, 286] @pytest.fixture def response( self, mock_get_playlists_model_success, mock_get_total_playlist_streams_model_success, mock_get_max_available_date_success, mock_add_outage_error_to_stores, request_context_for_label, mock_permissions_for_label, mock_store_availability, ): """Run get_playlists.""" return playlists.get_playlists( request_context_for_label, self.isrc, ["theorchard"], self.countries, self.store_ids, ) def test_succeeds(self, response, expected_payload): """Test successful response.""" assert response.status == 200 response_message = response.message response_sources = response_message["sources"] expected_sources = expected_payload["sources"] del response_message["sources"] del expected_payload["sources"] assert response.message == expected_payload assert sorted(response_sources, key=lambda x: x["id"]) == sorted( expected_sources, key=lambda x: x["id"] ) def test_get_playlists_model_is_called( self, response, mock_get_playlists_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test get_playlists_model is called.""" mock_get_playlists_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], self.countries, self.store_ids, datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), 0, 0, ) def test_total_playlist_streams_model_is_called( self, response, mock_get_total_playlist_streams_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test total_playlist_streams_model is called.""" mock_get_total_playlist_streams_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], self.countries, self.store_ids, datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), ) class TestGetPlaylistsWithPagination: """Test get_playlists with pagination parameters.""" isrc = "isrc" limit = 10 offset = 20 @pytest.fixture def response( self, mock_get_playlists_model_success, mock_get_total_playlist_streams_model_success, mock_get_max_available_date_success, mock_add_outage_error_to_stores, request_context_for_label, mock_permissions_for_label, mock_store_availability, ): """Run get_playlists.""" return playlists.get_playlists( request_context_for_label, self.isrc, ["theorchard"], [], [], None, None, self.offset, self.limit, ) def test_succeeds(self, response, expected_payload): """Test successful response.""" assert response.status == 200 response_message = response.message response_sources = response_message["sources"] expected_sources = expected_payload["sources"] del response_message["sources"] del expected_payload["sources"] assert response.message == expected_payload assert sorted(response_sources, key=lambda x: x["id"]) == sorted( expected_sources, key=lambda x: x["id"] ) def test_get_playlists_model_is_called( self, response, mock_get_playlists_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test get_playlists_model is called.""" mock_get_playlists_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), self.offset, self.limit, ) def test_total_playlist_streams_model_is_called( self, response, mock_get_total_playlist_streams_model_success, mock_add_outage_error_to_stores, permissions_for_label, ): """Test total_playlist_streams_model is called.""" mock_get_total_playlist_streams_model_success.assert_called_once_with( permissions_for_label, self.isrc, ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=28 - 1), datetime.date(2018, 7, 6), )