"""Unit tests for the top markets logic layer.""" from unittest.mock import patch import pytest from analytics.api import app from analytics.logic import top_markets SOURCES = [{"id": 286, "name": "Spotify"}, {"id": 1405, "name": "VKontakte"}] @pytest.fixture(autouse=True) def mock_context(): """Run in Flask request 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_sources(mocker): """Mock add_outage_error_to_stores + store_availability.""" mocker.patch( "analytics.logic.top_markets.add_outage_error_to_stores", return_value=SOURCES, ) mocker.patch( "analytics.logic.top_markets.store_availability.get_store_ids", return_value=[1, 286], ) @pytest.fixture def mock_top_markets_rows(): """Rows returned by TopMarkets.execute.""" return [ { "country_code": "UA", "orchard_region_name": "Kyiv", "streams_7_days": 42, "growth_percentage": 0.333, }, { "country_code": "US", "orchard_region_name": "NYC", "streams_7_days": 37, "growth_percentage": 0.42, }, ] @pytest.fixture def expected_items(): """Expected items section of the schema-rendered response.""" return [ { "country_code": "UA", "region": "Kyiv", "streams": 42, "growth_percentage": 0.333, }, { "country_code": "US", "region": "NYC", "streams": 37, "growth_percentage": 0.42, }, ] @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], } def _query_params(countries=None, store_ids=None): return { "isrc": "TEST", "country_ids": countries or [], "store_ids": store_ids or [], "distributors": ["theorchard", "sme", "awal"], } class TestGetTopMarketsSuccess: """Test get_top_markets with a non-empty query result.""" def test_default(self, mocker, permissions, mock_top_markets_rows, expected_items): """Top markets returns isrc, items, sources.""" mocker.patch( "analytics.logic.top_markets.TopMarkets.execute", return_value=mock_top_markets_rows, ) response = top_markets.get_top_markets(_query_params(), permissions) assert response.status == 200 assert response.message == { "isrc": "TEST", "items": expected_items, "sources": SOURCES, } def test_with_countries( self, mocker, permissions, mock_top_markets_rows, expected_items ): """country_ids is forwarded to the query.""" MockQuery = mocker.patch("analytics.logic.top_markets.TopMarkets") MockQuery.return_value.execute.return_value = mock_top_markets_rows response = top_markets.get_top_markets( _query_params(countries=["US", "GB"]), permissions ) assert response.status == 200 assert response.message == { "isrc": "TEST", "items": expected_items, "sources": SOURCES, } query_input = MockQuery.call_args[0][0] assert query_input["country_ids"] == ["US", "GB"] def test_with_store_ids( self, mocker, permissions, mock_top_markets_rows, expected_items ): """store_ids intersect with available stores.""" MockQuery = mocker.patch("analytics.logic.top_markets.TopMarkets") MockQuery.return_value.execute.return_value = mock_top_markets_rows response = top_markets.get_top_markets( _query_params(store_ids=[1, 286]), permissions ) assert response.status == 200 assert response.message == { "isrc": "TEST", "items": expected_items, "sources": SOURCES, } query_input = MockQuery.call_args[0][0] assert query_input["store_ids"] == [1, 286] class TestGetTopMarketsEmpty: """Test get_top_markets when the query returns no rows.""" def test_empty_result(self, mocker, permissions): """Empty query result returns body with empty items.""" mocker.patch("analytics.logic.top_markets.TopMarkets.execute", return_value=[]) response = top_markets.get_top_markets(_query_params(), permissions) assert response.status == 200 assert response.message == { "isrc": "TEST", "items": [], "sources": SOURCES, } class TestGetTopMarketsEmptyStoreIds: """Test get_top_markets when store_ids intersection yields empty.""" def test_empty_store_ids_returns_empty_body_without_query( self, mocker, permissions ): """Unavailable store_ids → empty body, query never called.""" execute = mocker.patch("analytics.logic.top_markets.TopMarkets.execute") response = top_markets.get_top_markets( _query_params(store_ids=[99999]), permissions ) assert response.status == 200 assert response.message == { "isrc": "TEST", "items": [], "sources": SOURCES, } execute.assert_not_called()