feat: add MusicBrainz fallback to !similar and !tags commands
Remove early return on missing Last.fm API key. Both commands now fall back to MusicBrainz (mb_search_artist -> mb_artist_tags -> mb_find_similar_recordings) when no API key is configured or when Last.fm returns empty results. Same pattern used by discover_similar. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -339,12 +339,56 @@ class TestFmtMatch:
|
||||
|
||||
|
||||
class TestCmdSimilar:
|
||||
def test_no_api_key(self):
|
||||
def test_no_api_key_mb_fallback(self):
|
||||
"""No API key falls back to MusicBrainz for similar results."""
|
||||
bot = _FakeBot(api_key="")
|
||||
msg = _Msg(text="!similar Tool")
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
mb_picks = [{"artist": "MB Artist", "title": "MB Song"}]
|
||||
with patch.dict("os.environ", {}, clear=True), \
|
||||
patch("plugins._musicbrainz.mb_search_artist",
|
||||
return_value="mbid-123"), \
|
||||
patch("plugins._musicbrainz.mb_artist_tags",
|
||||
return_value=["rock", "metal"]), \
|
||||
patch("plugins._musicbrainz.mb_find_similar_recordings",
|
||||
return_value=mb_picks):
|
||||
asyncio.run(_mod.cmd_similar(bot, msg))
|
||||
assert any("not configured" in r for r in bot.replied)
|
||||
assert any("Similar to Tool" in r for r in bot.replied)
|
||||
assert any("MB Artist" in r for r in bot.replied)
|
||||
|
||||
def test_no_api_key_mb_no_results(self):
|
||||
"""No API key + MusicBrainz returns nothing shows 'no similar'."""
|
||||
bot = _FakeBot(api_key="")
|
||||
msg = _Msg(text="!similar Tool")
|
||||
with patch.dict("os.environ", {}, clear=True), \
|
||||
patch("plugins._musicbrainz.mb_search_artist",
|
||||
return_value=None):
|
||||
asyncio.run(_mod.cmd_similar(bot, msg))
|
||||
assert any("No similar artists" in r for r in bot.replied)
|
||||
|
||||
def test_no_api_key_play_mode(self):
|
||||
"""No API key + play mode delegates to cmd_play via MB results."""
|
||||
bot = _FakeBot(api_key="")
|
||||
msg = _Msg(text="!similar play Tool")
|
||||
mb_picks = [{"artist": "MB Band", "title": "MB Track"}]
|
||||
play_called = []
|
||||
|
||||
async def fake_play(b, m):
|
||||
play_called.append(m.text)
|
||||
|
||||
music_mod = MagicMock()
|
||||
music_mod.cmd_play = fake_play
|
||||
bot.registry._modules["music"] = music_mod
|
||||
|
||||
with patch.dict("os.environ", {}, clear=True), \
|
||||
patch("plugins._musicbrainz.mb_search_artist",
|
||||
return_value="mbid-123"), \
|
||||
patch("plugins._musicbrainz.mb_artist_tags",
|
||||
return_value=["rock"]), \
|
||||
patch("plugins._musicbrainz.mb_find_similar_recordings",
|
||||
return_value=mb_picks):
|
||||
asyncio.run(_mod.cmd_similar(bot, msg))
|
||||
assert len(play_called) == 1
|
||||
assert "MB Band" in play_called[0]
|
||||
|
||||
def test_no_artist_nothing_playing(self):
|
||||
bot = _FakeBot()
|
||||
@@ -468,12 +512,29 @@ class TestCmdSimilar:
|
||||
|
||||
|
||||
class TestCmdTags:
|
||||
def test_no_api_key(self):
|
||||
def test_no_api_key_mb_fallback(self):
|
||||
"""No API key falls back to MusicBrainz for tags."""
|
||||
bot = _FakeBot(api_key="")
|
||||
msg = _Msg(text="!tags Tool")
|
||||
with patch.dict("os.environ", {}, clear=True):
|
||||
with patch.dict("os.environ", {}, clear=True), \
|
||||
patch("plugins._musicbrainz.mb_search_artist",
|
||||
return_value="mbid-123"), \
|
||||
patch("plugins._musicbrainz.mb_artist_tags",
|
||||
return_value=["rock", "progressive metal", "art rock"]):
|
||||
asyncio.run(_mod.cmd_tags(bot, msg))
|
||||
assert any("not configured" in r for r in bot.replied)
|
||||
assert any("Tool:" in r for r in bot.replied)
|
||||
assert any("rock" in r for r in bot.replied)
|
||||
assert any("progressive metal" in r for r in bot.replied)
|
||||
|
||||
def test_no_api_key_mb_no_results(self):
|
||||
"""No API key + MusicBrainz returns nothing shows 'no tags'."""
|
||||
bot = _FakeBot(api_key="")
|
||||
msg = _Msg(text="!tags Obscure")
|
||||
with patch.dict("os.environ", {}, clear=True), \
|
||||
patch("plugins._musicbrainz.mb_search_artist",
|
||||
return_value=None):
|
||||
asyncio.run(_mod.cmd_tags(bot, msg))
|
||||
assert any("No tags found" in r for r in bot.replied)
|
||||
|
||||
def test_no_artist_nothing_playing(self):
|
||||
bot = _FakeBot()
|
||||
|
||||
Reference in New Issue
Block a user