"""Tests for Playlist Available Dates endpoint.""" from contextlib import contextmanager from unittest.mock import patch import pytest from flask import g from owsrequest.utils import mock_request from playlist import handlers from playlist.api import app from playlist.services.ows_permissions import ( OWS_PERMISSIONS_PROFILE_URL, OWS_PERMISSIONS_SERVICE_NAME, ) from tests.integration.consts.permissions import ALL_PERMISSIONS_EMPLOYEE_HEADERS from tests.unit.services.test_ows_permissions import OWS_PERMISSIONS_ALL_ACCESS_RESPONSE @contextmanager def mock_handler( snowflake_db_response={}, permissions_response=OWS_PERMISSIONS_ALL_ACCESS_RESPONSE ): """Mock Snowflake query and permissions service.""" with ( patch( "playlist.connectors.snowflake.SnowflakeQuery.execute", return_value=snowflake_db_response, ) as snowflake, patch( "playlist.queries.fetch_queries.get_max_available_streaming_date", return_value="2021-06-01", ), ): mock_request.get( OWS_PERMISSIONS_SERVICE_NAME, OWS_PERMISSIONS_PROFILE_URL.format( profile_id=ALL_PERMISSIONS_EMPLOYEE_HEADERS["Orchard-Profile-Id"], profile_type=ALL_PERMISSIONS_EMPLOYEE_HEADERS["Orchard-Profile-Type"], ), status=200, response=permissions_response, ) yield snowflake @pytest.fixture def client(): """Return test client.""" test_client = app.test_client() class Ows: def __init__(self): self.correlation_id = "1" class RequestContext: def __init__(self): self.authorization = True self.context_type = "abcdef" with test_client.application.app_context(): g.ows = Ows() g.request_context = RequestContext() yield test_client class TestPlaylistAvailableDates: """Test suite for GET /playlist//dates/ endpoint.""" def test_get_playlist_available_dates_success(self, client): """Test successfully getting available dates for a playlist's tracklist history.""" playlist_dates_response = [ ("2025-11-09",), ("2025-11-08",), ("2025-11-07",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 assert response.json["data"]["dates"] == [ "2025-11-09", "2025-11-08", "2025-11-07", ] def test_get_playlist_available_dates_spotify(self, client): """Test getting dates for Spotify playlist (store_id=286).""" playlist_dates_response = [ ("2025-11-05",), ("2025-11-04",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 assert len(response.json["data"]["dates"]) == 2 assert response.json["data"]["dates"][0] == "2025-11-05" assert response.json["data"]["dates"][1] == "2025-11-04" def test_get_playlist_available_dates_apple_music(self, client): """Test getting dates for Apple Music playlist with storefront.""" playlist_dates_response = [ ("2025-11-09",), ("2025-11-06",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/pl.u-zPyL0qDJq0xv/dates/?store_id=1&storefront=us", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 assert response.json["data"]["dates"] == ["2025-11-09", "2025-11-06"] def test_get_playlist_available_dates_empty(self, client): """Test getting dates for a playlist with no tracklist history.""" playlist_dates_response = [] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 assert response.json["data"]["dates"] == [] def test_get_playlist_available_dates_missing_store_id(self, client): """Test that store_id is required parameter.""" with mock_handler(): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) # Missing required param should return error status assert response.status_code != 200 def test_get_playlist_available_dates_descending_order(self, client): """Test that dates are returned in descending order (newest first).""" playlist_dates_response = [ ("2025-11-09",), ("2025-11-08",), ("2025-11-07",), ("2025-11-06",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 dates = response.json["data"]["dates"] assert dates == ["2025-11-09", "2025-11-08", "2025-11-07", "2025-11-06"] def test_get_playlist_available_dates_format(self, client): """Test that dates are returned in YYYY-MM-DD format.""" playlist_dates_response = [ ("2025-01-15",), ("2025-01-01",), ("2024-12-25",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 dates = response.json["data"]["dates"] # Verify format matches YYYY-MM-DD assert all(len(d) == 10 for d in dates) assert all(d[4] == "-" and d[7] == "-" for d in dates) def test_get_playlist_available_dates_many_dates(self, client): """Test getting a large number of historical dates.""" # Generate 90 days of dates playlist_dates_response = [ (f"2025-{(11 - i // 30):02d}-{(9 - i % 30):02d}",) for i in range(90) ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 dates = response.json["data"]["dates"] assert len(dates) == 90 def test_get_playlist_available_dates_different_stores(self, client): """Test that same playlist can have different dates per store.""" # Same playlist, different stores playlist_dates_response = [ ("2025-11-09",), ("2025-11-08",), ] with mock_handler(playlist_dates_response): response_spotify = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) with mock_handler(playlist_dates_response): response_apple = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=1", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response_spotify.status_code == 200 assert response_apple.status_code == 200 def test_get_playlist_available_dates_response_structure(self, client): """Test response has correct structure with 'data' and 'dates' keys.""" playlist_dates_response = [ ("2025-11-09",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 assert "data" in response.json assert "dates" in response.json["data"] assert isinstance(response.json["data"]["dates"], list) assert all(isinstance(d, str) for d in response.json["data"]["dates"]) def test_get_playlist_available_dates_storefront_none_for_spotify(self, client): """Test Spotify requests work without storefront parameter (Spotify never has storefront).""" playlist_dates_response = [ ("2025-11-09",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 def test_get_playlist_available_dates_spotify_ignore_storefront(self, client): """Test Spotify ignores storefront parameter if provided (Spotify never has storefront).""" playlist_dates_response = [ ("2025-11-09",), ] with mock_handler(playlist_dates_response): # Storefront param should be ignored for Spotify response = client.get( "/playlist/37i9dQZF1DX4W3aJJYCDfV/dates/?store_id=286&storefront=us", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) # Should still work (storefront is ignored for Spotify) assert response.status_code == 200 def test_get_playlist_available_dates_storefront_us(self, client): """Test Apple Music US storefront.""" playlist_dates_response = [ ("2025-11-09",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/pl.u-zPyL0qDJq0xv/dates/?store_id=1&storefront=us", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200 def test_get_playlist_available_dates_storefront_gb(self, client): """Test Apple Music GB storefront.""" playlist_dates_response = [ ("2025-11-09",), ] with mock_handler(playlist_dates_response): response = client.get( "/playlist/pl.u-zPyL0qDJq0xv/dates/?store_id=1&storefront=gb", headers=ALL_PERMISSIONS_EMPLOYEE_HEADERS, ) assert response.status_code == 200