334 lines
11 KiB
Python
334 lines
11 KiB
Python
"""Test per core.spotify_client.search_track()."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
import responses
|
|
|
|
from core import spotify_client
|
|
|
|
|
|
class TestSearchTrack:
|
|
@responses.activate
|
|
def test_returns_track_dict_when_found(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={
|
|
"tracks": {
|
|
"items": [
|
|
{
|
|
"id": "abc123",
|
|
"name": "Hot Sauce",
|
|
"artists": [{"name": "Kapuchon"}],
|
|
"external_urls": {"spotify": "https://open.spotify.com/track/abc123"},
|
|
}
|
|
]
|
|
}
|
|
},
|
|
status=200,
|
|
)
|
|
result = spotify_client.search_track("fake-token", "Kapuchon Hot Sauce")
|
|
assert result is not None
|
|
assert result["id"] == "abc123"
|
|
assert result["url"] == "https://open.spotify.com/track/abc123"
|
|
|
|
@responses.activate
|
|
def test_returns_none_when_no_results(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"tracks": {"items": []}},
|
|
status=200,
|
|
)
|
|
result = spotify_client.search_track("fake-token", "no-match")
|
|
assert result is None
|
|
|
|
@responses.activate
|
|
def test_sends_bearer_token(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"tracks": {"items": []}},
|
|
status=200,
|
|
)
|
|
spotify_client.search_track("my-token", "q")
|
|
assert responses.calls[0].request.headers["Authorization"] == "Bearer my-token"
|
|
|
|
@responses.activate
|
|
def test_query_params_correct(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"tracks": {"items": []}},
|
|
status=200,
|
|
)
|
|
spotify_client.search_track("t", "Kapuchon Hot Sauce")
|
|
params = responses.calls[0].request.params
|
|
assert params["q"] == "Kapuchon Hot Sauce"
|
|
assert params["type"] == "track"
|
|
assert params["limit"] == "1"
|
|
|
|
|
|
class TestSearchTracks:
|
|
@responses.activate
|
|
def test_returns_list_of_tracks(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={
|
|
"tracks": {
|
|
"items": [
|
|
{
|
|
"id": f"id{i}",
|
|
"name": f"Track {i}",
|
|
"artists": [{"name": "Solomun"}],
|
|
"album": {"name": "Album X"},
|
|
"duration_ms": 300000 + i * 1000,
|
|
"external_urls": {"spotify": f"https://open.spotify.com/track/id{i}"},
|
|
}
|
|
for i in range(50)
|
|
]
|
|
}
|
|
},
|
|
status=200,
|
|
)
|
|
result = spotify_client.search_tracks("t", "solomun", limit=50)
|
|
assert isinstance(result, list)
|
|
assert len(result) == 50
|
|
first = result[0]
|
|
assert first["id"] == "id0"
|
|
assert first["name"] == "Track 0"
|
|
assert first["artists"] == "Solomun"
|
|
assert first["album"] == "Album X"
|
|
assert first["duration_sec"] == 300
|
|
assert first["url"] == "https://open.spotify.com/track/id0"
|
|
|
|
@responses.activate
|
|
def test_multiple_artists_joined_with_comma(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"tracks": {"items": [{
|
|
"id": "x", "name": "n",
|
|
"artists": [{"name": "A"}, {"name": "B"}, {"name": "C"}],
|
|
"album": {"name": "Alb"},
|
|
"duration_ms": 60000,
|
|
"external_urls": {"spotify": "u"},
|
|
}]}},
|
|
status=200,
|
|
)
|
|
result = spotify_client.search_tracks("t", "q", limit=1)
|
|
assert result[0]["artists"] == "A, B, C"
|
|
|
|
@responses.activate
|
|
def test_empty_query_returns_empty_list(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"tracks": {"items": []}},
|
|
status=200,
|
|
)
|
|
result = spotify_client.search_tracks("t", "no-match", limit=50)
|
|
assert result == []
|
|
|
|
@responses.activate
|
|
def test_query_params_include_limit(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"tracks": {"items": []}},
|
|
status=200,
|
|
)
|
|
spotify_client.search_tracks("t", "q", limit=25)
|
|
params = responses.calls[0].request.params
|
|
assert params["q"] == "q"
|
|
assert params["type"] == "track"
|
|
assert params["limit"] == "25"
|
|
|
|
|
|
class TestSearchArtistDiscography:
|
|
@responses.activate
|
|
def test_exact_name_match_beats_popular(self):
|
|
# 3 candidati: uno esatto (case-insensitive), altri popolari
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"artists": {"items": [
|
|
{"id": "pop", "name": "Solomun Tribute", "popularity": 90},
|
|
{"id": "exact", "name": "SOLOMUN", "popularity": 60},
|
|
{"id": "unrelated", "name": "Other", "popularity": 70},
|
|
]}},
|
|
status=200,
|
|
)
|
|
# Mock top-tracks vuoto per non allungare il test
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/exact/top-tracks",
|
|
json={"tracks": []},
|
|
status=200,
|
|
)
|
|
# Mock albums vuoto
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/exact/albums",
|
|
json={"items": []},
|
|
status=200,
|
|
)
|
|
result = spotify_client.search_artist_discography("t", "Solomun")
|
|
assert result == []
|
|
# Verifica che sia stato chiamato l'artista "exact", non "pop"
|
|
top_tracks_calls = [c for c in responses.calls if "/top-tracks" in c.request.url]
|
|
assert len(top_tracks_calls) == 1
|
|
assert "/exact/top-tracks" in top_tracks_calls[0].request.url
|
|
|
|
@responses.activate
|
|
def test_falls_back_to_most_popular_if_no_exact_match(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"artists": {"items": [
|
|
{"id": "a1", "name": "Solomun Fanpage", "popularity": 30},
|
|
{"id": "a2", "name": "Solomun Live", "popularity": 80},
|
|
]}},
|
|
status=200,
|
|
)
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/a2/top-tracks",
|
|
json={"tracks": []},
|
|
status=200,
|
|
)
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/a2/albums",
|
|
json={"items": []},
|
|
status=200,
|
|
)
|
|
spotify_client.search_artist_discography("t", "solomun")
|
|
top_tracks_calls = [c for c in responses.calls if "/top-tracks" in c.request.url]
|
|
assert "/a2/top-tracks" in top_tracks_calls[0].request.url
|
|
|
|
@responses.activate
|
|
def test_raises_when_no_artist_found(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"artists": {"items": []}},
|
|
status=200,
|
|
)
|
|
with pytest.raises(ValueError, match="Artista"):
|
|
spotify_client.search_artist_discography("t", "asdgjhkasdgj")
|
|
|
|
@responses.activate
|
|
def test_combines_top_tracks_and_album_tracks(self):
|
|
# 1 artista esatto
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"artists": {"items": [{"id": "artX", "name": "artX", "popularity": 50}]}},
|
|
status=200,
|
|
)
|
|
# 3 top-tracks
|
|
top_tracks_data = {"tracks": [
|
|
{
|
|
"id": f"t{i}", "name": f"Top{i}",
|
|
"artists": [{"name": "artX"}],
|
|
"album": {"name": "AlbTop"},
|
|
"duration_ms": 200000,
|
|
"external_urls": {"spotify": f"u{i}"},
|
|
} for i in range(3)
|
|
]}
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/artX/top-tracks",
|
|
json=top_tracks_data,
|
|
status=200,
|
|
)
|
|
# 2 album
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/artX/albums",
|
|
json={"items": [
|
|
{"id": "alb1", "name": "Album 1"},
|
|
{"id": "alb2", "name": "Album 2"},
|
|
]},
|
|
status=200,
|
|
)
|
|
# album 1: 2 tracce
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/albums/alb1/tracks",
|
|
json={"items": [
|
|
{
|
|
"id": f"a1t{i}", "name": f"Alb1Track{i}",
|
|
"artists": [{"name": "artX"}],
|
|
"duration_ms": 180000,
|
|
"external_urls": {"spotify": f"a1u{i}"},
|
|
} for i in range(2)
|
|
]},
|
|
status=200,
|
|
)
|
|
# album 2: 1 traccia
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/albums/alb2/tracks",
|
|
json={"items": [
|
|
{
|
|
"id": "a2t0", "name": "Alb2Track0",
|
|
"artists": [{"name": "artX"}],
|
|
"duration_ms": 240000,
|
|
"external_urls": {"spotify": "a2u0"},
|
|
}
|
|
]},
|
|
status=200,
|
|
)
|
|
with patch("core.spotify_client.time.sleep"): # no wait
|
|
result = spotify_client.search_artist_discography("t", "artX")
|
|
# 3 top + 2 alb1 + 1 alb2 = 6
|
|
assert len(result) == 6
|
|
titles = {r["name"] for r in result}
|
|
assert "Top0" in titles
|
|
assert "Alb1Track0" in titles
|
|
assert "Alb2Track0" in titles
|
|
|
|
@responses.activate
|
|
def test_dedupe_across_top_and_album(self):
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/search",
|
|
json={"artists": {"items": [{"id": "aX", "name": "aX", "popularity": 50}]}},
|
|
status=200,
|
|
)
|
|
# Stesso track name+artist in top-tracks e in album (id diverso)
|
|
common = {
|
|
"name": "Same Song",
|
|
"artists": [{"name": "aX"}],
|
|
"album": {"name": "OG Album"},
|
|
"duration_ms": 200000,
|
|
"external_urls": {"spotify": "u"},
|
|
}
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/aX/top-tracks",
|
|
json={"tracks": [{**common, "id": "top-id"}]},
|
|
status=200,
|
|
)
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/artists/aX/albums",
|
|
json={"items": [{"id": "alb", "name": "OG"}]},
|
|
status=200,
|
|
)
|
|
responses.add(
|
|
responses.GET,
|
|
"https://api.spotify.com/v1/albums/alb/tracks",
|
|
json={"items": [{**common, "id": "alb-id"}]},
|
|
status=200,
|
|
)
|
|
with patch("core.spotify_client.time.sleep"):
|
|
result = spotify_client.search_artist_discography("t", "aX")
|
|
assert len(result) == 1
|