# This file is part of beets. # Copyright 2016, Adrian Sampson. # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the # "Software"), to deal in the Software without restriction, including # without limitation the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the Software, and to # permit persons to whom the Software is furnished to do so, subject to # the following conditions: # # The above copyright notice and this permission notice shall be # included in all copies or substantial portions of the Software. """Tests for MusicBrainz API wrapper. """ import unittest from test import _common from unittest import mock from beets import config from beets.autotag import mb class MBAlbumInfoTest(_common.TestCase): def _make_release( self, date_str="2009", tracks=None, track_length=None, track_artist=False, multi_artist_credit=False, data_tracks=None, medium_format="FORMAT", ): release = { "title": "ALBUM TITLE", "id": "ALBUM ID", "asin": "ALBUM ASIN", "disambiguation": "R_DISAMBIGUATION", "release-group": { "type": "Album", "first-release-date": date_str, "id": "RELEASE GROUP ID", "disambiguation": "RG_DISAMBIGUATION", }, "artist-credit": [ { "artist": { "name": "ARTIST NAME", "id": "ARTIST ID", "sort-name": "ARTIST SORT NAME", }, "name": "ARTIST CREDIT", } ], "date": "3001", "medium-list": [], "label-info-list": [ { "catalog-number": "CATALOG NUMBER", "label": {"name": "LABEL NAME"}, } ], "text-representation": { "script": "SCRIPT", "language": "LANGUAGE", }, "country": "COUNTRY", "status": "STATUS", } if multi_artist_credit: release["artist-credit"].append(" & ") # add join phase release["artist-credit"].append( { "artist": { "name": "ARTIST 2 NAME", "id": "ARTIST 2 ID", "sort-name": "ARTIST 2 SORT NAME", }, "name": "ARTIST MULTI CREDIT", } ) i = 0 track_list = [] if tracks: for recording in tracks: i += 1 track = { "id": "RELEASE TRACK ID %d" % i, "recording": recording, "position": i, "number": "A1", } if track_length: # Track lengths are distinct from recording lengths. track["length"] = track_length if track_artist: # Similarly, track artists can differ from recording # artists. track["artist-credit"] = [ { "artist": { "name": "TRACK ARTIST NAME", "id": "TRACK ARTIST ID", "sort-name": "TRACK ARTIST SORT NAME", }, "name": "TRACK ARTIST CREDIT", } ] if multi_artist_credit: track["artist-credit"].append(" & ") # add join phase track["artist-credit"].append( { "artist": { "name": "TRACK ARTIST 2 NAME", "id": "TRACK ARTIST 2 ID", "sort-name": "TRACK ARTIST 2 SORT NAME", }, "name": "TRACK ARTIST 2 CREDIT", } ) track_list.append(track) data_track_list = [] if data_tracks: for recording in data_tracks: i += 1 data_track = { "id": "RELEASE TRACK ID %d" % i, "recording": recording, "position": i, "number": "A1", } data_track_list.append(data_track) release["medium-list"].append( { "position": "1", "track-list": track_list, "data-track-list": data_track_list, "format": medium_format, "title": "MEDIUM TITLE", } ) return release def _make_track( self, title, tr_id, duration, artist=False, video=False, disambiguation=None, remixer=False, multi_artist_credit=False, ): track = { "title": title, "id": tr_id, } if duration is not None: track["length"] = duration if artist: track["artist-credit"] = [ { "artist": { "name": "RECORDING ARTIST NAME", "id": "RECORDING ARTIST ID", "sort-name": "RECORDING ARTIST SORT NAME", }, "name": "RECORDING ARTIST CREDIT", } ] if multi_artist_credit: track["artist-credit"].append(" & ") # add join phase track["artist-credit"].append( { "artist": { "name": "RECORDING ARTIST 2 NAME", "id": "RECORDING ARTIST 2 ID", "sort-name": "RECORDING ARTIST 2 SORT NAME", }, "name": "RECORDING ARTIST 2 CREDIT", } ) if remixer: track["artist-relation-list"] = [ { "type": "remixer", "type-id": "RELATION TYPE ID", "target": "RECORDING REMIXER ARTIST ID", "direction": "RECORDING RELATION DIRECTION", "artist": { "id": "RECORDING REMIXER ARTIST ID", "type": "RECORDING REMIXER ARTIST TYPE", "name": "RECORDING REMIXER ARTIST NAME", "sort-name": "RECORDING REMIXER ARTIST SORT NAME", }, } ] if video: track["video"] = "true" if disambiguation: track["disambiguation"] = disambiguation return track def test_parse_release_with_year(self): release = self._make_release("1984") d = mb.album_info(release) self.assertEqual(d.album, "ALBUM TITLE") self.assertEqual(d.album_id, "ALBUM ID") self.assertEqual(d.artist, "ARTIST NAME") self.assertEqual(d.artist_id, "ARTIST ID") self.assertEqual(d.original_year, 1984) self.assertEqual(d.year, 3001) self.assertEqual(d.artist_credit, "ARTIST CREDIT") def test_parse_release_type(self): release = self._make_release("1984") d = mb.album_info(release) self.assertEqual(d.albumtype, "album") def test_parse_release_full_date(self): release = self._make_release("1987-03-31") d = mb.album_info(release) self.assertEqual(d.original_year, 1987) self.assertEqual(d.original_month, 3) self.assertEqual(d.original_day, 31) def test_parse_tracks(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) t = d.tracks self.assertEqual(len(t), 2) self.assertEqual(t[0].title, "TITLE ONE") self.assertEqual(t[0].track_id, "ID ONE") self.assertEqual(t[0].length, 100.0) self.assertEqual(t[1].title, "TITLE TWO") self.assertEqual(t[1].track_id, "ID TWO") self.assertEqual(t[1].length, 200.0) def test_parse_track_indices(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) t = d.tracks self.assertEqual(t[0].medium_index, 1) self.assertEqual(t[0].index, 1) self.assertEqual(t[1].medium_index, 2) self.assertEqual(t[1].index, 2) def test_parse_medium_numbers_single_medium(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) self.assertEqual(d.mediums, 1) t = d.tracks self.assertEqual(t[0].medium, 1) self.assertEqual(t[1].medium, 1) def test_parse_medium_numbers_two_mediums(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=[tracks[0]]) second_track_list = [ { "id": "RELEASE TRACK ID 2", "recording": tracks[1], "position": "1", "number": "A1", } ] release["medium-list"].append( { "position": "2", "track-list": second_track_list, } ) d = mb.album_info(release) self.assertEqual(d.mediums, 2) t = d.tracks self.assertEqual(t[0].medium, 1) self.assertEqual(t[0].medium_index, 1) self.assertEqual(t[0].index, 1) self.assertEqual(t[1].medium, 2) self.assertEqual(t[1].medium_index, 1) self.assertEqual(t[1].index, 2) def test_parse_release_year_month_only(self): release = self._make_release("1987-03") d = mb.album_info(release) self.assertEqual(d.original_year, 1987) self.assertEqual(d.original_month, 3) def test_no_durations(self): tracks = [self._make_track("TITLE", "ID", None)] release = self._make_release(tracks=tracks) d = mb.album_info(release) self.assertEqual(d.tracks[0].length, None) def test_track_length_overrides_recording_length(self): tracks = [self._make_track("TITLE", "ID", 1.0 * 1000.0)] release = self._make_release(tracks=tracks, track_length=2.0 * 1000.0) d = mb.album_info(release) self.assertEqual(d.tracks[0].length, 2.0) def test_no_release_date(self): release = self._make_release(None) d = mb.album_info(release) self.assertFalse(d.original_year) self.assertFalse(d.original_month) self.assertFalse(d.original_day) def test_various_artists_defaults_false(self): release = self._make_release(None) d = mb.album_info(release) self.assertFalse(d.va) def test_detect_various_artists(self): release = self._make_release(None) release["artist-credit"][0]["artist"]["id"] = mb.VARIOUS_ARTISTS_ID d = mb.album_info(release) self.assertTrue(d.va) def test_parse_artist_sort_name(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.artist_sort, "ARTIST SORT NAME") def test_parse_releasegroupid(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.releasegroup_id, "RELEASE GROUP ID") def test_parse_asin(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.asin, "ALBUM ASIN") def test_parse_catalognum(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.catalognum, "CATALOG NUMBER") def test_parse_textrepr(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.script, "SCRIPT") self.assertEqual(d.language, "LANGUAGE") def test_parse_country(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.country, "COUNTRY") def test_parse_status(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.albumstatus, "STATUS") def test_parse_media(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(None, tracks=tracks) d = mb.album_info(release) self.assertEqual(d.media, "FORMAT") def test_parse_disambig(self): release = self._make_release(None) d = mb.album_info(release) self.assertEqual(d.albumdisambig, "R_DISAMBIGUATION") self.assertEqual(d.releasegroupdisambig, "RG_DISAMBIGUATION") def test_parse_disctitle(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(None, tracks=tracks) d = mb.album_info(release) t = d.tracks self.assertEqual(t[0].disctitle, "MEDIUM TITLE") self.assertEqual(t[1].disctitle, "MEDIUM TITLE") def test_missing_language(self): release = self._make_release(None) del release["text-representation"]["language"] d = mb.album_info(release) self.assertEqual(d.language, None) def test_parse_recording_artist(self): tracks = [self._make_track("a", "b", 1, True)] release = self._make_release(None, tracks=tracks) track = mb.album_info(release).tracks[0] self.assertEqual(track.artist, "RECORDING ARTIST NAME") self.assertEqual(track.artist_id, "RECORDING ARTIST ID") self.assertEqual(track.artist_sort, "RECORDING ARTIST SORT NAME") self.assertEqual(track.artist_credit, "RECORDING ARTIST CREDIT") def test_parse_recording_artist_multi(self): tracks = [self._make_track("a", "b", 1, True, multi_artist_credit=True)] release = self._make_release(None, tracks=tracks) track = mb.album_info(release).tracks[0] self.assertEqual( track.artist, "RECORDING ARTIST NAME & RECORDING ARTIST 2 NAME" ) self.assertEqual(track.artist_id, "RECORDING ARTIST ID") self.assertEqual( track.artist_sort, "RECORDING ARTIST SORT NAME & RECORDING ARTIST 2 SORT NAME", ) self.assertEqual( track.artist_credit, "RECORDING ARTIST CREDIT & RECORDING ARTIST 2 CREDIT", ) self.assertEqual( track.artists, ["RECORDING ARTIST NAME", "RECORDING ARTIST 2 NAME"] ) self.assertEqual( track.artists_ids, ["RECORDING ARTIST ID", "RECORDING ARTIST 2 ID"] ) self.assertEqual( track.artists_sort, ["RECORDING ARTIST SORT NAME", "RECORDING ARTIST 2 SORT NAME"], ) self.assertEqual( track.artists_credit, ["RECORDING ARTIST CREDIT", "RECORDING ARTIST 2 CREDIT"], ) def test_track_artist_overrides_recording_artist(self): tracks = [self._make_track("a", "b", 1, True)] release = self._make_release(None, tracks=tracks, track_artist=True) track = mb.album_info(release).tracks[0] self.assertEqual(track.artist, "TRACK ARTIST NAME") self.assertEqual(track.artist_id, "TRACK ARTIST ID") self.assertEqual(track.artist_sort, "TRACK ARTIST SORT NAME") self.assertEqual(track.artist_credit, "TRACK ARTIST CREDIT") def test_track_artist_overrides_recording_artist_multi(self): tracks = [self._make_track("a", "b", 1, True, multi_artist_credit=True)] release = self._make_release( None, tracks=tracks, track_artist=True, multi_artist_credit=True ) track = mb.album_info(release).tracks[0] self.assertEqual( track.artist, "TRACK ARTIST NAME & TRACK ARTIST 2 NAME" ) self.assertEqual(track.artist_id, "TRACK ARTIST ID") self.assertEqual( track.artist_sort, "TRACK ARTIST SORT NAME & TRACK ARTIST 2 SORT NAME", ) self.assertEqual( track.artist_credit, "TRACK ARTIST CREDIT & TRACK ARTIST 2 CREDIT" ) self.assertEqual( track.artists, ["TRACK ARTIST NAME", "TRACK ARTIST 2 NAME"] ) self.assertEqual( track.artists_ids, ["TRACK ARTIST ID", "TRACK ARTIST 2 ID"] ) self.assertEqual( track.artists_sort, ["TRACK ARTIST SORT NAME", "TRACK ARTIST 2 SORT NAME"], ) self.assertEqual( track.artists_credit, ["TRACK ARTIST CREDIT", "TRACK ARTIST 2 CREDIT"], ) def test_parse_recording_remixer(self): tracks = [self._make_track("a", "b", 1, remixer=True)] release = self._make_release(None, tracks=tracks) track = mb.album_info(release).tracks[0] self.assertEqual(track.remixer, "RECORDING REMIXER ARTIST NAME") def test_data_source(self): release = self._make_release() d = mb.album_info(release) self.assertEqual(d.data_source, "MusicBrainz") def test_ignored_media(self): config["match"]["ignored_media"] = ["IGNORED1", "IGNORED2"] tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks, medium_format="IGNORED1") d = mb.album_info(release) self.assertEqual(len(d.tracks), 0) def test_no_ignored_media(self): config["match"]["ignored_media"] = ["IGNORED1", "IGNORED2"] tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks, medium_format="NON-IGNORED") d = mb.album_info(release) self.assertEqual(len(d.tracks), 2) def test_skip_data_track(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("[data track]", "ID DATA TRACK", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 2) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE TWO") def test_skip_audio_data_tracks_by_default(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] data_tracks = [ self._make_track( "TITLE AUDIO DATA", "ID DATA TRACK", 100.0 * 1000.0 ) ] release = self._make_release(tracks=tracks, data_tracks=data_tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 2) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE TWO") def test_no_skip_audio_data_tracks_if_configured(self): config["match"]["ignore_data_tracks"] = False tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] data_tracks = [ self._make_track( "TITLE AUDIO DATA", "ID DATA TRACK", 100.0 * 1000.0 ) ] release = self._make_release(tracks=tracks, data_tracks=data_tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 3) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE TWO") self.assertEqual(d.tracks[2].title, "TITLE AUDIO DATA") def test_skip_video_tracks_by_default(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track( "TITLE VIDEO", "ID VIDEO", 100.0 * 1000.0, False, True ), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 2) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE TWO") def test_skip_video_data_tracks_by_default(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] data_tracks = [ self._make_track( "TITLE VIDEO", "ID VIDEO", 100.0 * 1000.0, False, True ) ] release = self._make_release(tracks=tracks, data_tracks=data_tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 2) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE TWO") def test_no_skip_video_tracks_if_configured(self): config["match"]["ignore_data_tracks"] = False config["match"]["ignore_video_tracks"] = False tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track( "TITLE VIDEO", "ID VIDEO", 100.0 * 1000.0, False, True ), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 3) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE VIDEO") self.assertEqual(d.tracks[2].title, "TITLE TWO") def test_no_skip_video_data_tracks_if_configured(self): config["match"]["ignore_data_tracks"] = False config["match"]["ignore_video_tracks"] = False tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track("TITLE TWO", "ID TWO", 200.0 * 1000.0), ] data_tracks = [ self._make_track( "TITLE VIDEO", "ID VIDEO", 100.0 * 1000.0, False, True ) ] release = self._make_release(tracks=tracks, data_tracks=data_tracks) d = mb.album_info(release) self.assertEqual(len(d.tracks), 3) self.assertEqual(d.tracks[0].title, "TITLE ONE") self.assertEqual(d.tracks[1].title, "TITLE TWO") self.assertEqual(d.tracks[2].title, "TITLE VIDEO") def test_track_disambiguation(self): tracks = [ self._make_track("TITLE ONE", "ID ONE", 100.0 * 1000.0), self._make_track( "TITLE TWO", "ID TWO", 200.0 * 1000.0, disambiguation="SECOND TRACK", ), ] release = self._make_release(tracks=tracks) d = mb.album_info(release) t = d.tracks self.assertEqual(len(t), 2) self.assertEqual(t[0].trackdisambig, None) self.assertEqual(t[1].trackdisambig, "SECOND TRACK") class ParseIDTest(_common.TestCase): def test_parse_id_correct(self): id_string = "28e32c71-1450-463e-92bf-e0a46446fc11" out = mb._parse_id(id_string) self.assertEqual(out, id_string) def test_parse_id_non_id_returns_none(self): id_string = "blah blah" out = mb._parse_id(id_string) self.assertEqual(out, None) def test_parse_id_url_finds_id(self): id_string = "28e32c71-1450-463e-92bf-e0a46446fc11" id_url = "https://musicbrainz.org/entity/%s" % id_string out = mb._parse_id(id_url) self.assertEqual(out, id_string) class ArtistFlatteningTest(_common.TestCase): def _credit_dict(self, suffix=""): return { "artist": { "name": "NAME" + suffix, "sort-name": "SORT" + suffix, }, "name": "CREDIT" + suffix, } def _add_alias(self, credit_dict, suffix="", locale="", primary=False): alias = { "alias": "ALIAS" + suffix, "locale": locale, "sort-name": "ALIASSORT" + suffix, } if primary: alias["primary"] = "primary" if "alias-list" not in credit_dict["artist"]: credit_dict["artist"]["alias-list"] = [] credit_dict["artist"]["alias-list"].append(alias) def test_single_artist(self): credit = [self._credit_dict()] a, s, c = mb._flatten_artist_credit(credit) self.assertEqual(a, "NAME") self.assertEqual(s, "SORT") self.assertEqual(c, "CREDIT") a, s, c = mb._multi_artist_credit(credit, include_join_phrase=False) self.assertEqual(a, ["NAME"]) self.assertEqual(s, ["SORT"]) self.assertEqual(c, ["CREDIT"]) def test_two_artists(self): credit = [self._credit_dict("a"), " AND ", self._credit_dict("b")] a, s, c = mb._flatten_artist_credit(credit) self.assertEqual(a, "NAMEa AND NAMEb") self.assertEqual(s, "SORTa AND SORTb") self.assertEqual(c, "CREDITa AND CREDITb") a, s, c = mb._multi_artist_credit(credit, include_join_phrase=False) self.assertEqual(a, ["NAMEa", "NAMEb"]) self.assertEqual(s, ["SORTa", "SORTb"]) self.assertEqual(c, ["CREDITa", "CREDITb"]) def test_alias(self): credit_dict = self._credit_dict() self._add_alias(credit_dict, suffix="en", locale="en", primary=True) self._add_alias( credit_dict, suffix="en_GB", locale="en_GB", primary=True ) self._add_alias(credit_dict, suffix="fr", locale="fr") self._add_alias(credit_dict, suffix="fr_P", locale="fr", primary=True) self._add_alias(credit_dict, suffix="pt_BR", locale="pt_BR") # test no alias config["import"]["languages"] = [""] flat = mb._flatten_artist_credit([credit_dict]) self.assertEqual(flat, ("NAME", "SORT", "CREDIT")) # test en primary config["import"]["languages"] = ["en"] flat = mb._flatten_artist_credit([credit_dict]) self.assertEqual(flat, ("ALIASen", "ALIASSORTen", "CREDIT")) # test en_GB en primary config["import"]["languages"] = ["en_GB", "en"] flat = mb._flatten_artist_credit([credit_dict]) self.assertEqual(flat, ("ALIASen_GB", "ALIASSORTen_GB", "CREDIT")) # test en en_GB primary config["import"]["languages"] = ["en", "en_GB"] flat = mb._flatten_artist_credit([credit_dict]) self.assertEqual(flat, ("ALIASen", "ALIASSORTen", "CREDIT")) # test fr primary config["import"]["languages"] = ["fr"] flat = mb._flatten_artist_credit([credit_dict]) self.assertEqual(flat, ("ALIASfr_P", "ALIASSORTfr_P", "CREDIT")) # test for not matching non-primary config["import"]["languages"] = ["pt_BR", "fr"] flat = mb._flatten_artist_credit([credit_dict]) self.assertEqual(flat, ("ALIASfr_P", "ALIASSORTfr_P", "CREDIT")) class MBLibraryTest(unittest.TestCase): def test_match_track(self): with mock.patch("musicbrainzngs.search_recordings") as p: p.return_value = { "recording-list": [ { "title": "foo", "id": "bar", "length": 42, } ], } ti = list(mb.match_track("hello", "there"))[0] p.assert_called_with(artist="hello", recording="there", limit=5) self.assertEqual(ti.title, "foo") self.assertEqual(ti.track_id, "bar") def test_match_album(self): mbid = "d2a6f856-b553-40a0-ac54-a321e8e2da99" with mock.patch("musicbrainzngs.search_releases") as sp: sp.return_value = { "release-list": [ { "id": mbid, } ], } with mock.patch("musicbrainzngs.get_release_by_id") as gp: gp.return_value = { "release": { "title": "hi", "id": mbid, "status": "status", "medium-list": [ { "track-list": [ { "id": "baz", "recording": { "title": "foo", "id": "bar", "length": 42, }, "position": 9, "number": "A1", } ], "position": 5, } ], "artist-credit": [ { "artist": { "name": "some-artist", "id": "some-id", }, } ], "release-group": { "id": "another-id", }, } } ai = list(mb.match_album("hello", "there"))[0] sp.assert_called_with(artist="hello", release="there", limit=5) gp.assert_called_with(mbid, mock.ANY) self.assertEqual(ai.tracks[0].title, "foo") self.assertEqual(ai.album, "hi") def test_match_track_empty(self): with mock.patch("musicbrainzngs.search_recordings") as p: til = list(mb.match_track(" ", " ")) self.assertFalse(p.called) self.assertEqual(til, []) def test_match_album_empty(self): with mock.patch("musicbrainzngs.search_releases") as p: ail = list(mb.match_album(" ", " ")) self.assertFalse(p.called) self.assertEqual(ail, []) def test_follow_pseudo_releases(self): side_effect = [ { "release": { "title": "pseudo", "id": "d2a6f856-b553-40a0-ac54-a321e8e2da02", "status": "Pseudo-Release", "medium-list": [ { "track-list": [ { "id": "baz", "recording": { "title": "translated title", "id": "bar", "length": 42, }, "position": 9, "number": "A1", } ], "position": 5, } ], "artist-credit": [ { "artist": { "name": "some-artist", "id": "some-id", }, } ], "release-group": { "id": "another-id", }, "release-relation-list": [ { "type": "transl-tracklisting", "target": "d2a6f856-b553-40a0-ac54-a321e8e2da01", "direction": "backward", } ], } }, { "release": { "title": "actual", "id": "d2a6f856-b553-40a0-ac54-a321e8e2da01", "status": "Official", "medium-list": [ { "track-list": [ { "id": "baz", "recording": { "title": "original title", "id": "bar", "length": 42, }, "position": 9, "number": "A1", } ], "position": 5, } ], "artist-credit": [ { "artist": { "name": "some-artist", "id": "some-id", }, } ], "release-group": { "id": "another-id", }, "country": "COUNTRY", } }, ] with mock.patch("musicbrainzngs.get_release_by_id") as gp: gp.side_effect = side_effect album = mb.album_for_id("d2a6f856-b553-40a0-ac54-a321e8e2da02") self.assertEqual(album.country, "COUNTRY") def test_pseudo_releases_with_empty_links(self): side_effect = [ { "release": { "title": "pseudo", "id": "d2a6f856-b553-40a0-ac54-a321e8e2da02", "status": "Pseudo-Release", "medium-list": [ { "track-list": [ { "id": "baz", "recording": { "title": "translated title", "id": "bar", "length": 42, }, "position": 9, "number": "A1", } ], "position": 5, } ], "artist-credit": [ { "artist": { "name": "some-artist", "id": "some-id", }, } ], "release-group": { "id": "another-id", }, "release-relation-list": [], } }, ] with mock.patch("musicbrainzngs.get_release_by_id") as gp: gp.side_effect = side_effect album = mb.album_for_id("d2a6f856-b553-40a0-ac54-a321e8e2da02") self.assertEqual(album.country, None) def test_pseudo_releases_without_links(self): side_effect = [ { "release": { "title": "pseudo", "id": "d2a6f856-b553-40a0-ac54-a321e8e2da02", "status": "Pseudo-Release", "medium-list": [ { "track-list": [ { "id": "baz", "recording": { "title": "translated title", "id": "bar", "length": 42, }, "position": 9, "number": "A1", } ], "position": 5, } ], "artist-credit": [ { "artist": { "name": "some-artist", "id": "some-id", }, } ], "release-group": { "id": "another-id", }, } }, ] with mock.patch("musicbrainzngs.get_release_by_id") as gp: gp.side_effect = side_effect album = mb.album_for_id("d2a6f856-b553-40a0-ac54-a321e8e2da02") self.assertEqual(album.country, None) def test_pseudo_releases_with_unsupported_links(self): side_effect = [ { "release": { "title": "pseudo", "id": "d2a6f856-b553-40a0-ac54-a321e8e2da02", "status": "Pseudo-Release", "medium-list": [ { "track-list": [ { "id": "baz", "recording": { "title": "translated title", "id": "bar", "length": 42, }, "position": 9, "number": "A1", } ], "position": 5, } ], "artist-credit": [ { "artist": { "name": "some-artist", "id": "some-id", }, } ], "release-group": { "id": "another-id", }, "release-relation-list": [ { "type": "remaster", "target": "d2a6f856-b553-40a0-ac54-a321e8e2da01", "direction": "backward", } ], } }, ] with mock.patch("musicbrainzngs.get_release_by_id") as gp: gp.side_effect = side_effect album = mb.album_for_id("d2a6f856-b553-40a0-ac54-a321e8e2da02") self.assertEqual(album.country, None) def suite(): return unittest.TestLoader().loadTestsFromName(__name__) if __name__ == "__main__": unittest.main(defaultTest="suite")