"""Tests for new_music_friday_search logic.""" from unittest.mock import patch, ANY from charts.connectors import snowflake, snowflake_cortex_search from charts.logic import new_music_friday_search def test_search_no_cortex_results() -> None: with patch.object(snowflake_cortex_search, 'query', return_value=[]) as mock_cortex: result = new_music_friday_search.new_music_friday_search('2025-05-16', 'unknown track') mock_cortex.assert_called_once() assert result.model_dump() == {'total': 0, 'entries': []} def test_search_text_term_sends_query_payload() -> None: with patch.object(snowflake_cortex_search, 'query', return_value=[]) as mock_cortex: new_music_friday_search.new_music_friday_search('2025-05-16', 'Taylor Swift') payload = mock_cortex.call_args[0][0] assert payload['query'] == 'Taylor Swift' assert payload['filter'] == {'@eq': {'CHART_DATE': '2025-05-16'}} assert 'ISRC' in payload['columns'] assert 'TRACK_NAME' in payload['columns'] assert 'ARTIST_NAME' in payload['columns'] def test_search_isrc_term_sends_filter_payload() -> None: with patch.object(snowflake_cortex_search, 'query', return_value=[]) as mock_cortex: new_music_friday_search.new_music_friday_search('2025-05-16', 'USUM72400001') payload = mock_cortex.call_args[0][0] assert payload['query'] == '' assert payload['filter'] == { '@and': [ {'@eq': {'ISRC': 'USUM72400001'}}, {'@eq': {'CHART_DATE': '2025-05-16'}}, ] } def test_search_isrc_term_normalised_to_uppercase() -> None: with patch.object(snowflake_cortex_search, 'query', return_value=[]) as mock_cortex: new_music_friday_search.new_music_friday_search('2025-05-16', 'usum72400001') payload = mock_cortex.call_args[0][0] assert payload['filter']['@and'][0]['@eq']['ISRC'] == 'USUM72400001' def test_search_term_stripped_of_whitespace() -> None: with patch.object(snowflake_cortex_search, 'query', return_value=[]) as mock_cortex: new_music_friday_search.new_music_friday_search('2025-05-16', ' Taylor Swift ') payload = mock_cortex.call_args[0][0] assert payload['query'] == 'Taylor Swift' def test_search_with_results() -> None: cortex_rows = [ {'ISRC': 'USUM72400001', 'TRACK_NAME': 'Test Track', 'ARTIST_NAME': 'Test Artist', 'CHART_DATE': '2025-05-16'}, {'ISRC': 'GBUM72400002', 'TRACK_NAME': None, 'ARTIST_NAME': None, 'CHART_DATE': '2025-05-16'}, ] fetchall_mock_response = [ ( 2, 'USUM72400001', 'Test Track', 'Test Artist', 'https://example.com/image.jpg', 3, 5.0, 2, '[{"market":"US","position":1,"spotify_id":"spotify1"},{"market":"GB","position":null,"spotify_id":"spotify2"}]', ), ( 2, 'GBUM72400002', None, None, None, 1, 10.0, 0, '[{"market":"GB","position":10,"spotify_id":"spotify3"}]', ), ] with patch.object(snowflake_cortex_search, 'query', return_value=cortex_rows), \ patch.object(snowflake, 'fetchall_nocache', return_value=fetchall_mock_response) as mock_fetchall: result = new_music_friday_search.new_music_friday_search('2025-05-16', 'Test Track') sql_arg = mock_fetchall.call_args[0][0] assert "'USUM72400001'" in sql_arg assert "'GBUM72400002'" in sql_arg assert mock_fetchall.call_args[0][1] == {'date': '2025-05-16'} assert result.total == 2 assert result.entries[0].isrc == 'USUM72400001' assert result.entries[0].track_name == 'Test Track' assert result.entries[0].artist_name == 'Test Artist' assert result.entries[0].image_url == 'https://example.com/image.jpg' assert result.entries[0].feature == 3 assert result.entries[0].average_position == 5.0 assert result.entries[0].top_10_rank == 2 assert len(result.entries[0].placements) == 2 assert result.entries[0].placements[0].market == 'US' assert result.entries[0].placements[0].position == 1 assert result.entries[1].isrc == 'GBUM72400002' assert result.entries[1].track_name is None def test_search_results_ordered_by_cortex_relevance() -> None: cortex_rows = [ {'ISRC': 'GBUM72400002', 'TRACK_NAME': 'Second Track', 'ARTIST_NAME': 'Artist B', 'CHART_DATE': '2025-05-16'}, {'ISRC': 'USUM72400001', 'TRACK_NAME': 'First Track', 'ARTIST_NAME': 'Artist A', 'CHART_DATE': '2025-05-16'}, ] # DB returns rows in the reverse of Cortex relevance order fetchall_mock_response = [ (2, 'USUM72400001', 'First Track', 'Artist A', None, 5, 3.0, 1, '[]'), (2, 'GBUM72400002', 'Second Track', 'Artist B', None, 1, 8.0, 0, '[]'), ] with patch.object(snowflake_cortex_search, 'query', return_value=cortex_rows), \ patch.object(snowflake, 'fetchall_nocache', return_value=fetchall_mock_response): result = new_music_friday_search.new_music_friday_search('2025-05-16', 'track') assert result.entries[0].isrc == 'GBUM72400002' assert result.entries[1].isrc == 'USUM72400001' def test_search_handles_null_average_position() -> None: """Cortex matches that have no charting positions on the date (NULL AVG) must serialize cleanly rather than failing Pydantic validation. This covers the case where ingestion of Track/Artist metadata lags the chart data, or where an ISRC only charts in markets where data_received=FALSE on the requested date. """ cortex_rows = [ {'ISRC': 'USUM72400001', 'TRACK_NAME': 'Test Track', 'ARTIST_NAME': 'Test Artist', 'CHART_DATE': '2025-05-16'}, ] fetchall_mock_response = [ ( 1, 'USUM72400001', 'Test Track', 'Test Artist', None, 0, None, 0, '[]', ), ] with patch.object(snowflake_cortex_search, 'query', return_value=cortex_rows), \ patch.object(snowflake, 'fetchall_nocache', return_value=fetchall_mock_response): result = new_music_friday_search.new_music_friday_search('2025-05-16', 'Test Track') assert result.total == 1 assert result.entries[0].isrc == 'USUM72400001' assert result.entries[0].feature == 0 assert result.entries[0].average_position is None assert result.entries[0].top_10_rank == 0 assert result.entries[0].placements == []