"""Unit tests for the streams breakdown 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 streams_breakdown SOURCES = [ {"id": 286, "name": "Spotify"}, {"id": 1, "name": "Apple Music"}, {"id": 716, "name": "Amazon Unlimited"}, ] SOS_BREAKDOWN_TIMESERIES = { "passive": [{"date": "2021-01-16", "value": 1}, {"date": "2021-01-17", "value": 2}], "active": [{"date": "2021-01-16", "value": 3}, {"date": "2021-01-17", "value": 4}], "collection": [ {"date": "2021-01-16", "value": 5}, {"date": "2021-01-17", "value": 6}, ], "unknown": [{"date": "2021-01-16", "value": 1}, {"date": "2021-01-17", "value": 2}], } SOURCE_OF_STREAMS = { "active": {"value": 0.2, "growth_percentage": None, "total": 2}, "passive": {"value": 0.2, "growth_percentage": None, "total": 2}, "collection": {"value": 0.4, "growth_percentage": None, "total": 4}, "unknown": {"value": 0.2, "growth_percentage": None, "total": 2}, "sources": SOURCES, } SOURCE_OF_STREAMS_WITH_TIMESERIES = { "active": { "value": 0.2, "growth_percentage": None, "total": 2, "items": SOS_BREAKDOWN_TIMESERIES["active"], }, "passive": { "value": 0.2, "growth_percentage": None, "total": 2, "items": SOS_BREAKDOWN_TIMESERIES["passive"], }, "collection": { "value": 0.4, "growth_percentage": None, "total": 4, "items": SOS_BREAKDOWN_TIMESERIES["collection"], }, "unknown": { "value": 0.2, "growth_percentage": None, "total": 2, "items": SOS_BREAKDOWN_TIMESERIES["unknown"], }, "sources": SOURCES, } SOURCE_OF_STREAMS_ZERO_STREAMS = { "active": {"value": 0, "growth_percentage": None, "total": 0}, "passive": {"value": 0, "growth_percentage": None, "total": 0}, "collection": {"value": 0, "growth_percentage": None, "total": 0}, "unknown": {"value": 0, "growth_percentage": None, "total": 0}, "sources": SOURCES, } SOURCE_OF_STREAMS_WITH_TIMESERIES_ZERO_STREAMS = { "active": {"value": 0, "growth_percentage": None, "total": 0, "items": []}, "passive": {"value": 0, "growth_percentage": None, "total": 0, "items": []}, "collection": {"value": 0, "growth_percentage": None, "total": 0, "items": []}, "unknown": {"value": 0, "growth_percentage": None, "total": 0, "items": []}, "sources": SOURCES, } STREAMS_BY_SUBSCRIPTION = { "subscription": {"value": 0.5, "growth_percentage": None, "total": 5}, "ad_supported": {"value": 0.2, "growth_percentage": None, "total": 2}, "mid_tier": {"value": 0.2, "growth_percentage": None, "total": 2}, "sources": SOURCES, } STREAMS_BY_SUBSCRIPTION_ZERO_STREAMS = { "subscription": {"value": 0, "growth_percentage": None, "total": 0}, "ad_supported": {"value": 0, "growth_percentage": None, "total": 0}, "mid_tier": {"value": 0, "growth_percentage": None, "total": 0}, "sources": SOURCES, } @pytest.fixture def mock_get_streams_breakdown_model_payload(): """Mock payload for streams breakdown model.""" return { "isrc": "TEST", "streams_passive": 2, "streams_active": 2, "streams_collection": 4, "subscription": 5, "ad_supported": 2, "mid_tier": 2, "streams": 10, } @pytest.fixture def mock_get_streams_breakdown_payload_zero_streams(): """Mock payload for streams breakdown model.""" return { "isrc": "TEST", "streams_passive": 0, "streams_active": 0, "streams_collection": 0, "subscription": 0, "ad_supported": 0, "mid_tier": 0, "streams": 0, } @pytest.fixture def expected_payload(): """Return expected payload from logic.""" return { "isrc": "TEST", "source_of_streams": SOURCE_OF_STREAMS, "streams_by_subscription": STREAMS_BY_SUBSCRIPTION, } @pytest.fixture def expected_payload_with_timeseries(): """Return expected payload with timeseries from logic.""" return { "isrc": "TEST", "source_of_streams": SOURCE_OF_STREAMS_WITH_TIMESERIES, "streams_by_subscription": STREAMS_BY_SUBSCRIPTION, } @pytest.fixture def expected_payload_zero_streams(): """Return expected payload from logic.""" return { "isrc": "TEST", "source_of_streams": SOURCE_OF_STREAMS_ZERO_STREAMS, "streams_by_subscription": STREAMS_BY_SUBSCRIPTION_ZERO_STREAMS, } @pytest.fixture def expected_payload_zero_streams_timeseries(): """Return expected payload from logic.""" return { "isrc": "TEST", "source_of_streams": SOURCE_OF_STREAMS_WITH_TIMESERIES_ZERO_STREAMS, "streams_by_subscription": STREAMS_BY_SUBSCRIPTION_ZERO_STREAMS, } @pytest.fixture def mock_add_outage_error_to_stores(): """Mock add_outage_error_to_stores.""" with patch( "sound_recordings.logic.streams_breakdown.add_outage_error_to_stores" ) as add_outage_error_to_stores: add_outage_error_to_stores.return_value = [ {"id": 286, "name": "Spotify"}, {"id": 1, "name": "Apple Music"}, {"id": 716, "name": "Amazon Unlimited"}, ] yield add_outage_error_to_stores @pytest.fixture def mock_get_streams_breakdown_success( mock_get_streams_breakdown_model_payload, mock_add_outage_error_to_stores ): """Mock successful get_streams_breakdown model response.""" with patch( "sound_recordings.models.streams_breakdown.get_streams_breakdown" ) as get_streams_breakdown: get_streams_breakdown.return_value = oto_response.Response( mock_get_streams_breakdown_model_payload ) with app.test_request_context(): yield get_streams_breakdown @pytest.fixture def mock_get_streams_breakdown_success_zero_streams( mock_get_streams_breakdown_payload_zero_streams, mock_add_outage_error_to_stores ): """Mock successful get_streams_breakdown response with zero streams.""" with patch( "sound_recordings.models.streams_breakdown.get_streams_breakdown" ) as get_streams_breakdown: get_streams_breakdown.return_value = oto_response.Response( mock_get_streams_breakdown_payload_zero_streams ) with app.test_request_context(): yield get_streams_breakdown @pytest.fixture def mock_get_streams_breakdown_failure(mock_add_outage_error_to_stores): """Mock failure streams_breakdown model response.""" with patch( "sound_recordings.models.streams_breakdown.get_streams_breakdown" ) as get_streams_breakdown: get_streams_breakdown.return_value = oto_response.Response(None) with app.test_request_context(): yield get_streams_breakdown @pytest.fixture def mock_store_availability(): """Mock store_availability.""" with patch( "sound_recordings.models.streams_breakdown.store_availability" ) as store_availability: store_availability.get_sources.return_value = [ {"id": 286, "name": "Spotify"}, {"id": 1, "name": "Apple Music"}, ] yield store_availability @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 get_streams_breakdown_response( mock_add_outage_error_to_stores, mock_get_max_available_date_success, request_context_for_label, mock_permissions_for_label, ): """Return response from get_streams_breakdown.""" return streams_breakdown.get_streams_breakdown( request_context_for_label, "TEST", [], [], None, None, ["theorchard"] ) @pytest.fixture def get_streams_breakdown_response_with_start_and_end_date( mock_add_outage_error_to_stores, request_context_for_label, mock_permissions_for_label, ): """Return response from get_streams_breakdown.""" return streams_breakdown.get_streams_breakdown( request_context_for_label, "TEST", [], [], "2019-11-01", "2019-12-01", ["theorchard"], ) @pytest.fixture def get_streams_breakdown_response_with_countries( mock_add_outage_error_to_stores, mock_get_max_available_date_success, request_context_for_label, mock_permissions_for_label, ): """Return response from get_streams_breakdown.""" return streams_breakdown.get_streams_breakdown( request_context_for_label, "TEST", ["DE", "NO", "US"], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=7 - 1), datetime.date(2018, 7, 6), ["theorchard"], ) @pytest.fixture def get_streams_breakdown_response_with_store_ids( mock_add_outage_error_to_stores, mock_get_max_available_date_success, request_context_for_label, mock_permissions_for_label, ): """Return response from get_streams_breakdown.""" return streams_breakdown.get_streams_breakdown( request_context_for_label, "TEST", [], [1, 286], datetime.date(2018, 7, 6) - datetime.timedelta(days=7 - 1), datetime.date(2018, 7, 6), ["theorchard"], ) def test_get_streams_breakdown_model_called( mock_get_streams_breakdown_success, permissions_for_label, get_streams_breakdown_response, mock_add_outage_error_to_stores, ): """Test model layer called from logic.""" mock_get_streams_breakdown_success.assert_called_once_with( permissions_for_label, "TEST", ["theorchard"], [], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=7 - 1), datetime.date(2018, 7, 6), ) def test_get_streams_breakdown_model_called_with_start_and_end_date( mock_get_streams_breakdown_success, permissions_for_label, get_streams_breakdown_response_with_start_and_end_date, mock_add_outage_error_to_stores, ): """Test model layer called from logic.""" mock_get_streams_breakdown_success.assert_called_once_with( permissions_for_label, "TEST", ["theorchard"], [], [], "2019-11-01", "2019-12-01", ) def test_get_streams_breakdown_model_called_with_countries( mock_get_streams_breakdown_success, permissions_for_label, get_streams_breakdown_response_with_countries, mock_add_outage_error_to_stores, ): """Test model layer called from logic.""" mock_get_streams_breakdown_success.assert_called_once_with( permissions_for_label, "TEST", ["theorchard"], ["DE", "NO", "US"], [], datetime.date(2018, 7, 6) - datetime.timedelta(days=7 - 1), datetime.date(2018, 7, 6), ) def test_get_streams_breakdown_model_called_with_store_ids( mock_get_streams_breakdown_success, permissions_for_label, get_streams_breakdown_response_with_store_ids, mock_add_outage_error_to_stores, ): """Test model layer called from logic.""" mock_get_streams_breakdown_success.assert_called_once_with( permissions_for_label, "TEST", ["theorchard"], [], [1, 286], datetime.date(2018, 7, 6) - datetime.timedelta(days=7 - 1), datetime.date(2018, 7, 6), ) def test_get_streams_breakdown_success( mock_get_streams_breakdown_success, expected_payload, get_streams_breakdown_response, mock_add_outage_error_to_stores, ): """Test successful response.""" assert get_streams_breakdown_response.status == 200 assert get_streams_breakdown_response.message == expected_payload def test_get_streams_breakdown_success_with_start_and_end_date( mock_get_streams_breakdown_success, expected_payload, get_streams_breakdown_response_with_start_and_end_date, mock_add_outage_error_to_stores, ): """Test successful response.""" assert get_streams_breakdown_response_with_start_and_end_date.status == 200 assert ( get_streams_breakdown_response_with_start_and_end_date.message == expected_payload ) def test_get_streams_breakdown_success_with_countries( mock_get_streams_breakdown_success, expected_payload, get_streams_breakdown_response_with_countries, mock_add_outage_error_to_stores, ): """Test successful response.""" assert get_streams_breakdown_response_with_countries.status == 200 assert get_streams_breakdown_response_with_countries.message == expected_payload def test_get_streams_breakdown_success_with_store_ids( mock_get_streams_breakdown_success, expected_payload, get_streams_breakdown_response_with_store_ids, mock_add_outage_error_to_stores, ): """Test successful response.""" assert get_streams_breakdown_response_with_store_ids.status == 200 assert get_streams_breakdown_response_with_store_ids.message == expected_payload def test_get_streams_breakdown_success_zero_streams( mock_get_streams_breakdown_success_zero_streams, expected_payload_zero_streams, get_streams_breakdown_response, mock_add_outage_error_to_stores, ): """Test successful response.""" assert get_streams_breakdown_response.status == 200 assert get_streams_breakdown_response.message == expected_payload_zero_streams def test_get_streams_breakdown_failure( mock_get_streams_breakdown_failure, get_streams_breakdown_response, mock_add_outage_error_to_stores, expected_payload, ): """Test get_streams_breakdown when model fails.""" assert get_streams_breakdown_response.status == 200 expected_payload["source_of_streams"]["active"] = {} expected_payload["source_of_streams"]["passive"] = {} expected_payload["source_of_streams"]["collection"] = {} expected_payload["source_of_streams"]["unknown"] = {} expected_payload["streams_by_subscription"]["subscription"] = {} expected_payload["streams_by_subscription"]["ad_supported"] = {} expected_payload["streams_by_subscription"]["mid_tier"] = {} assert get_streams_breakdown_response.message == expected_payload def test_get_streams_breakdown_failure_with_start_and_end_date( mock_get_streams_breakdown_failure, get_streams_breakdown_response_with_start_and_end_date, mock_add_outage_error_to_stores, expected_payload, ): """Test get_streams_breakdown when model fails.""" assert get_streams_breakdown_response_with_start_and_end_date.status == 200 expected_payload["source_of_streams"]["active"] = {} expected_payload["source_of_streams"]["passive"] = {} expected_payload["source_of_streams"]["collection"] = {} expected_payload["source_of_streams"]["unknown"] = {} expected_payload["streams_by_subscription"]["subscription"] = {} expected_payload["streams_by_subscription"]["ad_supported"] = {} expected_payload["streams_by_subscription"]["mid_tier"] = {} assert ( get_streams_breakdown_response_with_start_and_end_date.message == expected_payload ) def test_get_streams_breakdown_failure_with_countries( mock_get_streams_breakdown_failure, get_streams_breakdown_response_with_countries, mock_add_outage_error_to_stores, expected_payload, ): """Test get_streams_breakdown when model fails.""" assert get_streams_breakdown_response_with_countries.status == 200 expected_payload["source_of_streams"]["active"] = {} expected_payload["source_of_streams"]["passive"] = {} expected_payload["source_of_streams"]["collection"] = {} expected_payload["source_of_streams"]["unknown"] = {} expected_payload["streams_by_subscription"]["subscription"] = {} expected_payload["streams_by_subscription"]["ad_supported"] = {} expected_payload["streams_by_subscription"]["mid_tier"] = {} assert get_streams_breakdown_response_with_countries.message == expected_payload def test_get_streams_breakdown_failure_with_store_ids( mock_get_streams_breakdown_failure, get_streams_breakdown_response_with_store_ids, mock_add_outage_error_to_stores, expected_payload, ): """Test get_streams_breakdown when model fails.""" assert get_streams_breakdown_response_with_store_ids.status == 200 expected_payload["source_of_streams"]["active"] = {} expected_payload["source_of_streams"]["passive"] = {} expected_payload["source_of_streams"]["collection"] = {} expected_payload["source_of_streams"]["unknown"] = {} expected_payload["streams_by_subscription"]["subscription"] = {} expected_payload["streams_by_subscription"]["ad_supported"] = {} expected_payload["streams_by_subscription"]["mid_tier"] = {} assert get_streams_breakdown_response_with_store_ids.message == expected_payload