soulsync/tests/library/test_direct_id_match.py
BoulderBadgeDad 8b7609cdb2 Manual match: paste a MusicBrainz ID/URL to match directly (Ashh)
Ashh: the manual-match modal fuzzy-searches a service and shows the top 8.
When the right release isn't in those 8 (common title — their example was
"Idols", which returns 8 unrelated releases and not Yungblud's), there was no
way through. But the user usually already knows the exact MBID.

Now the modal's search box doubles as a direct-ID box. Paste a MusicBrainz
MBID (bare UUID or a musicbrainz.org URL) and SoulSync looks that exact
entity up and shows it as the single result to confirm + Match — no fighting
the search ranking.

- core/library/direct_id.py: pure detector, returns the canonical ID only
  when the text unambiguously IS one (whole-query UUID, or a UUID inside a
  musicbrainz.org URL). "Idols", "Yungblud Idols", a UUID buried in free
  text → None, so normal search is never hijacked.
- _search_service: direct-ID fast path before the fuzzy search —
  get_release (→ get_release_group fallback for albums) / get_artist /
  get_recording. A pasted-but-unresolvable ID falls THROUGH to fuzzy search,
  so a typo can't dead-end the modal.
- UI: MusicBrainz placeholder now says "…or paste a MusicBrainz ID/URL".

Detector is service-keyed so Spotify/iTunes/etc. direct IDs can be added
later; today only MusicBrainz has a confirmable direct lookup, matching the
reporter's ask + screenshot. 9 tests: detector truth table (bare/URL/plain/
buried/other-service) + dispatch (confirmed release, release-group fallback,
unresolvable→fuzzy, plain query skips direct lookup).
2026-06-07 13:04:17 -07:00

113 lines
4.5 KiB
Python

"""Direct-ID manual matching (Ashh: 'just slap MB ID in that search').
When the right release isn't in the top-8 fuzzy results, the user pastes the
exact ID. extract_direct_id detects it (pure); _search_service confirms it
via a direct lookup and returns just that entity, falling back to fuzzy
search if the paste only looks ID-ish.
"""
from __future__ import annotations
from types import SimpleNamespace
from unittest.mock import MagicMock
from core.library.direct_id import extract_direct_id
MBID = "1af02ea7-3f00-40ca-804b-41e2dca7e4a9"
# ── pure detector ────────────────────────────────────────────────────────────
def test_bare_mbid_detected():
assert extract_direct_id("musicbrainz", "album", MBID) == MBID
assert extract_direct_id("musicbrainz", "artist", MBID.upper()) == MBID # normalized
def test_mbid_in_url_detected():
for url in (
f"https://musicbrainz.org/release/{MBID}",
f"https://musicbrainz.org/release/{MBID}/cover-art",
f" https://beta.musicbrainz.org/artist/{MBID} ",
):
assert extract_direct_id("musicbrainz", "album", url) == MBID
def test_plain_text_query_is_not_an_id():
assert extract_direct_id("musicbrainz", "album", "Idols") is None
assert extract_direct_id("musicbrainz", "album", "Yungblud Idols") is None
assert extract_direct_id("musicbrainz", "album", "") is None
assert extract_direct_id("musicbrainz", "album", " ") is None
def test_loose_uuid_without_url_context_is_rejected():
# A UUID embedded in free text (not the whole query, no MB URL) is NOT
# treated as a direct ID — avoids hijacking a genuine search.
assert extract_direct_id("musicbrainz", "album", f"album {MBID} deluxe") is None
def test_non_musicbrainz_services_have_no_direct_id_yet():
assert extract_direct_id("spotify", "album", MBID) is None
assert extract_direct_id("deezer", "track", "12345") is None
# ── _search_service direct dispatch ──────────────────────────────────────────
def _wire_mb(monkeypatch, **methods):
import core.library.service_search as ss
mb_client = MagicMock(**methods)
worker = SimpleNamespace(mb_service=SimpleNamespace(mb_client=mb_client))
monkeypatch.setattr(ss, "mb_worker", worker)
return ss, mb_client
def test_pasted_mbid_returns_single_confirmed_release(monkeypatch):
ss, mb_client = _wire_mb(monkeypatch)
mb_client.get_release.return_value = {
"id": MBID, "title": "Idols", "date": "2025-06-20",
"artist-credit": [{"name": "Yungblud"}],
}
results = ss._search_service("musicbrainz", "album", MBID)
assert len(results) == 1
assert results[0]["id"] == MBID
assert results[0]["name"] == "Idols"
assert "Direct ID match" in results[0]["extra"]
assert "Yungblud" in results[0]["extra"]
mb_client.get_release.assert_called_once_with(MBID)
mb_client.search_release.assert_not_called() # never fuzzy-searched
def test_album_falls_back_to_release_group(monkeypatch):
ss, mb_client = _wire_mb(
monkeypatch,
get_release=lambda mbid: None,
get_release_group=lambda mbid: {"id": MBID, "title": "Idols", "artist-credit": []},
)
results = ss._search_service("musicbrainz", "album", MBID)
assert len(results) == 1 and results[0]["name"] == "Idols"
def test_unresolvable_mbid_falls_through_to_fuzzy(monkeypatch):
# ID-shaped but doesn't resolve → don't dead-end; run the normal search.
ss, mb_client = _wire_mb(
monkeypatch,
get_release=lambda mbid: None,
get_release_group=lambda mbid: None,
search_release=lambda q, limit=8, strict=False: [
{"id": "other", "title": "Idols (fuzzy)", "artist-credit": [], "date": "", "score": 90},
],
)
results = ss._search_service("musicbrainz", "album", MBID)
assert len(results) == 1 and results[0]["id"] == "other" # fuzzy result
def test_plain_query_skips_direct_lookup(monkeypatch):
ss, mb_client = _wire_mb(
monkeypatch,
search_release=lambda q, limit=8, strict=False: [
{"id": "r1", "title": "Idols", "artist-credit": [], "date": "", "score": 100},
],
)
results = ss._search_service("musicbrainz", "album", "Idols")
assert results[0]["id"] == "r1"
mb_client.get_release.assert_not_called() # no wasted direct lookup