"""Integration tests for the demographics endpoint. Endpoints tested: - /demographics-2 (TestDemographics2) Access control varies by entity type: - Accounts: uses account-level ACCESS_RULES (label/subaccount) - Artists: uses participant-level access by gp_id - Songs: uses song-level access by ISRC """ import pytest from analytics import config from tests.integration.endpoints.conftest import ( ALL_HEADERS, BAD_BUNNY, RIMAS, assert_endpoint, has_access, profile_id, ) class TestDemographics2: """Integration tests for /demographics-2. Verifies demographics data for accounts, artists, and songs across country and date filters. Access control varies by entity type: - Accounts: uses account-level ACCESS_RULES (label/subaccount) - Artists: uses participant-level access by gp_id - Songs: uses song-level access by ISRC Groups: 1. For accounts — vendor and subaccount (48 tests) 2. For artists — by global_participant_id (48 tests) 3. For songs — by ISRC with date/country filters (48 tests) 4. For songs all-time — by ISRC with store_ids (24 tests) """ # Bad Bunny (RIMAS 26760): employee + D3 + subaccount + label_and_subaccount have access # Local Natives (Frenchkiss 6971): employee + label + label_and_subaccount + artist ACCESS_RULES_ARTISTS = { "e49ea9f9-0a74-4623-b3c6-ff819c7c0a4d": [ "1100", "99918924", "99918926", "99918927", ], "95ee273c-09cb-411c-ae00-8806e9ca938e": [ "1100", "99918925", "99918927", "99918928", ], } ACCESS_RULES_SONGS = { "QMDA72252608": ["1100", "99918924"], "GBZUZ0900064": ["1100", "99918925", "99918927", "99918928"], } # ── Group 1: Demographics for accounts ───────────────────────── # # 6 headers x 2 accounts x 2 country_variants x 2 date_variants = 48 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize("account", [RIMAS, BAD_BUNNY]) @pytest.mark.parametrize( "countries", [ pytest.param("", id="all_countries"), pytest.param("&country_code=US&country_code=MX", id="US+MX"), ], ) @pytest.mark.parametrize( "dates", [ pytest.param("", id="all_time"), pytest.param( "&start_date=2023-01-01&end_date=2023-01-28", id="custom_dates" ), ], ) def test_for_accounts(self, hdrs, account, countries, dates): """Demographics by account returns age and gender breakdowns. Authorized users see populated demographics; unauthorized see empty age/gender dicts. """ url = ( f"{config.DEMOGRAPHICS_2_URL}?account_type={account['account_type']}" f"&account_id={account['account_id']}{countries}{dates}" ) payload = assert_endpoint(url, headers=hdrs, items_key=None) if has_access(hdrs, account["account_id"]): assert payload["demographics"]["age"] assert payload["demographics"]["gender"]["F"] > 0 else: assert payload["demographics"] == {"age": {}, "gender": {}} # ── Group 2: Demographics for artists ────────────────────────── # # 6 headers × 2 gp_ids × 2 country_variants × 2 date_variants = 48 tests. # Bad Bunny (RIMAS) exercises D3 access; Local Natives (Frenchkiss) # exercises label and artist profile access. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "gp_id", [ pytest.param("e49ea9f9-0a74-4623-b3c6-ff819c7c0a4d", id="bad_bunny"), pytest.param("95ee273c-09cb-411c-ae00-8806e9ca938e", id="local_natives"), ], ) @pytest.mark.parametrize( "countries", [ pytest.param("", id="all_countries"), pytest.param("&country_code=US&country_code=MX", id="US+MX"), ], ) @pytest.mark.parametrize( "dates", [ pytest.param("", id="all_time"), pytest.param( "&start_date=2023-01-01&end_date=2023-01-28", id="custom_dates" ), ], ) def test_for_artists(self, hdrs, gp_id, countries, dates): """Demographics by global_participant_id returns age and gender. Access is determined by participant-level rules (gp_id -> profile_ids), not account-level rules. Bad Bunny (RIMAS) and Local Natives (Frenchkiss) exercise different label access paths. """ url = ( f"{config.DEMOGRAPHICS_2_URL}?global_participant_id={gp_id}" f"{countries}{dates}" ) payload = assert_endpoint(url, headers=hdrs, items_key=None) if profile_id(hdrs) in self.ACCESS_RULES_ARTISTS.get(gp_id, []): assert payload["demographics"]["age"] assert payload["demographics"]["gender"]["F"] > 0 else: assert payload["demographics"] == {"age": {}, "gender": {}} # ── Group 3: Demographics for songs ──────────────────────────── # # 6 headers × 2 isrcs × 2 country_variants × 2 date_variants = 48 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "isrc", [ pytest.param("QMDA72252608", id="bad_bunny"), pytest.param("GBZUZ0900064", id="local_natives"), ], ) @pytest.mark.parametrize( "countries", [ pytest.param("", id="all_countries"), pytest.param("&country_code=US&country_code=MX", id="US+MX"), ], ) @pytest.mark.parametrize( "dates", [ pytest.param("", id="all_time"), pytest.param( "&start_date=2023-01-01&end_date=2023-01-28", id="custom_dates" ), ], ) def test_for_songs(self, hdrs, isrc, countries, dates): """Demographics by ISRC returns age and gender. Access is determined by song-level rules (ISRC -> profile_ids). QMDA72252608 (Bad Bunny, RIMAS) and GBZUZ0900064 (Local Natives, Frenchkiss) exercise different label access paths. """ url = f"{config.DEMOGRAPHICS_2_URL}?isrc={isrc}{countries}{dates}" payload = assert_endpoint(url, headers=hdrs, items_key=None) if profile_id(hdrs) in self.ACCESS_RULES_SONGS.get(isrc, []): assert payload["demographics"]["age"] assert payload["demographics"]["gender"]["F"] > 0 else: assert payload["demographics"] == {"age": {}, "gender": {}} # ── Group 4: Demographics for songs all-time ─────────────────── # # 6 headers × 2 isrcs × 2 store_ids = 24 tests. @pytest.mark.parametrize("hdrs", ALL_HEADERS) @pytest.mark.parametrize( "isrc", [ pytest.param("QMDA72252608", id="bad_bunny"), pytest.param("GBZUZ0900064", id="local_natives"), ], ) @pytest.mark.parametrize( "store_ids", [ pytest.param("286", id="spotify"), pytest.param("1", id="itunes"), ], ) def test_for_songs_all_time(self, hdrs, isrc, store_ids): """Demographics by ISRC with store_ids returns store-specific age buckets. store_ids=286 (Spotify) -> _60_ age bucket has data. store_ids=1 (iTunes) -> _65_ age bucket has data (Apple uses different age group boundaries). """ url = f"{config.DEMOGRAPHICS_2_URL}?isrc={isrc}&store_ids={store_ids}" payload = assert_endpoint(url, headers=hdrs, items_key=None) if profile_id(hdrs) in self.ACCESS_RULES_SONGS.get(isrc, []): assert payload["demographics"]["age"] assert payload["demographics"]["gender"]["F"] > 0 if store_ids == "1": assert payload["demographics"]["age"]["_65_"] > 0 else: assert payload["demographics"]["age"]["_60_"] > 0 else: assert payload["demographics"] == {"age": {}, "gender": {}}