Files
beets/test/plugins/test_musicbrainz.py
2026-06-29 11:20:15 +02:00

922 lines
32 KiB
Python

"""Tests for MusicBrainz API wrapper."""
from __future__ import annotations
import uuid
from typing import TYPE_CHECKING, ClassVar
import pytest
import requests
from beets.library import Item
from beets.test.helper import PluginMixin
from beetsplug import musicbrainz
from beetsplug.musicbrainz import MusicBrainzPlugin
from .factories import musicbrainz as factories
if TYPE_CHECKING:
from beetsplug._utils import musicbrainz as mb
_p = pytest.param
def alias_factory(**kwargs) -> mb.Alias:
return factories.AliasFactory.build(**kwargs)
def artist_credit_factory(**kwargs) -> mb.ArtistCredit:
return factories.ArtistCreditFactory.build(**kwargs)
def artist_relation_factory(**kwargs) -> mb.ArtistRelation:
return factories.ArtistRelationFactory.build(**kwargs)
def label_info_factory(**kwargs) -> mb.LabelInfo:
return factories.LabelInfoFactory.build(**kwargs)
def recording_factory(**kwargs) -> mb.Recording:
return factories.RecordingFactory.build(**kwargs)
def track_factory(**kwargs) -> mb.Track:
return factories.TrackFactory.build(**kwargs)
def url_relation_factory(**kwargs) -> mb.UrlRelation:
return factories.UrlRelationFactory.build(**kwargs)
def medium_factory(**kwargs) -> mb.Medium:
return factories.MediumFactory.build(**kwargs)
def release_factory(**kwargs) -> mb.Release:
return factories.ReleaseFactory.build(**kwargs)
class TestUtils:
@pytest.mark.parametrize(
"date, expected_parts",
[
("1987-03-01", (1987, 3, 1)),
("1987-03", (1987, 3, None)),
("1987", (1987, None, None)),
],
)
def test_get_date(self, date, expected_parts):
assert musicbrainz._get_date(date) == expected_parts
def test_single_artist(self):
credit = [artist_credit_factory(artist__name="Artist")]
assert MusicBrainzPlugin()._parse_artist_credits(credit) == {
"artist": "Artist",
"artist_id": "00000000-0000-0000-0000-000000000001",
"artist_sort": "Artist, The",
"artist_credit": "Artist Credit",
"artists": ["Artist"],
"artists_ids": ["00000000-0000-0000-0000-000000000001"],
"artists_sort": ["Artist, The"],
"artists_credit": ["Artist Credit"],
}
def test_two_artists(self):
credit = [
artist_credit_factory(artist__name="Artist", joinphrase=" AND "),
artist_credit_factory(artist__name="Other Artist", artist__index=2),
]
assert MusicBrainzPlugin()._parse_artist_credits(credit) == {
"artist": "Artist AND Other Artist",
"artist_id": "00000000-0000-0000-0000-000000000001",
"artist_sort": "Artist, The AND Other Artist, The",
"artist_credit": "Artist Credit AND Other Artist Credit",
"artists": ["Artist", "Other Artist"],
"artists_ids": [
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002",
],
"artists_sort": ["Artist, The", "Other Artist, The"],
"artists_credit": ["Artist Credit", "Other Artist Credit"],
}
@pytest.mark.parametrize(
"languages_config, expected_alias_name",
[
_p([], None, id="no alias without languages"),
_p(["en"], "Alias en", id="en primary"),
_p(["en_GB", "en"], "Alias en_GB", id="en_GB primary"),
_p(["en", "en_GB"], "Alias en", id="en primary over en_GB"),
_p(["fr"], "Alias fr_P", id="fr primary"),
_p(["pt_BR", "fr"], "Alias fr_P", id="non-primary ignored"),
],
)
def test_preferred_alias(
self, config, languages_config, expected_alias_name
):
aliases = [
alias_factory(locale="en"),
alias_factory(locale="en_GB"),
alias_factory(locale="fr", primary=False),
alias_factory(suffix="fr_P", locale="fr"),
alias_factory(locale="pt_BR", primary=False),
]
config["import"]["languages"] = languages_config
alias = musicbrainz._preferred_alias(aliases)
if expected_alias_name is None:
assert not alias
else:
assert alias["name"] == expected_alias_name
@pytest.mark.parametrize(
"label_infos, expected",
[
_p([], {"catalognum": None, "label": None}, id="no label"),
_p(
[label_info_factory(label=None)],
{"catalognum": "LAB123", "label": None},
id="no label",
),
_p(
[label_info_factory(label__name="[no label]")],
{"catalognum": "LAB123", "label": None},
id="label with ignored [no label] name",
),
_p(
[label_info_factory()],
{"catalognum": "LAB123", "label": "Label"},
id="normal case",
),
],
)
def test_parse_label_info(self, label_infos, expected):
assert MusicBrainzPlugin._parse_label_infos(label_infos) == expected
class MusicBrainzPluginTestMixin(PluginMixin):
plugin = "musicbrainz"
@pytest.fixture
def plugin_config(self):
return {}
@pytest.fixture
def mb(self, plugin_config):
self.config[self.plugin].set(plugin_config)
return musicbrainz.MusicBrainzPlugin()
class TestParseRecording(MusicBrainzPluginTestMixin):
def test_parse_recording(self, mb):
recording = recording_factory(
length=None,
disambiguation="Recording Disambiguation",
artist_relations=[
artist_relation_factory(
type="remixer",
artist__index=1,
artist__name="Recording Remixer",
),
artist_relation_factory(
type="arranger",
artist__index=2,
artist__name="Recording Arranger",
),
artist_relation_factory(
type="arranger",
artist__index=3,
artist__name="Another Recording Arranger",
),
artist_relation_factory(type="engineer"),
],
work_relations=[
{
"type": "performance",
"work": {
"id": "WORK ID",
"title": "WORK TITLE",
"artist_relations": [
artist_relation_factory(
type="lyricist",
artist__index=4,
artist__name="Recording Lyricist",
),
artist_relation_factory(
type="lyricist",
artist__index=5,
artist__name="Another Recording Lyricist",
),
artist_relation_factory(
type="composer",
artist__index=6,
artist__name="Recording Composer",
),
artist_relation_factory(
type="composer",
artist__index=7,
artist__name="Another Recording Composer",
),
artist_relation_factory(type="mastering"),
],
},
}
],
)
assert mb.track_info(recording) == {
"album": None,
"arrangers": ["Recording Arranger", "Another Recording Arranger"],
"arrangers_ids": [
"00000000-0000-0000-0000-000000000002",
"00000000-0000-0000-0000-000000000003",
],
"artist": "Recording Artist",
"artist_credit": "Recording Artist Credit",
"artist_id": "00000000-0000-0000-0000-000000000001",
"artist_sort": "Recording Artist, The",
"artists": ["Recording Artist"],
"artists_credit": ["Recording Artist Credit"],
"artists_ids": ["00000000-0000-0000-0000-000000000001"],
"artists_sort": ["Recording Artist, The"],
"bpm": None,
"composer_sort": "Recording Composer, The, Another Recording Composer, The",
"composers": ["Recording Composer", "Another Recording Composer"],
"composers_ids": [
"00000000-0000-0000-0000-000000000006",
"00000000-0000-0000-0000-000000000007",
],
"data_source": "MusicBrainz",
"data_url": "https://musicbrainz.org/recording/00000000-0000-0000-0000-000000001001",
"disctitle": None,
"genres": None,
"index": None,
"initial_key": None,
"isrc": None,
"length": None,
"lyricists": ["Recording Lyricist", "Another Recording Lyricist"],
"lyricists_ids": [
"00000000-0000-0000-0000-000000000004",
"00000000-0000-0000-0000-000000000005",
],
"mb_workid": "WORK ID",
"media": None,
"medium": None,
"medium_index": None,
"medium_total": None,
"release_track_id": None,
"remixers": ["Recording Remixer"],
"remixers_ids": ["00000000-0000-0000-0000-000000000001"],
"title": "Recording",
"track_alt": None,
"track_id": "00000000-0000-0000-0000-000000001001",
"trackdisambig": "Recording Disambiguation",
"work": "WORK TITLE",
"work_disambig": None,
}
class TestParseMedia(MusicBrainzPluginTestMixin):
def test_multiple_mediums(self, mb):
first_medium = medium_factory(
title="First Medium",
position=1,
tracks=[
track_factory(recording__length=100000),
track_factory(
position=2,
recording__index=2,
recording__length=200000,
recording__title="Other Recording",
),
],
)
second_medium = medium_factory(
title="Second Medium",
position=2,
pregap=track_factory(recording__title="Pregap", position=0),
tracks=[track_factory()],
)
release = release_factory(media=[first_medium, second_medium])
d = mb.album_info(release)
assert d.mediums == 2
t = d.tracks
assert len(t) == 4
assert t[0].title == "Recording"
assert t[0].track_id == "00000000-0000-0000-0000-000000001001"
assert t[0].length == 100.0
assert t[0].medium == 1
assert t[0].medium_index == 1
assert t[0].index == 1
assert t[0].disctitle == "First Medium"
assert t[1].title == "Other Recording"
assert t[1].track_id == "00000000-0000-0000-0000-000000001002"
assert t[1].length == 200.0
assert t[1].medium == 1
assert t[1].medium_index == 2
assert t[1].index == 2
assert t[1].disctitle == "First Medium"
assert t[2].title == "Pregap"
assert t[2].medium == 2
assert t[2].medium_index == 0
assert t[2].index == 3
assert t[2].disctitle == "Second Medium"
assert t[3].medium == 2
assert t[3].medium_index == 1
assert t[3].index == 4
assert t[3].disctitle == "Second Medium"
def test_track_overrides_recording(self, mb):
release = release_factory(
media__0__tracks=[
track_factory(
length=1000.0,
artist_credit=[
artist_credit_factory(
artist__name="Track Artist", joinphrase=" & "
),
artist_credit_factory(
artist__name="Other Track Artist", artist__index=2
),
],
recording__length=2000.0,
recording__artist_credit=[
artist_credit_factory(
artist__name="Recording Artist", joinphrase=" & "
),
artist_credit_factory(
artist__name="Other Recording Artist",
artist__index=2,
),
],
)
]
)
track = mb.album_info(release).tracks[0]
assert track.length == 1.0
assert track.artist == "Track Artist & Other Track Artist"
assert track.artist_id == "00000000-0000-0000-0000-000000000001"
assert (
track.artist_sort == "Track Artist, The & Other Track Artist, The"
)
assert (
track.artist_credit
== "Track Artist Credit & Other Track Artist Credit"
)
assert track.artists == ["Track Artist", "Other Track Artist"]
assert track.artists_ids == [
"00000000-0000-0000-0000-000000000001",
"00000000-0000-0000-0000-000000000002",
]
assert track.artists_sort == [
"Track Artist, The",
"Other Track Artist, The",
]
assert track.artists_credit == [
"Track Artist Credit",
"Other Track Artist Credit",
]
def test_missing_tracks(self, mb):
release = release_factory(
media=[
medium_factory(),
medium_factory(
tracks=[
track_factory(),
track_factory(
recording__title="Other Recording",
recording__disambiguation="SECOND TRACK",
),
]
),
]
)
assert mb.album_info(release).mediums == 2
@pytest.mark.parametrize(
"beets_match_config, expected_titles",
[
_p({}, ("Audio",), id="only audio tracks by default"),
_p(
{"ignore_data_tracks": False},
("Audio", "Data"),
id="include data tracks",
),
_p(
{"ignore_data_tracks": False, "ignore_video_tracks": False},
("Audio", "Video: Video", "Data"),
id="include data and video tracks",
),
_p({"ignored_media": "Vinyl"}, (), id="ignore all tracks"),
],
)
def test_data_tracks(self, config, beets_match_config, mb, expected_titles):
medium = medium_factory(
format="Vinyl",
tracks=[
track_factory(recording__title="Audio"),
track_factory(recording__title="[data track]"),
track_factory(recording__title="Video", recording__video=True),
],
data_tracks=[track_factory(recording__title="Data")],
)
release = release_factory(media=[medium])
config.set({"match": beets_match_config})
actual_titles = tuple(t.title for t in mb.album_info(release).tracks)
assert actual_titles == expected_titles
class TestParseRelease(MusicBrainzPluginTestMixin):
def test_parse_release(self, config, mb):
config["match"]["preferred"]["countries"] = ["US"]
mb.config.set(
{
"external_ids": {
"discogs": True,
"bandcamp": True,
"spotify": False,
}
}
)
release = release_factory(
url_relations=[
url_relation_factory(
url__resource="https://discogs.com/release/123456"
),
url_relation_factory(
url__resource="https://open.spotify.com/album/ABCDab2ImQyHZ9sXCXFyZ8"
),
url_relation_factory(
url__resource="https://somemusic.bandcamp.com/album/somealbum"
),
]
)
d = mb.album_info(release)
assert d == {
"album": "Album",
"album_id": "00000000-0000-0000-0000-000001000001",
"albumdisambig": "Album Disambiguation",
"albumstatus": "Official",
"albumtype": "album",
"albumtypes": ["album"],
"artist": "Artist",
"artist_credit": "Artist Credit",
"artist_id": "00000000-0000-0000-0000-000000000011",
"artist_sort": "Artist, The",
"artists": ["Artist"],
"artists_credit": ["Artist Credit"],
"artists_ids": ["00000000-0000-0000-0000-000000000011"],
"artists_sort": ["Artist, The"],
"asin": "Album Asin",
"bandcamp_album_id": "https://somemusic.bandcamp.com/album/somealbum",
"barcode": "0000000000000",
"catalognum": "LAB123",
"country": "US",
"data_source": "MusicBrainz",
"data_url": "https://musicbrainz.org/release/00000000-0000-0000-0000-000001000001",
"day": 1,
"discogs_album_id": "123456",
"discogs_albumid": None,
"discogs_artistid": None,
"discogs_labelid": None,
"genres": None,
"label": "Label",
"language": "eng",
"media": "Digital Media",
"mediums": 1,
"month": 1,
"original_day": 3,
"original_month": 2,
"original_year": 2001,
"release_group_title": "Release Group",
"releasegroup_id": "00000000-0000-0000-0000-000000000101",
"releasegroupdisambig": "Release Group Disambiguation",
"script": "Latn",
"style": None,
"tracks": [
{
"album": None,
"arrangers": None,
"arrangers_ids": [],
"artist": "Recording Artist",
"artist_credit": "Recording Artist Credit",
"artist_id": "00000000-0000-0000-0000-000000000001",
"artist_sort": "Recording Artist, The",
"artists": ["Recording Artist"],
"artists_credit": ["Recording Artist Credit"],
"artists_ids": ["00000000-0000-0000-0000-000000000001"],
"artists_sort": ["Recording Artist, The"],
"bpm": None,
"composer_sort": None,
"composers": None,
"composers_ids": [],
"data_source": "MusicBrainz",
"data_url": "https://musicbrainz.org/recording/00000000-0000-0000-0000-000000001001",
"disctitle": "Medium",
"genres": None,
"index": 1,
"initial_key": None,
"isrc": None,
"length": 0.36,
"lyricists": None,
"lyricists_ids": [],
"mb_workid": None,
"media": "Digital Media",
"medium": 1,
"medium_index": 1,
"medium_total": 1,
"release_track_id": "00000000-0000-0000-0000-000000010001",
"remixers": None,
"remixers_ids": [],
"title": "Recording",
"track_alt": "A1",
"track_id": "00000000-0000-0000-0000-000000001001",
"trackdisambig": None,
"work": None,
"work_disambig": None,
}
],
"va": False,
"year": 2020,
}
def test_detect_various_artists(self, mb):
release = release_factory(
artist_credit=[
artist_credit_factory(artist__id=musicbrainz.VARIOUS_ARTISTS_ID)
]
)
assert mb.album_info(release).va
def test_missing_language(self, mb):
release = release_factory(text_representation__language=None)
assert mb.album_info(release).language is None
@pytest.mark.parametrize(
"plugin_config, expected_genres",
[
_p({"genres": False}, None, id="genres disabled"),
_p({"genres": True, "genres_tag": "genre"}, ["Genre"], id="use genres"),
_p({"genres": True, "genres_tag": "tag"}, ["Tag"], id="use tags"),
],
) # fmt: skip
def test_genres(self, mb, expected_genres):
assert mb.album_info(release_factory()).genres == expected_genres
@pytest.mark.parametrize(
"plugin_config", [_p({"aliases_as_credits": True})]
)
def test_parse_aliased_titles(self, config, mb: MusicBrainzPlugin):
release = release_factory()
config["import"]["languages"] = ["en"]
d = mb.album_info(release)
assert d.album == "Album Alias en"
assert d.release_group_title == "Release Group Alias en"
assert d.tracks[0].title == "Recording Alias en"
album_artist = "Artist Alias en"
assert d.artist == album_artist
assert d.artists == [album_artist]
# There is no artist credit specific alias
assert d.artist_credit == album_artist
assert d.artists_credit == [album_artist]
album_artist_sort = "Artist Alias en, The"
assert d.artist_sort == album_artist_sort
assert d.artists_sort == [album_artist_sort]
first_track = d.tracks[0]
track_artist = "Recording Artist Alias en"
assert first_track.artist == track_artist
assert first_track.artists == [track_artist]
# There is no artist credit specific alias
assert first_track.artist_credit == track_artist
assert first_track.artists_credit == [track_artist]
track_artist_sort = "Recording Artist Alias en, The"
assert first_track.artist_sort == track_artist_sort
assert first_track.artists_sort == [track_artist_sort]
@pytest.mark.parametrize(
"plugin_config, expected_artist_credit",
[
_p(
{"aliases_as_credits": False},
{
"track_artist_credit": "Recording Artist Credit",
"track_artists_credit": ["Recording Artist Credit"],
"artist_credit": "Artist Credit",
"artists_credit": ["Artist Credit"],
},
id="no aliases",
),
_p(
{"aliases_as_credits": True},
{
"artist_credit": "Artist Alias en",
"artists_credit": ["Artist Alias en"],
"track_artist_credit": "Recording Artist Alias en",
"track_artists_credit": ["Recording Artist Alias en"],
},
id="aliases",
),
],
)
def test_aliases_as_credits(
self, config, mb: MusicBrainzPlugin, expected_artist_credit
):
config["import"]["languages"] = ["en"]
release = release_factory()
d = mb.album_info(release)
assert d.artist_credit == expected_artist_credit["artist_credit"]
assert d.artists_credit == expected_artist_credit["artists_credit"]
first_track = d.tracks[0]
assert (
first_track.artist_credit
== expected_artist_credit["track_artist_credit"]
)
assert (
first_track.artists_credit
== expected_artist_credit["track_artists_credit"]
)
def test_ensure_complete_recordings(self, monkeypatch, mb):
titles = ["Recording", "Other Recording"]
initial_recordings = [
recording_factory(index=idx, title=t)
for idx, t in enumerate(titles)
]
complete_recordings = [
{**r, "url_relations": [url_relation_factory()]}
for r in initial_recordings
]
monkeypatch.setattr("beetsplug.musicbrainz.BROWSE_CHUNKSIZE", 1)
monkeypatch.setattr("beetsplug.musicbrainz.BROWSE_MAXTRACKS", 1)
monkeypatch.setattr(
mb.mb_api,
"browse_recordings",
lambda offset=0, **__: [complete_recordings[offset]],
)
release = release_factory(
media__0__tracks=[
track_factory(recording=r) for r in initial_recordings
]
)
mb._ensure_complete_recordings(release)
new_recordings = [
t["recording"] for m in release["media"] for t in m["tracks"]
]
assert new_recordings == complete_recordings
def test_album_info_browse_recordings_prefers_alias_over_track_title(
self, config, monkeypatch, mb
):
config["import"]["languages"] = ["en"]
recording = recording_factory(
title="Recording Title",
aliases=[alias_factory(type="Recording name", locale="en")],
)
browsed_recording = {
**recording,
"url_relations": [url_relation_factory()],
}
monkeypatch.setattr("beetsplug.musicbrainz.BROWSE_CHUNKSIZE", 1)
monkeypatch.setattr("beetsplug.musicbrainz.BROWSE_MAXTRACKS", 0)
monkeypatch.setattr(
mb.mb_api,
"browse_recordings",
lambda offset=0, **__: [browsed_recording] if offset == 0 else [],
)
release = release_factory(
media__0__tracks=[
track_factory(recording=recording, title="Track Title")
]
)
album = mb.album_info(release)
assert album.tracks[0].title == "Alias en"
class TestPseudoRelease(MusicBrainzPluginTestMixin):
ACTUAL_RELEASE = release_factory(index=1, country="US")
PSEUDO_WITHOUT_LINKS = release_factory(
index=2, status="Pseudo-Release", country=None
)
PSEUDO_INVALID_LINK = release_factory(
index=3,
status="Pseudo-Release",
country=None,
release_relations=[
{
"type": "remaster",
"direction": "backward",
"release": {"id": "d2a6f856-b553-40a0-ac54-a321e8e2da01"},
}
],
)
PSEUDO_VALID_LINK = release_factory(
index=4,
status="Pseudo-Release",
country=None,
release_relations=[
{
"type": "transl-tracklisting",
"direction": "backward",
"release": {"id": ACTUAL_RELEASE["id"]},
}
],
)
@pytest.fixture(autouse=True)
def setup_album_lookup(self, monkeypatch, mb):
releases = [
self.ACTUAL_RELEASE,
self.PSEUDO_WITHOUT_LINKS,
self.PSEUDO_INVALID_LINK,
self.PSEUDO_VALID_LINK,
]
release_by_id = {r["id"]: r for r in releases}
monkeypatch.setattr(
mb.mb_api, "get_release", lambda id_: release_by_id[id_]
)
@pytest.mark.parametrize(
"release_id, expected_country",
[
_p(ACTUAL_RELEASE["id"], "US", id="actual release"),
_p(PSEUDO_WITHOUT_LINKS["id"], None, id="pseudo without links"),
_p(PSEUDO_INVALID_LINK["id"], None, id="pseudo with invalid link"),
_p(PSEUDO_VALID_LINK["id"], "US", id="pseudo with valid link"),
],
)
def test_follow_pseudo_release(self, mb, release_id, expected_country):
album = mb.album_for_id(release_id)
assert album
assert album.country == expected_country
class TestMusicBrainzPlugin(MusicBrainzPluginTestMixin):
mbid = "d2a6f856-b553-40a0-ac54-a321e8e2da99"
RECORDING: ClassVar[mb.Recording] = recording_factory()
@pytest.mark.parametrize(
"plugin_config,va_likely,expected_additional_criteria",
[
_p({}, False, {"artist": "Artist "}, id="default"),
_p(
{},
True,
{"arid": "89ad4ac3-39f7-470e-963a-56509c546377"},
id="va likely",
),
_p(
{"extra_tags": ["label", "catalognum"]},
False,
{"artist": "Artist ", "label": "abc", "catno": "ABC123"},
id="value-based extra_tags",
),
_p(
{"extra_tags": ["alias", "tracks"]},
False,
{"artist": "Artist ", "alias": " Album", "tracks": "3"},
id="non-value-based extra_tags",
),
],
)
def test_get_album_criteria(
self, mb, va_likely, expected_additional_criteria
):
items = [
Item(catalognum="ABC 123", label="abc"),
Item(catalognum="ABC 123", label="abc"),
Item(catalognum="ABC 123", label="def"),
]
assert mb.get_album_criteria(items, "Artist ", " Album", va_likely) == {
"release": " Album",
**expected_additional_criteria,
}
def test_item_candidates(self, monkeypatch, mb):
monkeypatch.setattr(
"beetsplug._utils.musicbrainz.MusicBrainzAPI.get_json",
lambda *_, **__: {"recordings": [self.RECORDING]},
)
monkeypatch.setattr(
"beetsplug._utils.musicbrainz.MusicBrainzAPI.get_recording",
lambda *_, **__: self.RECORDING,
)
candidates = list(mb.item_candidates(Item(), "hello", "there"))
assert len(candidates) == 1
assert candidates[0].track_id == self.RECORDING["id"]
def test_candidates(self, monkeypatch, mb):
monkeypatch.setattr(
"beetsplug._utils.musicbrainz.MusicBrainzAPI.get_json",
lambda *_, **__: {"releases": [{"id": self.mbid}]},
)
monkeypatch.setattr(
"beetsplug._utils.musicbrainz.MusicBrainzAPI.get_release",
lambda *_, **__: release_factory(
id=self.mbid, media=[medium_factory()]
),
)
candidates = list(mb.candidates([], "hello", "there", False))
assert len(candidates) == 1
assert (
candidates[0].tracks[0].track_id
== "00000000-0000-0000-0000-000000001001"
)
assert candidates[0].album == "Album"
def test_import_handles_404_gracefully(self, mb, requests_mock):
id_ = uuid.uuid4()
response = requests.Response()
response.status_code = 404
requests_mock.get(
f"/ws/2/release/{id_}",
exc=requests.exceptions.HTTPError(response=response),
)
res = mb.album_for_id(str(id_))
assert res is None
def test_import_propagates_non_404_errors(self, mb):
class DummyResponse:
status_code = 500
error = requests.exceptions.HTTPError(response=DummyResponse())
def raise_error(*args, **kwargs):
raise error
# Simulate mb.mb_api.get_release raising a non-404 HTTP error
mb.mb_api.get_release = raise_error
with pytest.raises(requests.exceptions.HTTPError) as excinfo:
mb.album_for_id(str(uuid.uuid4()))
# Ensure the exact error is propagated, not swallowed
assert excinfo.value is error
@pytest.mark.parametrize(
"input_,expected",
[
("??-??-??", (None, None, None)),
("??-01-??", (None, 1, None)),
("??-??-02", (None, None, 2)),
("??-01-02", (None, 1, 2)),
("2010-??-01", (2010, None, 1)),
("2010-01-not_an_int", (2010, 1, None)),
("2010", (2010, None, None)),
("2010-01", (2010, 1, None)),
("2010-01-02", (2010, 1, 2)),
],
)
def test_get_date(self, input_, expected):
assert musicbrainz._get_date(input_) == expected