"""Unit tests for the participant track streams logic layer.""" import datetime from unittest.mock import patch import pytest from analytics.api import app from analytics.logic.participant_track_streams import ( get_track_streams_all, get_track_streams_store, ) MOCK_STORE_IDS = [1, 286] @pytest.fixture(autouse=True) def mock_context(): """Mock successful context.""" with app.test_request_context(): yield @pytest.fixture(autouse=True) def no_cache(): """Mock out caching of response.""" with patch("analytics.connectors.redis.client.get") as get: get.return_value = None yield get @pytest.fixture(autouse=True) def mock_store_availability(mocker): """Mock store_availability.""" mocker.patch( "analytics.logic.participant_track_streams.store_availability.get_store_ids", return_value=MOCK_STORE_IDS, ) mocker.patch( "analytics.logic.participant_track_streams.store_availability.get_store_names", return_value={1: "Apple Music", 286: "Spotify"}, ) @pytest.fixture def permissions(): """Return permissions with label access.""" return { "permission_label_ids": [7123], "permission_artist_ids": None, "permission_subaccount_ids": None, "permission_label_participant_ids": None, "permission_feed_ids": [1, 2], } @pytest.fixture def subaccount_permissions(): """Return permissions with subaccount access.""" return { "permission_label_ids": None, "permission_artist_ids": None, "permission_subaccount_ids": [345], "permission_label_participant_ids": None, "permission_feed_ids": [1, 2], } @pytest.fixture def mock_streams_all_query_result(): """Mock query result for track streams all (simulates SQLAlchemy Row objects).""" return [ { "date": datetime.date(2018, 6, 28), "streams": 500, "streams_with_skips": 0, "skips": None, "saves": None, }, { "date": datetime.date(2018, 6, 29), "streams": 2000, "streams_with_skips": 1000, "skips": 200, "saves": 400, }, ] @pytest.fixture def expected_payload_track_streams_all(): """Return expected payload from get_track_streams_all logic.""" return { "global_participant_id": "f67b0892-f0bc-4575-b23e-59566ccb44bc", "items": [ { "streams": 500, "saves": None, "skip_rate": None, "date": "2018-06-28T00:00:00Z", }, { "streams": 2000, "saves": 400, "skip_rate": 0.16666666666666666, "date": "2018-06-29T00:00:00Z", }, ], } class TestGetTrackStreamsAllWithSubaccount: """Test get_track_streams_all with Subaccount.""" global_participant_id = "f67b0892-f0bc-4575-b23e-59566ccb44bc" start_date = datetime.date(2018, 6, 28) end_date = datetime.date(2018, 6, 29) def _query_params(self, countries=None): return { "global_participant_id": self.global_participant_id, "distributors": ["theorchard", "sme", "awal"], "country_ids": countries or [], "store_ids": [], "start_date": self.start_date, "end_date": self.end_date, } def test_get_track_streams_all_success( self, mocker, subaccount_permissions, mock_streams_all_query_result, expected_payload_track_streams_all, ): """Test successful response.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsAll.execute", return_value=mock_streams_all_query_result, ) response = get_track_streams_all( self._query_params(countries=["GB", "US"]), subaccount_permissions, ) assert response.status == 200 assert response.message == expected_payload_track_streams_all def test_get_track_streams_all_failure(self, mocker, subaccount_permissions): """Test failure response when query returns empty.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsAll.execute", return_value=[], ) response = get_track_streams_all( self._query_params(countries=["GB", "US"]), subaccount_permissions, ) assert response.status == 200 assert response.message == { "global_participant_id": self.global_participant_id, "items": [], } class TestGetTrackStreamsAllWithLabel: """Test get_track_streams_all with Label.""" global_participant_id = "f67b0892-f0bc-4575-b23e-59566ccb44bc" start_date = datetime.date(2018, 6, 28) end_date = datetime.date(2018, 6, 29) def _query_params(self): return { "global_participant_id": self.global_participant_id, "distributors": ["theorchard", "sme", "awal"], "country_ids": [], "store_ids": [], "start_date": self.start_date, "end_date": self.end_date, } def test_get_track_streams_all_success( self, mocker, permissions, mock_streams_all_query_result, expected_payload_track_streams_all, ): """Test successful response.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsAll.execute", return_value=mock_streams_all_query_result, ) response = get_track_streams_all( self._query_params(), permissions, ) assert response.status == 200 assert response.message == expected_payload_track_streams_all def test_get_track_streams_all_failure(self, mocker, permissions): """Test failure response when query returns empty.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsAll.execute", return_value=[], ) response = get_track_streams_all( self._query_params(), permissions, ) assert response.status == 200 assert response.message == { "global_participant_id": self.global_participant_id, "items": [], } @pytest.fixture def mock_streams_store_query_result(): """Mock query result for track streams store.""" return [ { "store_id": 1, "date": datetime.date(2018, 6, 28), "streams": 500, "streams_with_skips": 0, "skips": None, "saves": None, }, { "store_id": 1, "date": datetime.date(2018, 6, 29), "streams": 3000, "streams_with_skips": 2000, "skips": 300, "saves": 600, }, { "store_id": 286, "date": datetime.date(2018, 6, 28), "streams": 3000, "streams_with_skips": 2000, "skips": 300, "saves": 600, }, { "store_id": 286, "date": datetime.date(2018, 6, 29), "streams": 2000, "streams_with_skips": 1000, "skips": 200, "saves": 400, }, { "store_id": 1, "date": datetime.date(2018, 6, 30), "streams": 1000, "streams_with_skips": 0, "skips": None, "saves": None, }, ] @pytest.fixture def payload_track_streams_store(): """Return expected payload from get_track_streams_store logic.""" return { "global_participant_id": "global_participant_id", "stores": [ { "id": 1, "name": "Apple Music", "items": [ { "streams": 500, "saves": None, "skip_rate": None, "date": "2018-06-28T00:00:00Z", }, { "streams": 3000, "saves": 600, "skip_rate": 0.13043478260869565, "date": "2018-06-29T00:00:00Z", }, { "streams": 1000, "saves": None, "skip_rate": None, "date": "2018-06-30T00:00:00Z", }, ], }, { "id": 286, "name": "Spotify", "items": [ { "streams": 3000, "saves": 600, "skip_rate": 0.13043478260869565, "date": "2018-06-28T00:00:00Z", }, { "streams": 2000, "saves": 400, "skip_rate": None, "date": "2018-06-29T00:00:00Z", }, ], }, ], } class TestGetTrackStreamsStoreWithSubaccount: """Test get_track_streams_store with Subaccount.""" global_participant_id = "f67b0892-f0bc-4575-b23e-59566ccb44bc" start_date = datetime.date(2018, 6, 28) end_date = datetime.date(2018, 6, 29) def _query_params(self, countries=None): return { "global_participant_id": self.global_participant_id, "distributors": ["theorchard", "sme", "awal"], "country_ids": countries or [], "store_ids": [], "start_date": self.start_date, "end_date": self.end_date, } def test_get_track_streams_store_success( self, mocker, subaccount_permissions, mock_streams_store_query_result, payload_track_streams_store, ): """Test successful response.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsStore.execute", return_value=mock_streams_store_query_result, ) response = get_track_streams_store( self._query_params(countries=["GB", "US"]), subaccount_permissions, ) assert response.status == 200 assert sorted(response.message) == sorted(payload_track_streams_store) def test_get_track_streams_store_failure(self, mocker, subaccount_permissions): """Test failure response when query returns empty.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsStore.execute", return_value=[], ) response = get_track_streams_store( self._query_params(countries=["GB", "US"]), subaccount_permissions, ) assert response.status == 200 assert response.message == { "global_participant_id": self.global_participant_id, "stores": [], } class TestGetTrackStreamsStoreWithLabel: """Test get_track_streams_store with Label.""" global_participant_id = "f67b0892-f0bc-4575-b23e-59566ccb44bc" start_date = datetime.date(2018, 6, 28) end_date = datetime.date(2018, 6, 29) def _query_params(self): return { "global_participant_id": self.global_participant_id, "distributors": ["theorchard", "sme", "awal"], "country_ids": [], "store_ids": [], "start_date": self.start_date, "end_date": self.end_date, } def test_get_track_streams_store_success( self, mocker, permissions, mock_streams_store_query_result, payload_track_streams_store, ): """Test successful response.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsStore.execute", return_value=mock_streams_store_query_result, ) response = get_track_streams_store( self._query_params(), permissions, ) assert response.status == 200 assert sorted(response.message) == sorted(payload_track_streams_store) def test_get_track_streams_store_failure(self, mocker, permissions): """Test failure response when query returns empty.""" mocker.patch( "analytics.logic.participant_track_streams.ParticipantTrackStreamsStore.execute", return_value=[], ) response = get_track_streams_store( self._query_params(), permissions, ) assert response.status == 200 assert response.message == { "global_participant_id": self.global_participant_id, "stores": [], }