import csv 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 Album, Artist, Track from scrobbles.models import Scrobble def _write_fix_csv(path, track_ids): with open(path, "w", newline="") as f: writer = csv.writer(f) writer.writerow( [ "track_id", "track_artist_name", "track_album_name", "raw_artist", "raw_album", "source", "mismatch", ] ) for track_id in track_ids: writer.writerow([track_id, "", "", "", "", "", ""]) def _scrobble(user, track, raw_data): return Scrobble.objects.create( track=track, media_type="Track", user=user, log={"raw_data": raw_data}, ) @pytest.mark.django_db def test_report_writes_mismatch_csv(tmp_path): user = get_user_model().objects.create(email="report@example.com") wrong = Artist.objects.create(name="Wrong Artist") track = Track.objects.create(title="Song", artist_fk=wrong) track.artists.add(wrong) _scrobble(user, track, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"}) out = tmp_path / "report.csv" call_command("report_mismatched_metadata", file_path=str(out)) with open(out) as f: rows = list(csv.DictReader(f)) assert len(rows) == 1 assert rows[0]["track_id"] == str(track.id) assert "artist" in rows[0]["mismatch"] assert rows[0]["raw_artist"] == "Right Artist" @pytest.mark.django_db def test_fix_file_commits_resolved_artists(tmp_path): user = get_user_model().objects.create(email="fix@example.com") wrong = Artist.objects.create(name="Wrong Artist") right = Artist.objects.create(name="Right Artist") track = Track.objects.create(title="Song", artist_fk=wrong) track.artists.add(wrong) _scrobble(user, track, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"}) fix = tmp_path / "fix.csv" _write_fix_csv(fix, [track.id]) call_command("report_mismatched_metadata", fix_file=str(fix), commit=True) track.refresh_from_db() assert list(track.artists.all().values_list("name", flat=True)) == ["Right Artist"] assert track.artist_fk.name == "Right Artist" assert "metadata-fixed" in track.tags.names() assert not Artist.objects.filter(name="Wrong Artist").exists() @pytest.mark.django_db def test_fix_file_dry_run_makes_no_changes(tmp_path): user = get_user_model().objects.create(email="dry-fix@example.com") wrong = Artist.objects.create(name="Wrong Artist") right = Artist.objects.create(name="Right Artist") track = Track.objects.create(title="Song", artist_fk=wrong) track.artists.add(wrong) _scrobble(user, track, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"}) fix = tmp_path / "fix.csv" _write_fix_csv(fix, [track.id]) call_command("report_mismatched_metadata", fix_file=str(fix)) track.refresh_from_db() assert sorted(track.artists.all().values_list("name", flat=True)) == [ "Wrong Artist" ] assert not track.tags.filter(name="metadata-fixed").exists() @pytest.mark.django_db def test_fix_file_only_touches_listed_tracks(tmp_path): user = get_user_model().objects.create(email="subset@example.com") wrong = Artist.objects.create(name="Wrong Artist") keep = Artist.objects.create(name="Keep Artist") track_a = Track.objects.create(title="Song A", artist_fk=wrong) track_a.artists.add(wrong) track_b = Track.objects.create(title="Song B", artist_fk=wrong) track_b.artists.add(wrong) _scrobble( user, track_a, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"} ) _scrobble( user, track_b, {"artist": "Right Artist", "musicbrainz_artist_id": "mb-1"} ) fix = tmp_path / "fix.csv" _write_fix_csv(fix, [track_a.id]) call_command("report_mismatched_metadata", fix_file=str(fix), commit=True) track_a.refresh_from_db() track_b.refresh_from_db() assert list(track_a.artists.all().values_list("name", flat=True)) == [ "Right Artist" ] assert sorted(track_b.artists.all().values_list("name", flat=True)) == [ "Wrong Artist" ] @pytest.mark.django_db @patch("music.models.Album.find_or_create") def test_fix_file_fixes_album(mock_album, tmp_path): user = get_user_model().objects.create(email="album@example.com") artist = Artist.objects.create(name="Right Artist") old_album = Album.objects.create(name="Old Album", album_artist=artist) new_album = Album.objects.create(name="New Album", album_artist=artist) mock_album.return_value = new_album track = Track.objects.create(title="Song", artist_fk=artist, album=old_album) track.artists.add(artist) _scrobble(user, track, {"Artist": "Right Artist", "Album": "New Album"}) fix = tmp_path / "fix.csv" _write_fix_csv(fix, [track.id]) call_command("report_mismatched_metadata", fix_file=str(fix), commit=True) track.refresh_from_db() assert track.album == new_album mock_album.assert_called_once_with("New Album", "Right Artist")