[music] Resolve artist identity before splitting (fd86a11a)
This commit is contained in:
0
tests/music_tests/__init__.py
Normal file
0
tests/music_tests/__init__.py
Normal file
173
tests/music_tests/test_artist_resolution.py
Normal file
173
tests/music_tests/test_artist_resolution.py
Normal file
@ -0,0 +1,173 @@
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.core.management import call_command
|
||||
from music.models import Artist, Track
|
||||
from music.utils import resolve_artist_names
|
||||
from scrobbles.models import Scrobble
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_resolve_with_mbid_keeps_literal_name():
|
||||
names = resolve_artist_names("Simon & Garfunkel", artist_mbid="grp-1")
|
||||
assert names == ["Simon & Garfunkel"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.utils.lookup_artist_from_mb", return_value={"name": "Simon & Garfunkel"})
|
||||
def test_resolve_mb_exact_match_keeps_literal_name(mock_lookup):
|
||||
names = resolve_artist_names("Simon & Garfunkel")
|
||||
assert names == ["Simon & Garfunkel"]
|
||||
mock_lookup.assert_called_once_with("Simon & Garfunkel")
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch(
|
||||
"music.utils.lookup_artist_from_mb", return_value={"name": "Simon and Garfunkel"}
|
||||
)
|
||||
def test_resolve_mb_inexact_match_falls_back_to_split(mock_lookup):
|
||||
names = resolve_artist_names("Simon & Garfunkel")
|
||||
assert names == ["Simon", "Garfunkel"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.utils.lookup_artist_from_mb", return_value={})
|
||||
def test_resolve_splits_collab_when_mb_has_no_single_artist(mock_lookup):
|
||||
names = resolve_artist_names("Matt Sweeney & Bonnie Prince Billy")
|
||||
assert names == ["Matt Sweeney", "Bonnie Prince Billy"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.utils.lookup_artist_from_mb")
|
||||
def test_resolve_existing_db_artist_skips_mb_lookup(mock_lookup):
|
||||
Artist.objects.create(name="Simon & Garfunkel")
|
||||
names = resolve_artist_names("Simon & Garfunkel")
|
||||
assert names == ["Simon & Garfunkel"]
|
||||
mock_lookup.assert_not_called()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.utils.lookup_artist_from_mb", return_value={})
|
||||
def test_resolve_strips_featured_names(mock_lookup):
|
||||
names = resolve_artist_names("Ariana Grande feat. Zedd")
|
||||
assert names == ["Ariana Grande"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_find_or_create_trust_webhook_keeps_literal_artist():
|
||||
track = Track.find_or_create(
|
||||
title="Bridge over Troubled Water",
|
||||
artist_name="Simon & Garfunkel",
|
||||
album_name="Bridge over Troubled Water",
|
||||
artist_mbid="grp-1",
|
||||
trust_webhook_data=True,
|
||||
)
|
||||
|
||||
assert list(track.artists.all().values_list("name", flat=True)) == [
|
||||
"Simon & Garfunkel"
|
||||
]
|
||||
assert not Artist.objects.filter(name="Simon").exists()
|
||||
assert not Artist.objects.filter(name="Garfunkel").exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.models.resolve_track", return_value=(None, ""))
|
||||
def test_find_or_create_reuses_literal_artist(mock_resolve):
|
||||
artist = Artist.objects.create(name="Simon & Garfunkel")
|
||||
|
||||
track = Track.find_or_create(
|
||||
title="Bridge over Troubled Water",
|
||||
artist_name="Simon & Garfunkel",
|
||||
)
|
||||
|
||||
assert list(track.artists.all().values_list("name", flat=True)) == [
|
||||
"Simon & Garfunkel"
|
||||
]
|
||||
assert not Artist.objects.filter(name="Simon").exists()
|
||||
assert not Artist.objects.filter(name="Garfunkel").exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.models.get_track_metadata_with_artist", return_value=None)
|
||||
@patch("music.models.lookup_artist_from_tadb", return_value=None)
|
||||
def test_reconcile_command_merges_split_artists(mock_tadb, mock_track_meta):
|
||||
user = get_user_model().objects.create(email="reconcile@example.com")
|
||||
simon = Artist.objects.create(name="Simon")
|
||||
garfunkel = Artist.objects.create(name="Garfunkel")
|
||||
track = Track.objects.create(title="Bridge over Troubled Water", artist_fk=simon)
|
||||
track.artists.add(simon, garfunkel)
|
||||
Scrobble.objects.create(
|
||||
track=track,
|
||||
media_type="Track",
|
||||
user=user,
|
||||
log={
|
||||
"raw_data": {
|
||||
"artist": "Simon & Garfunkel",
|
||||
"musicbrainz_artist_id": "grp-1",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
call_command("reconcile_split_artists", "--commit")
|
||||
|
||||
track.refresh_from_db()
|
||||
assert [a.name for a in track.artists.all()] == ["Simon & Garfunkel"]
|
||||
assert track.artist_fk.name == "Simon & Garfunkel"
|
||||
assert "artist-reconciled" in track.tags.names()
|
||||
assert not Artist.objects.filter(name="Simon").exists()
|
||||
assert not Artist.objects.filter(name="Garfunkel").exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_reconcile_command_dry_run_makes_no_changes():
|
||||
user = get_user_model().objects.create(email="dry@example.com")
|
||||
simon = Artist.objects.create(name="Simon")
|
||||
garfunkel = Artist.objects.create(name="Garfunkel")
|
||||
track = Track.objects.create(title="Bridge over Troubled Water")
|
||||
track.artists.add(simon, garfunkel)
|
||||
Scrobble.objects.create(
|
||||
track=track,
|
||||
media_type="Track",
|
||||
user=user,
|
||||
log={
|
||||
"raw_data": {
|
||||
"artist": "Simon & Garfunkel",
|
||||
"musicbrainz_artist_id": "grp-1",
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
call_command("reconcile_split_artists")
|
||||
|
||||
track.refresh_from_db()
|
||||
assert sorted(track.artists.all().values_list("name", flat=True)) == [
|
||||
"Garfunkel",
|
||||
"Simon",
|
||||
]
|
||||
assert Artist.objects.filter(name="Simon").exists()
|
||||
assert Artist.objects.filter(name="Garfunkel").exists()
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@patch("music.utils.lookup_artist_from_mb", return_value={})
|
||||
def test_reconcile_command_leaves_genuine_collab_alone(mock_lookup):
|
||||
user = get_user_model().objects.create(email="collab@example.com")
|
||||
matt = Artist.objects.create(name="Matt Sweeney")
|
||||
bonnie = Artist.objects.create(name="Bonnie Prince Billy")
|
||||
track = Track.objects.create(title="My Morning Song")
|
||||
track.artists.add(matt, bonnie)
|
||||
Scrobble.objects.create(
|
||||
track=track,
|
||||
media_type="Track",
|
||||
user=user,
|
||||
log={"raw_data": {"artist": "Matt Sweeney & Bonnie Prince Billy"}},
|
||||
)
|
||||
|
||||
call_command("reconcile_split_artists", "--commit")
|
||||
|
||||
track.refresh_from_db()
|
||||
assert sorted(track.artists.all().values_list("name", flat=True)) == [
|
||||
"Bonnie Prince Billy",
|
||||
"Matt Sweeney",
|
||||
]
|
||||
Reference in New Issue
Block a user