"""Integration tests for top/trending endpoints. Endpoints tested: - /top-metrics (TestTopMetrics, TestTopMetricsGainers, TestTopMetricsWithTiktok) - /top-sound-recordings (TestTopSoundRecordings) - /top-accounts-metrics (TestTopAccountMetrics) - /market-ranks (TestMarketRanks) Video/channel top metrics live in test_video.py. Every test parametrizes on ALL_HEADERS (6 auth profiles) to verify access control. """ import pytest from analytics import config from analytics.constants.countries import ISO_ALPHA_2 from tests.integration.endpoints.conftest import ALL_HEADERS, assert_endpoint # --------------------------------------------------------------------------- # Top-endpoint-specific constants # --------------------------------------------------------------------------- class TestTopMetrics: """Integration tests for /top-metrics. Verifies top metrics with various filter combinations (country, sorting, artist IDs, UPC). Groups: 1. Filter variants — different param combinations (30 tests) """ # ── Group 1: Filter variants ────────────────────────────────── # # 6 headers × 5 param_variants = 30 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "params", [ pytest.param("", id="defaults"), pytest.param( "?limit=10&offset=0&country_code=UA&country_code=NO" "&country_code=DE&country_code=US&country_code=GB" "&order_by=growth_percentage_28_days&order_dir=ASC", id="country+growth_sort", ), pytest.param( "?limit=10&offset=0&country_code=UA&country_code=NO" "&country_code=DE&country_code=US&country_code=GB" "&order_by=release_date&order_dir=ASC" "&global_participant_ids=90097833-cc23-470e-8643-2268e156497f", id="country+release_sort+artist", ), pytest.param( "?limit=10&offset=0" "&order_by=release_date&order_dir=ASC" "&global_participant_ids=90097833-cc23-470e-8643-2268e156497f", id="release_sort+artist", ), pytest.param( "?limit=10&offset=0&upc=195497640560", id="upc_filter", ), ], ) def test_top_metrics(self, hdrs, params): """Top metrics returns items with all expected fields for each filter variant.""" assert_endpoint( config.TOP_METRICS_URL + params, headers=hdrs, expect_payload_keys=["items", "total_results"], expect_keys=[ "isrc", "streams_1_day", "growth_percentage_1_day", "streams_7_days", "growth_percentage_7_days", "streams_28_days", "growth_percentage_28_days", "streams_all_time", ], ) class TestTopMetricsGainers: """Integration tests for /top-metrics with daily/weekly gainer filters. Groups: 1. Gainer filter variants — daily and weekly data flags (12 tests) 2. Growth field presence — daily data returns growth fields (6 tests) """ # ── Group 1: Gainer filter variants ─────────────────────────── # # 6 headers × 2 filter_variants = 12 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "params", [ pytest.param( "?limit=10&offset=0&only_daily_data=true" "&order_by=growth_percentage_1_day&order_dir=DESC", id="daily", ), pytest.param( "?limit=10&offset=0&only_weekly_data=true" "&order_by=growth_percentage_7_days&order_dir=DESC", id="weekly", ), ], ) def test_gainers(self, hdrs, params): """Gainer filter returns top metrics with correct pagination keys.""" assert_endpoint( config.TOP_METRICS_URL + params, headers=hdrs, expect_payload_keys=["items", "total_results"], ) # ── Group 2: Growth field presence ──────────────────────────── # # 6 tests (one per header). @pytest.mark.parametrize("hdrs", ALL_HEADERS) def test_daily_data_returns_growth_fields(self, hdrs): """Daily gainer data includes growth_percentage_1_day and isrc fields.""" params = ( "?limit=10&offset=0&only_daily_data=true" "&order_by=growth_percentage_1_day&order_dir=DESC" ) payload = assert_endpoint( config.TOP_METRICS_URL + params, headers=hdrs, ) if payload["items"]: assert "growth_percentage_1_day" in payload["items"][0] assert "isrc" in payload["items"][0] class TestTopMetricsWithTiktok: """Integration tests for /top-metrics with TikTok data. Groups: 1. TikTok top metrics — country-filtered with TikTok included (6 tests) """ # ── Group 1: TikTok top metrics ─────────────────────────────── # # 6 tests (one per header). @pytest.mark.parametrize("hdrs", ALL_HEADERS) def test_with_tiktok(self, hdrs): """Top metrics with country filter includes TikTok data without error.""" params = ( "?limit=10&offset=0&country_code=UA&country_code=NO" "&country_code=DE&country_code=US&country_code=GB" "&order_by=growth_percentage_28_days&order_dir=ASC" ) assert_endpoint( config.TOP_METRICS_URL + params, headers=hdrs, ) class TestTopSoundRecordings: """Integration tests for /top-sound-recordings. Groups: 1. Filter variants — default and country+sort (12 tests) """ # ── Group 1: Filter variants ────────────────────────────────── # # 6 headers × 2 param_variants = 12 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "params", [ pytest.param("", id="defaults"), pytest.param( "?limit=10&offset=0&country_code=UA&country_code=NO" "&country_code=DE&country_code=US&country_code=GB" "&order_by=streams_1_day", id="country+sort", ), ], ) def test_top_sound_recordings(self, hdrs, params): """Top sound recordings returns items with isrc, streams, and growth fields.""" assert_endpoint( config.TOP_SOUND_RECORDINGS_URL + params, headers=hdrs, expect_keys=["isrc", "streams", "growth_percentage"], ) class TestTopAccountMetrics: """Integration tests for /top-accounts-metrics. Groups: 1. Filter variants — parametrized query combos (42 tests) """ EXPECTED_ITEM_KEYS = [ "label_id", "streams_7_days", "streams_28_days", "streams_all_time", ] # ── Group 1: Filter variants ────────────────────────────────── # # 6 headers × 7 param_variants = 42 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "params", [ pytest.param("", id="defaults"), pytest.param("?label_ids=26760", id="label_rimas"), pytest.param("?subaccount_ids=46189", id="subaccount_bad_bunny"), pytest.param("?include_subaccounts=true", id="include_subaccounts"), pytest.param("?countries=US", id="country_us"), pytest.param("?store_ids=286", id="store_spotify"), pytest.param( "?order_by=streams_7_days&order_dir=ASC&limit=5&offset=0", id="pagination_sort", ), ], ) def test_top_account_metrics(self, hdrs, params): """Top account metrics returns items with expected fields for each filter.""" assert_endpoint( config.TOP_ACCOUNTS_METRICS_URL + params, headers=hdrs, expect_payload_keys=[ "items", "total_count", "labels_count", "subaccounts_count", ], expect_keys=self.EXPECTED_ITEM_KEYS, ) class TestMarketRanks: """Integration tests for /market-ranks. Groups: 1. Filter variants — default and store-filtered (12 tests) """ ITEM_FIELDS = ["store_id", "country_code", "market_rank"] # ── Group 1: Filter variants ────────────────────────────────── # # 6 headers × 2 param_variants = 12 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "params", [ pytest.param("", id="defaults"), pytest.param("?store_ids=1", id="store_filter"), ], ) def test_market_ranks(self, hdrs, params): """Market ranks returns items with store_id, country_code, and market_rank.""" assert_endpoint( config.MARKET_RANKS_URL + params, headers=hdrs, expect_keys=self.ITEM_FIELDS, ) class TestTopMetricsCountryCsv: """Integration tests for comma-separated country_code on GET /top-metrics. The handler accepts the country filter as a comma-separated value (``?country_code=US,GB``) as well as the legacy repeated form (``?country_code=US&country_code=GB``). The comma form keeps the URL short enough to clear the edge query-string size limit that drops the repeated form (502) when many countries are sent. Groups: 1. CSV/repeated parity — the two forms yield identical payloads (6) 2. Large country list — the full ISO list as CSV still 200s (6) """ PAYLOAD_KEYS = ["items", "total_results"] # ── Group 1: CSV/repeated parity ────────────────────────────── # # 6 tests (one per header). @pytest.mark.parametrize("hdrs", ALL_HEADERS) def test_csv_matches_repeated(self, hdrs): """Comma-separated and repeated country_code return the same payload.""" repeated = assert_endpoint( config.TOP_METRICS_URL + "?country_code=UA&country_code=NO&country_code=DE&limit=10", headers=hdrs, expect_payload_keys=self.PAYLOAD_KEYS, ) csv = assert_endpoint( config.TOP_METRICS_URL + "?country_code=UA,NO,DE&limit=10", headers=hdrs, expect_payload_keys=self.PAYLOAD_KEYS, ) assert csv == repeated # ── Group 2: Large country list ─────────────────────────────── # # 6 tests (one per header). The full ISO country list as repeated keys is # far larger than the edge tolerates; as a single comma-separated value the # URL stays short and must 200. @pytest.mark.skip( reason="Flaky: backend 504s computing the 249-country aggregation. " "CSV parsing is covered deterministically by " "tests/unit/test_metrics_country_csv.py (GO-4832)." ) @pytest.mark.parametrize("hdrs", ALL_HEADERS) def test_large_country_list_csv(self, hdrs): """The full ISO country list as a single comma-separated value 200s.""" assert_endpoint( config.TOP_METRICS_URL + "?country_code=" + ",".join(ISO_ALPHA_2) + "&limit=10&offset=0", headers=hdrs, expect_payload_keys=self.PAYLOAD_KEYS, )