music-assistant-server

14.3 KBPY
test_compare.py
14.3 KB379 lines • python
1"""Tests for mediaitem compare helper functions."""
2
3from music_assistant_models import media_items
4from music_assistant_models.enums import ExternalID
5
6from music_assistant.helpers import compare
7
8
9def test_compare_version() -> None:
10    """Test the version compare helper."""
11    assert compare.compare_version("Remaster", "remaster") is True
12    assert compare.compare_version("Remastered", "remaster") is True
13    assert compare.compare_version("Remaster", "") is False
14    assert compare.compare_version("Remaster", "Remix") is False
15    assert compare.compare_version("", "Deluxe") is False
16    assert compare.compare_version("", "Live") is False
17    assert compare.compare_version("Live", "live") is True
18    assert compare.compare_version("Live", "live version") is True
19    assert compare.compare_version("Live version", "live") is True
20    assert compare.compare_version("Deluxe Edition", "Deluxe") is True
21    assert compare.compare_version("Deluxe Karaoke Edition", "Deluxe") is False
22    assert compare.compare_version("Deluxe Karaoke Edition", "Karaoke") is False
23    assert compare.compare_version("Deluxe Edition", "Edition Deluxe") is True
24    assert compare.compare_version("", "Karaoke Version") is False
25    assert compare.compare_version("Karaoke", "Karaoke Version") is True
26    assert compare.compare_version("Remaster", "Remaster Edition Deluxe") is False
27    assert compare.compare_version("Remastered Version", "Deluxe Version") is False
28
29
30def test_compare_artist() -> None:
31    """Test artist comparison."""
32    artist_a = media_items.Artist(
33        item_id="1",
34        provider="test1",
35        name="Artist A",
36        provider_mappings={
37            media_items.ProviderMapping(
38                item_id="1", provider_domain="test", provider_instance="test1"
39            )
40        },
41    )
42    artist_b = media_items.Artist(
43        item_id="1",
44        provider="test2",
45        name="Artist A",
46        provider_mappings={
47            media_items.ProviderMapping(
48                item_id="2", provider_domain="test", provider_instance="test2"
49            )
50        },
51    )
52    # test match on name match
53    assert compare.compare_artist(artist_a, artist_b) is True
54    # test match on name mismatch
55    artist_b.name = "Artist B"
56    assert compare.compare_artist(artist_a, artist_b) is False
57    # test on exact item_id match
58    artist_b.item_id = artist_a.item_id
59    artist_b.provider = artist_a.provider
60    assert compare.compare_artist(artist_a, artist_b) is True
61    # test on external id match
62    artist_b.name = "Artist B"
63    artist_b.item_id = "2"
64    artist_b.provider = "test2"
65    artist_a.external_ids = {(ExternalID.MB_ARTIST, "123")}
66    artist_b.external_ids = artist_a.external_ids
67    assert compare.compare_artist(artist_a, artist_b) is True
68    # test on external id mismatch
69    artist_b.name = artist_a.name
70    artist_b.external_ids = {(ExternalID.MB_ARTIST, "1234")}
71    assert compare.compare_artist(artist_a, artist_b) is False
72    # test on external id mismatch while name matches
73    artist_a = media_items.Artist(
74        item_id="1",
75        provider="test1",
76        name="Artist A",
77        external_ids={(ExternalID.MB_ARTIST, "123")},
78        provider_mappings={
79            media_items.ProviderMapping(
80                item_id="1", provider_domain="test", provider_instance="test1"
81            )
82        },
83    )
84    artist_b = media_items.Artist(
85        item_id="1",
86        provider="test2",
87        name="Artist A",
88        external_ids={(ExternalID.MB_ARTIST, "abc")},
89        provider_mappings={
90            media_items.ProviderMapping(
91                item_id="2", provider_domain="test", provider_instance="test2"
92            )
93        },
94    )
95    assert compare.compare_artist(artist_a, artist_b) is False
96
97
98def test_compare_album() -> None:
99    """Test album comparison."""
100    album_a = media_items.Album(
101        item_id="1",
102        provider="test1",
103        name="Album A",
104        provider_mappings={
105            media_items.ProviderMapping(
106                item_id="1", provider_domain="test", provider_instance="test1"
107            )
108        },
109        artists=media_items.UniqueList(
110            [
111                media_items.Artist(
112                    item_id="1",
113                    provider="test1",
114                    name="Artist A",
115                    provider_mappings={
116                        media_items.ProviderMapping(
117                            item_id="1", provider_domain="test", provider_instance="test1"
118                        )
119                    },
120                )
121            ]
122        ),
123    )
124    album_b = media_items.Album(
125        item_id="1",
126        provider="test2",
127        name="Album A",
128        provider_mappings={
129            media_items.ProviderMapping(
130                item_id="2", provider_domain="test", provider_instance="test2"
131            )
132        },
133        artists=media_items.UniqueList(
134            [
135                media_items.Artist(
136                    item_id="1",
137                    provider="test1",
138                    name="Artist A",
139                    provider_mappings={
140                        media_items.ProviderMapping(
141                            item_id="1", provider_domain="test", provider_instance="test1"
142                        )
143                    },
144                )
145            ]
146        ),
147    )
148    # test match on name match
149    assert compare.compare_album(album_a, album_b) is True
150    # test match on name mismatch
151    album_b.name = "Album B"
152    assert compare.compare_album(album_a, album_b) is False
153    # test on version mismatch
154    album_b.name = album_a.name
155    album_b.version = "Deluxe"
156    assert compare.compare_album(album_a, album_b) is False
157    album_b.version = "Remix"
158    assert compare.compare_album(album_a, album_b) is False
159    # test on version match
160    album_b.name = album_a.name
161    album_a.version = "Deluxe"
162    album_b.version = "Deluxe Edition"
163    assert compare.compare_album(album_a, album_b) is True
164    # test on exact item_id match
165    album_b.item_id = album_a.item_id
166    album_b.provider = album_a.provider
167    assert compare.compare_album(album_a, album_b) is True
168    # test on external id match
169    album_b.name = "Album B"
170    album_b.item_id = "2"
171    album_b.provider = "test2"
172    album_a.external_ids = {(ExternalID.MB_ALBUM, "123")}
173    album_b.external_ids = album_a.external_ids
174    assert compare.compare_album(album_a, album_b) is True
175    # test on external id mismatch
176    album_b.name = album_a.name
177    album_b.external_ids = {(ExternalID.MB_ALBUM, "1234")}
178    assert compare.compare_album(album_a, album_b) is False
179    album_a.external_ids = set()
180    album_b.external_ids = set()
181    # fail on year mismatch
182    album_b.external_ids = set()
183    album_a.year = 2021
184    album_b.year = 2020
185    assert compare.compare_album(album_a, album_b) is False
186    # pass on year match
187    album_b.year = 2021
188    assert compare.compare_album(album_a, album_b) is True
189    # fail on artist mismatch
190    album_a.artists = media_items.UniqueList(
191        [media_items.ItemMapping(item_id="1", provider="test1", name="Artist A")]
192    )
193    album_b.artists = media_items.UniqueList(
194        [media_items.ItemMapping(item_id="2", provider="test1", name="Artist B")]
195    )
196    assert compare.compare_album(album_a, album_b) is False
197    # pass on partial artist match (if first artist matches)
198    album_a.artists = media_items.UniqueList(
199        [media_items.ItemMapping(item_id="1", provider="test1", name="Artist A")]
200    )
201    album_b.artists = media_items.UniqueList(
202        [
203            media_items.ItemMapping(item_id="1", provider="test1", name="Artist A"),
204            media_items.ItemMapping(item_id="2", provider="test1", name="Artist B"),
205        ]
206    )
207    assert compare.compare_album(album_a, album_b) is True
208    # fail on partial artist match in strict mode
209    album_b.artists = media_items.UniqueList(
210        [
211            media_items.ItemMapping(item_id="2", provider="test1", name="Artist B"),
212            media_items.ItemMapping(item_id="1", provider="test1", name="Artist A"),
213        ]
214    )
215    assert compare.compare_album(album_a, album_b) is False
216    # partial artist match is allowed in non-strict mode
217    assert compare.compare_album(album_a, album_b, False) is True
218
219
220def test_compare_track() -> None:  # noqa: PLR0915
221    """Test track comparison."""
222    track_a = media_items.Track(
223        item_id="1",
224        provider="test1",
225        name="Track A",
226        provider_mappings={
227            media_items.ProviderMapping(
228                item_id="1", provider_domain="test", provider_instance="test1"
229            )
230        },
231        artists=media_items.UniqueList(
232            [
233                media_items.Artist(
234                    item_id="1",
235                    provider="test1",
236                    name="Artist A",
237                    provider_mappings={
238                        media_items.ProviderMapping(
239                            item_id="1", provider_domain="test", provider_instance="test1"
240                        )
241                    },
242                )
243            ]
244        ),
245    )
246    track_b = media_items.Track(
247        item_id="1",
248        provider="test2",
249        name="Track A",
250        provider_mappings={
251            media_items.ProviderMapping(
252                item_id="2", provider_domain="test", provider_instance="test2"
253            )
254        },
255        artists=media_items.UniqueList(
256            [
257                media_items.Artist(
258                    item_id="1",
259                    provider="test1",
260                    name="Artist A",
261                    provider_mappings={
262                        media_items.ProviderMapping(
263                            item_id="1", provider_domain="test", provider_instance="test1"
264                        )
265                    },
266                )
267            ]
268        ),
269    )
270    # test match on name match
271    assert compare.compare_track(track_a, track_b) is True
272    # test match on name mismatch
273    track_b.name = "Track B"
274    assert compare.compare_track(track_a, track_b) is False
275    # test on version mismatch
276    track_b.name = track_a.name
277    track_b.version = "Deluxe"
278    assert compare.compare_track(track_a, track_b) is False
279    track_b.version = "Remix"
280    assert compare.compare_track(track_a, track_b) is False
281    # test on version mismatch
282    track_b.name = track_a.name
283    track_a.version = ""
284    track_b.version = "Remaster"
285    assert compare.compare_track(track_a, track_b) is False
286    track_b.version = "Remix"
287    assert compare.compare_track(track_a, track_b) is False
288    # test on version match
289    track_b.name = track_a.name
290    track_a.version = "Deluxe"
291    track_b.version = "Deluxe Edition"
292    assert compare.compare_track(track_a, track_b) is True
293    # test on exact item_id match
294    track_b.item_id = track_a.item_id
295    track_b.provider = track_a.provider
296    assert compare.compare_track(track_a, track_b) is True
297    # test on external id match
298    track_b.name = "Track B"
299    track_b.item_id = "2"
300    track_b.provider = "test2"
301    track_a.external_ids = {(ExternalID.MB_RECORDING, "123")}
302    track_b.external_ids = track_a.external_ids
303    assert compare.compare_track(track_a, track_b) is True
304    # test on external id mismatch
305    track_b.name = track_a.name
306    track_b.external_ids = {(ExternalID.MB_RECORDING, "1234")}
307    assert compare.compare_track(track_a, track_b) is False
308    track_a.external_ids = set()
309    track_b.external_ids = set()
310    # fail on artist mismatch
311    track_a.artists = media_items.UniqueList(
312        [media_items.ItemMapping(item_id="1", provider="test1", name="Artist A")]
313    )
314    track_b.artists = media_items.UniqueList(
315        [media_items.ItemMapping(item_id="2", provider="test1", name="Artist B")]
316    )
317    assert compare.compare_track(track_a, track_b) is False
318    # pass on partial artist match (if first artist matches)
319    track_a.artists = media_items.UniqueList(
320        [media_items.ItemMapping(item_id="1", provider="test1", name="Artist A")]
321    )
322    track_b.artists = media_items.UniqueList(
323        [
324            media_items.ItemMapping(item_id="1", provider="test1", name="Artist A"),
325            media_items.ItemMapping(item_id="2", provider="test1", name="Artist B"),
326        ]
327    )
328    assert compare.compare_track(track_a, track_b) is True
329    # fail on partial artist match in strict mode
330    track_b.artists = media_items.UniqueList(
331        [
332            media_items.ItemMapping(item_id="2", provider="test1", name="Artist B"),
333            media_items.ItemMapping(item_id="1", provider="test1", name="Artist A"),
334        ]
335    )
336    assert compare.compare_track(track_a, track_b) is False
337    # partial artist match is allowed in non-strict mode
338    assert compare.compare_track(track_a, track_b, False) is True
339    track_b.artists = track_a.artists
340    # fail on album mismatch
341    track_a.album = media_items.ItemMapping(item_id="1", provider="test1", name="Album A")
342    track_b.album = media_items.ItemMapping(item_id="2", provider="test1", name="Album B")
343    assert compare.compare_track(track_a, track_b) is False
344    # pass on exact album(track) match (regardless duration)
345    track_b.album = track_a.album
346    track_a.disc_number = 1
347    track_a.track_number = 1
348    track_b.disc_number = track_a.disc_number
349    track_b.track_number = track_a.track_number
350    track_a.duration = 300
351    track_b.duration = 310
352    assert compare.compare_track(track_a, track_b) is True
353    # pass on album(track) mismatch
354    track_b.album = track_a.album
355    track_a.disc_number = 1
356    track_a.track_number = 1
357    track_b.disc_number = track_a.disc_number
358    track_b.track_number = 2
359    track_b.duration = track_a.duration
360    assert compare.compare_track(track_a, track_b) is False
361    # test special case - ISRC match but MusicBrainz ID mismatch
362    # this can happen for some classical music albums
363    track_a.external_ids = {
364        (ExternalID.ISRC, "123"),
365        (ExternalID.MB_RECORDING, "abc"),
366    }
367    track_b.external_ids = {
368        (ExternalID.ISRC, "123"),
369        (ExternalID.MB_RECORDING, "abcd"),
370    }
371    assert compare.compare_track(track_a, track_b) is False
372
373
374def test_compare_strings_case_insensitive_fuzzy() -> None:
375    """Test that non-strict fuzzy matching is fully case-insensitive."""
376    # These differ slightly ("Feat." vs "FT.") so create_safe_string won't match,
377    # falling through to SequenceMatcher which must compare both strings lowered.
378    assert compare.compare_strings("Track Feat. John", "TRACK FT. JOHN", strict=False) is True
379