"""Unit tests for the aggregate-streams logic layer.""" from unittest.mock import patch import pytest from analytics.api import app from analytics.logic import aggregate_streams @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_store_availability(mocker): mocker.patch( "analytics.logic.aggregate_streams.store_availability.get_store_ids", return_value=[1, 286], ) @pytest.fixture def permissions(): 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(isrcs=("ISRC1", "ISRC2"), countries=None, store_ids=None): return { "isrcs": list(isrcs), "country_ids": countries or [], "store_ids": store_ids or [], "distributors": ["theorchard", "sme", "awal"], } class TestAggregateStreamsHappyPath: def test_sums_streams_all_time_and_growth_per_isrc(self, mocker, permissions): """Rows are grouped by isrc, fields summed per ISRC.""" rows = [ {"isrc": "ISRC1", "streams_all_time": 40000, "growth_percentage": 0.35}, {"isrc": "ISRC2", "streams_all_time": 100, "growth_percentage": 0.2}, ] mocker.patch( "analytics.logic.aggregate_streams.AggregateStreamsBulk.execute", return_value=rows, ) response = aggregate_streams.get_aggregate_streams(_query_params(), permissions) assert response.status == 200 assert response.message == { "ISRC1": {"streams_all_time": 40000, "growth_percentage_7_days": 0.35}, "ISRC2": {"streams_all_time": 100, "growth_percentage_7_days": 0.2}, } class TestAggregateStreamsNoRows: def test_empty_query_returns_zeros_per_isrc(self, mocker, permissions): """Missing rows for an ISRC → zeroed body.""" mocker.patch( "analytics.logic.aggregate_streams.AggregateStreamsBulk.execute", return_value=[], ) response = aggregate_streams.get_aggregate_streams(_query_params(), permissions) assert response.status == 200 assert response.message == { "ISRC1": {"streams_all_time": 0, "growth_percentage_7_days": None}, "ISRC2": {"streams_all_time": 0, "growth_percentage_7_days": None}, } class TestAggregateStreamsEmptyStoreIds: def test_unavailable_store_ids_skip_query(self, mocker, permissions): """store_ids that intersect to empty → query skipped, zeros returned.""" execute = mocker.patch( "analytics.logic.aggregate_streams.AggregateStreamsBulk.execute" ) response = aggregate_streams.get_aggregate_streams( _query_params(store_ids=[99999]), permissions ) assert response.status == 200 assert response.message == { "ISRC1": {"streams_all_time": 0, "growth_percentage_7_days": None}, "ISRC2": {"streams_all_time": 0, "growth_percentage_7_days": None}, } execute.assert_not_called() class TestAggregateStreamsCountryFilter: def test_forwards_country_ids_to_query(self, mocker, permissions): """country_ids forwarded in query_input.""" MockQuery = mocker.patch( "analytics.logic.aggregate_streams.AggregateStreamsBulk" ) MockQuery.return_value.execute.return_value = [] aggregate_streams.get_aggregate_streams( _query_params(countries=["US", "GB"]), permissions ) query_input = MockQuery.call_args[0][0] assert query_input["country_ids"] == ["US", "GB"] assert query_input["isrcs"] == ["ISRC1", "ISRC2"]