funkwhale/api/tests/subsonic/test_serializers.py

377 wiersze
12 KiB
Python

import datetime
import pytest
from django.db.models.aggregates import Count
from funkwhale_api.music import models as music_models
from funkwhale_api.subsonic import renderers, serializers
@pytest.mark.parametrize(
"date, expected",
[
(datetime.datetime(2017, 1, 12, 9, 53, 12, 1890), "2017-01-12T09:53:12.000Z"),
(None, None),
],
)
def test_to_subsonic_date(date, expected):
assert serializers.to_subsonic_date(date) == expected
@pytest.mark.parametrize(
"input, expected",
[
("AC/DC", "AC_DC"),
("AC-DC", "AC-DC"),
("A" * 100, "A" * 50),
("Album (2019)", "Album (2019)"),
("Haven't", "Haven_t"),
],
)
def test_get_valid_filepart(input, expected):
assert serializers.get_valid_filepart(input) == expected
@pytest.mark.parametrize(
"factory_kwargs, suffix, expected",
[
(
{
"artist__name": "Hello",
"album__title": "World",
"title": "foo",
"position": None,
},
"mp3",
"Hello/World/foo.mp3",
),
(
{
"artist__name": "AC/DC",
"album__title": "escape/my",
"title": "sla/sh",
"position": 12,
},
"ogg",
"/".join(
[
serializers.get_valid_filepart("AC/DC"),
serializers.get_valid_filepart("escape/my"),
]
)
+ "/12 - {}.ogg".format(serializers.get_valid_filepart("sla/sh")),
),
],
)
def test_get_track_path(factory_kwargs, suffix, expected, factories):
track = factories["music.Track"](**factory_kwargs)
assert serializers.get_track_path(track, suffix) == expected
def test_get_artists_serializer(factories):
artist1 = factories["music.Artist"](name="eliot", with_cover=True)
artist2 = factories["music.Artist"](name="Ellena", with_cover=True)
artist3 = factories["music.Artist"](name="Rilay", with_cover=True)
artist4 = factories["music.Artist"](
name="", with_cover=False
) # Shouldn't be serialised
factories["music.Album"].create_batch(size=3, artist=artist1)
factories["music.Album"].create_batch(size=2, artist=artist2)
expected = {
"ignoredArticles": "",
"index": [
{
"name": "E",
"artist": [
{
"id": artist1.pk,
"name": artist1.name,
"albumCount": 3,
"coverArt": f"ar-{artist1.id}",
"musicBrainzId": artist1.mbid,
},
{
"id": artist2.pk,
"name": artist2.name,
"albumCount": 2,
"coverArt": f"ar-{artist2.id}",
"musicBrainzId": artist2.mbid,
},
],
},
{
"name": "R",
"artist": [
{
"id": artist3.pk,
"name": artist3.name,
"albumCount": 0,
"coverArt": f"ar-{artist3.id}",
"musicBrainzId": artist3.mbid,
}
],
},
],
}
queryset = artist1.__class__.objects.filter(
pk__in=[artist1.pk, artist2.pk, artist3.pk, artist4.pk]
)
assert serializers.GetArtistsSerializer(queryset).data == expected
def test_get_artist_serializer(factories):
artist = factories["music.Artist"](with_cover=True)
album = factories["music.Album"](artist=artist, with_cover=True)
tracks = factories["music.Track"].create_batch(size=3, album=album)
expected = {
"id": artist.pk,
"name": artist.name,
"albumCount": 1,
"coverArt": f"ar-{artist.id}",
"album": [
{
"id": album.pk,
"coverArt": f"al-{album.id}",
"artistId": artist.pk,
"name": album.title,
"artist": artist.name,
"songCount": len(tracks),
"created": serializers.to_subsonic_date(album.creation_date),
"year": album.release_date.year,
}
],
}
assert serializers.GetArtistSerializer(artist).data == expected
def test_get_artist_info_2_serializer(factories):
content = factories["common.Content"]()
artist = factories["music.Artist"](with_cover=True, description=content)
expected = {
"musicBrainzId": artist.mbid,
"mediumImageUrl": renderers.TagValue(
artist.attachment_cover.download_url_medium_square_crop
),
"largeImageUrl": renderers.TagValue(
artist.attachment_cover.download_url_large_square_crop
),
"biography": renderers.TagValue(artist.description.rendered),
}
assert serializers.GetArtistInfo2Serializer(artist).data == expected
@pytest.mark.parametrize(
"mimetype, extension, expected",
[
("audio/ogg", "noop", "audio/ogg"),
("", "ogg", "audio/ogg"),
("", "mp3", "audio/mpeg"),
("", "", "audio/mpeg"),
],
)
def test_get_track_data_content_type(mimetype, extension, expected, factories):
upload = factories["music.Upload"]()
upload.mimetype = mimetype
upload.audio_file = f"test.{extension}"
data = serializers.get_track_data(
album=upload.track.album, track=upload.track, upload=upload
)
assert data["contentType"] == expected
def test_get_album_serializer(factories):
artist = factories["music.Artist"]()
album = factories["music.Album"](artist=artist, with_cover=True)
track = factories["music.Track"](album=album, disc_number=42)
upload = factories["music.Upload"](track=track, bitrate=42000, duration=43, size=44)
tagged_item = factories["tags.TaggedItem"](content_object=album, tag__name="foo")
expected = {
"id": album.pk,
"artistId": artist.pk,
"name": album.title,
"artist": artist.name,
"songCount": 1,
"created": serializers.to_subsonic_date(album.creation_date),
"year": album.release_date.year,
"coverArt": f"al-{album.id}",
"genre": tagged_item.tag.name,
"genres": [{"name": tagged_item.tag.name}],
"mediaType": "album",
"musicBrainzId": album.mbid,
"duration": 43,
"playCount": album.tracks.aggregate(l=Count("listenings"))["l"] or 0,
"song": [
{
"id": track.pk,
"isDir": "false",
"title": track.title,
"coverArt": f"al-{album.id}",
"album": album.title,
"artist": artist.name,
"track": track.position,
"discNumber": track.disc_number,
"year": track.album.release_date.year,
"contentType": upload.mimetype,
"suffix": upload.extension or "",
"path": serializers.get_track_path(track, upload.extension),
"bitRate": 42,
"duration": 43,
"size": 44,
"created": serializers.to_subsonic_date(track.creation_date),
"albumId": album.pk,
"artistId": artist.pk,
"type": "music",
"mediaType": "song",
"musicBrainzId": track.mbid,
}
],
}
qs = album.__class__.objects.with_duration()
assert serializers.GetAlbumSerializer(qs.first()).data == expected
def test_starred_tracks2_serializer(factories):
artist = factories["music.Artist"]()
album = factories["music.Album"](artist=artist)
track = factories["music.Track"](album=album)
upload = factories["music.Upload"](track=track)
favorite = factories["favorites.TrackFavorite"](track=track)
expected = [serializers.get_track_data(album, track, upload)]
expected[0]["starred"] = serializers.to_subsonic_date(favorite.creation_date)
data = serializers.get_starred_tracks_data([favorite])
assert data == expected
def test_get_album_list2_serializer(factories):
album1 = factories["music.Album"]().__class__.objects.with_duration().first()
album2 = factories["music.Album"]().__class__.objects.with_duration().last()
qs = music_models.Album.objects.with_tracks_count().with_duration().order_by("pk")
expected = [
serializers.get_album2_data(album1),
serializers.get_album2_data(album2),
]
data = serializers.get_album_list2_data(qs)
assert data == expected
def test_playlist_serializer(factories):
plt = factories["playlists.PlaylistTrack"]()
playlist = plt.playlist
qs = music_models.Album.objects.with_tracks_count().order_by("pk")
expected = {
"id": playlist.pk,
"name": playlist.name,
"owner": playlist.user.username,
"public": "false",
"songCount": 1,
"duration": 0,
"created": serializers.to_subsonic_date(playlist.creation_date),
}
qs = playlist.__class__.objects.with_tracks_count()
data = serializers.get_playlist_data(qs.first())
assert data == expected
def test_playlist_detail_serializer(factories):
plt = factories["playlists.PlaylistTrack"]()
upload = factories["music.Upload"](track=plt.track)
playlist = plt.playlist
qs = music_models.Album.objects.with_tracks_count().order_by("pk")
expected = {
"id": playlist.pk,
"name": playlist.name,
"owner": playlist.user.username,
"public": "false",
"songCount": 1,
"duration": 0,
"created": serializers.to_subsonic_date(playlist.creation_date),
"entry": [serializers.get_track_data(plt.track.album, plt.track, upload)],
}
qs = playlist.__class__.objects.with_tracks_count()
data = serializers.get_playlist_detail_data(qs.first())
assert data == expected
def test_scrobble_serializer(factories):
upload = factories["music.Upload"]()
track = upload.track
user = factories["users.User"](with_actor=True)
payload = {"id": track.pk, "submission": True}
serializer = serializers.ScrobbleSerializer(data=payload, context={"user": user})
assert serializer.is_valid(raise_exception=True)
listening = serializer.save()
assert listening.actor.user == user
assert listening.track == track
def test_channel_serializer(factories):
description = factories["common.Content"]()
channel = factories["audio.Channel"](
external=True, artist__description=description, artist__with_cover=True
)
upload = factories["music.Upload"](
playable=True, library=channel.library, duration=42
)
expected = {
"id": str(channel.uuid),
"url": channel.rss_url,
"title": channel.artist.name,
"description": description.as_plain_text,
"coverArt": f"at-{channel.artist.attachment_cover.uuid}",
"originalImageUrl": channel.artist.attachment_cover.url,
"status": "completed",
"episode": [serializers.get_channel_episode_data(upload, channel.uuid)],
}
data = serializers.get_channel_data(channel, [upload])
assert data == expected
def test_channel_episode_serializer(factories):
description = factories["common.Content"]()
channel = factories["audio.Channel"]()
track = factories["music.Track"](
description=description, artist=channel.artist, with_cover=True
)
upload = factories["music.Upload"](
playable=True, track=track, bitrate=128000, duration=42
)
expected = {
"id": str(upload.uuid),
"channelId": str(channel.uuid),
"streamId": upload.track.id,
"title": track.title,
"description": description.as_plain_text,
"coverArt": f"at-{track.attachment_cover.uuid}",
"isDir": "false",
"year": track.creation_date.year,
"created": track.creation_date.isoformat(),
"publishDate": track.creation_date.isoformat(),
"genre": "Podcast",
"size": upload.size,
"duration": upload.duration,
"bitRate": upload.bitrate / 1000,
"contentType": upload.mimetype,
"suffix": upload.extension,
"status": "completed",
}
data = serializers.get_channel_episode_data(upload, channel.uuid)
assert data == expected