174 lines
5.9 KiB
Python
174 lines
5.9 KiB
Python
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",
|
|
]
|