Newer
Older
import datetime
import pytest
import pytz
from django.templatetags.static import static
Eliot Berriot
committed
from funkwhale_api.audio import serializers
from funkwhale_api.common import serializers as common_serializers
from funkwhale_api.common import utils as common_utils
Eliot Berriot
committed
from funkwhale_api.federation import serializers as federation_serializers
from funkwhale_api.federation import utils as federation_utils
Eliot Berriot
committed
from funkwhale_api.music import serializers as music_serializers
def test_channel_serializer_create(factories, mocker):
Eliot Berriot
committed
attributed_to = factories["federation.Actor"](local=True)
attachment = factories["common.Attachment"](actor=attributed_to)
request = mocker.Mock(user=mocker.Mock(actor=attributed_to))
Eliot Berriot
committed
data = {
"name": "My channel",
"username": "mychannel",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
Eliot Berriot
committed
"tags": ["hello", "world"],
"cover": attachment.uuid,
Eliot Berriot
committed
}
serializer = serializers.ChannelCreateSerializer(
data=data, context={"actor": attributed_to, "request": request}
Eliot Berriot
committed
assert serializer.is_valid(raise_exception=True) is True
channel = serializer.save(attributed_to=attributed_to)
assert channel.artist.name == data["name"]
assert channel.artist.attributed_to == attributed_to
assert (
sorted(channel.artist.tagged_items.values_list("tag__name", flat=True))
== data["tags"]
)
assert channel.artist.description.text == data["description"]["text"]
assert channel.artist.attachment_cover == attachment
assert channel.artist.content_category == data["content_category"]
assert (
channel.artist.description.content_type == data["description"]["content_type"]
)
Eliot Berriot
committed
assert channel.attributed_to == attributed_to
assert channel.actor.preferred_username == data["username"]
assert channel.actor.name == data["name"]
assert channel.library.privacy_level == "everyone"
Eliot Berriot
committed
assert channel.library.actor == attributed_to
def test_channel_serializer_create_honor_max_channels_setting(factories, preferences):
preferences["audio__max_channels"] = 1
attributed_to = factories["federation.Actor"](local=True)
factories["audio.Channel"](attributed_to=attributed_to)
data = {
"name": "My channel",
"username": "mychannel",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
"tags": ["hello", "world"],
"content_category": "other",
}
serializer = serializers.ChannelCreateSerializer(
data=data, context={"actor": attributed_to}
)
with pytest.raises(serializers.serializers.ValidationError, match=r".*max.*"):
assert serializer.is_valid(raise_exception=True)
Eliot Berriot
committed
def test_channel_serializer_create_validates_username_uniqueness(factories):
attributed_to = factories["federation.Actor"](local=True)
data = {
"name": "My channel",
"username": attributed_to.preferred_username.upper(),
"description": {"text": "This is my channel", "content_type": "text/markdown"},
"tags": ["hello", "world"],
"content_category": "other",
}
serializer = serializers.ChannelCreateSerializer(
data=data, context={"actor": attributed_to}
)
Eliot Berriot
committed
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
with pytest.raises(
serializers.serializers.ValidationError, match=r".*username is already taken.*"
):
assert serializer.is_valid(raise_exception=True)
def test_channel_serializer_create_validates_username_chars(factories):
attributed_to = factories["federation.Actor"](local=True)
data = {
"name": "My channel",
"username": "hello world",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
"tags": ["hello", "world"],
"content_category": "other",
}
serializer = serializers.ChannelCreateSerializer(
data=data, context={"actor": attributed_to}
)
with pytest.raises(
serializers.serializers.ValidationError, match=r".*Enter a valid username.*"
):
assert serializer.is_valid(raise_exception=True)
def test_channel_serializer_create_validates_blacklisted_username(factories, settings):
settings.ACCOUNT_USERNAME_BLACKLIST = ["forBidden"]
attributed_to = factories["federation.Actor"](local=True)
data = {
"name": "My channel",
"username": "FORBIDDEN",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
"tags": ["hello", "world"],
"content_category": "other",
}
serializer = serializers.ChannelCreateSerializer(
data=data, context={"actor": attributed_to}
)
with pytest.raises(
serializers.serializers.ValidationError, match=r".*username is already taken.*"
):
assert serializer.is_valid(raise_exception=True)
def test_channel_serializer_create_podcast(factories):
attributed_to = factories["federation.Actor"](local=True)
data = {
# TODO: cover
"name": "My channel",
"username": "mychannel",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
"tags": ["hello", "world"],
"content_category": "podcast",
"metadata": {"itunes_category": "Sports", "language": "en"},
}
serializer = serializers.ChannelCreateSerializer(
data=data, context={"actor": attributed_to}
)
assert serializer.is_valid(raise_exception=True) is True
channel = serializer.save(attributed_to=attributed_to)
assert channel.metadata == data["metadata"]
def test_channel_serializer_update(factories, mocker):
channel = factories["audio.Channel"](
artist__set_tags=["rock"], attributed_to__local=True
)
attributed_to = channel.attributed_to
attachment = factories["common.Attachment"](actor=attributed_to)
request = mocker.Mock(user=mocker.Mock(actor=attributed_to))
Eliot Berriot
committed
data = {
"name": "My channel",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
Eliot Berriot
committed
"tags": ["hello", "world"],
"cover": attachment.uuid,
Eliot Berriot
committed
}
serializer = serializers.ChannelUpdateSerializer(
channel, data=data, context={"request": request}
)
Eliot Berriot
committed
assert serializer.is_valid(raise_exception=True) is True
serializer.save()
channel.refresh_from_db()
assert channel.artist.name == data["name"]
assert channel.artist.attachment_cover == attachment
assert channel.artist.content_category == data["content_category"]
Eliot Berriot
committed
assert (
sorted(channel.artist.tagged_items.values_list("tag__name", flat=True))
== data["tags"]
)
assert channel.actor.summary == common_utils.render_html(
data["description"]["text"], "text/markdown"
)
assert channel.artist.description.text == data["description"]["text"]
assert channel.artist.description.content_type == "text/markdown"
Eliot Berriot
committed
assert channel.actor.name == data["name"]
def test_channel_serializer_update_podcast(factories):
channel = factories["audio.Channel"](artist__set_tags=["rock"])
data = {
# TODO: cover
"name": "My channel",
"description": {"text": "This is my channel", "content_type": "text/markdown"},
"tags": ["hello", "world"],
"content_category": "podcast",
"metadata": {"language": "en", "itunes_category": "Sports"},
}
serializer = serializers.ChannelUpdateSerializer(channel, data=data)
assert serializer.is_valid(raise_exception=True) is True
serializer.save()
channel.refresh_from_db()
assert channel.metadata == data["metadata"]
Eliot Berriot
committed
def test_channel_serializer_representation(factories, to_api_date):
content = factories["common.Content"]()
channel = factories["audio.Channel"](artist__description=content)
Eliot Berriot
committed
expected = {
"artist": music_serializers.serialize_artist_simple(channel.artist),
"uuid": str(channel.uuid),
"creation_date": to_api_date(channel.creation_date),
"actor": federation_serializers.APIActorSerializer(channel.actor).data,
"attributed_to": federation_serializers.APIActorSerializer(
channel.attributed_to
).data,
"rss_url": channel.get_rss_url(),
Eliot Berriot
committed
}
expected["artist"]["description"] = common_serializers.ContentSerializer(
content
).data
Eliot Berriot
committed
assert serializers.ChannelSerializer(channel).data == expected
def test_channel_serializer_representation_subscriptions_count(factories, to_api_date):
channel = factories["audio.Channel"]()
factories["federation.Follow"](target=channel.actor)
factories["federation.Follow"](target=channel.actor, approved=False)
serializer = serializers.ChannelSerializer(
channel, context={"subscriptions_count": True}
)
assert serializer.data["subscriptions_count"] == 1
def test_subscription_serializer(factories, to_api_date):
subscription = factories["audio.Subscription"]()
expected = {
"channel": serializers.ChannelSerializer(subscription.target.channel).data,
"uuid": str(subscription.uuid),
"creation_date": to_api_date(subscription.creation_date),
"approved": subscription.approved,
"fid": subscription.fid,
}
assert serializers.SubscriptionSerializer(subscription).data == expected
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
def test_rss_item_serializer(factories):
description = factories["common.Content"]()
upload = factories["music.Upload"](
playable=True,
track__set_tags=["pop", "rock"],
track__description=description,
track__disc_number=4,
track__position=42,
)
setattr(
upload.track,
"_prefetched_tagged_items",
upload.track.tagged_items.order_by("tag__name"),
)
expected = {
"title": [{"value": upload.track.title}],
"itunes:title": [{"value": upload.track.title}],
"itunes:subtitle": [{"value": description.truncate(255)}],
"itunes:summary": [{"cdata_value": description.rendered}],
"description": [{"value": description.as_plain_text}],
"guid": [{"cdata_value": str(upload.uuid), "isPermalink": "false"}],
"pubDate": [{"value": serializers.rss_date(upload.creation_date)}],
"itunes:duration": [{"value": serializers.rss_duration(upload.duration)}],
"itunes:keywords": [{"value": "pop rock"}],
"itunes:explicit": [{"value": "no"}],
"itunes:episodeType": [{"value": "full"}],
"itunes:season": [{"value": upload.track.disc_number}],
"itunes:episode": [{"value": upload.track.position}],
"itunes:image": [{"href": upload.track.attachment_cover.download_url_original}],
"link": [{"value": federation_utils.full_url(upload.track.get_absolute_url())}],
"enclosure": [
{
"url": federation_utils.full_url(upload.get_listen_url("mp3")),
"length": upload.size,
"type": "audio/mpeg",
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
],
}
assert serializers.rss_serialize_item(upload) == expected
def test_rss_channel_serializer(factories):
metadata = {
"language": "fr",
"itunes_category": "Parent",
"itunes_subcategory": "Child",
"copyright": "Myself",
"owner_name": "Name",
"owner_email": "name@domain.com",
"explicit": True,
}
description = factories["common.Content"]()
channel = factories["audio.Channel"](
artist__set_tags=["pop", "rock"],
artist__description=description,
metadata=metadata,
)
setattr(
channel.artist,
"_prefetched_tagged_items",
channel.artist.tagged_items.order_by("tag__name"),
)
expected = {
"title": [{"value": channel.artist.name}],
"language": [{"value": metadata["language"]}],
"copyright": [{"value": metadata["copyright"]}],
"itunes:subtitle": [{"value": description.truncate(255)}],
"itunes:summary": [{"cdata_value": description.rendered}],
"description": [{"value": description.as_plain_text}],
"itunes:keywords": [{"value": "pop rock"}],
"itunes:category": [
{
"text": metadata["itunes_category"],
"itunes:category": [{"text": metadata["itunes_subcategory"]}],
}
],
"itunes:explicit": [{"value": "yes"}],
"itunes:owner": [
{
"itunes:name": [{"value": metadata["owner_name"]}],
"itunes:email": [{"value": metadata["owner_email"]}],
}
],
"itunes:author": [{"value": metadata["owner_name"]}],
"itunes:type": [{"value": "episodic"}],
"itunes:image": [
{"href": channel.artist.attachment_cover.download_url_original}
],
"link": [{"value": channel.get_absolute_url()}],
"atom:link": [
{
"href": channel.get_rss_url(),
"rel": "self",
"type": "application/rss+xml",
}
],
}
assert serializers.rss_serialize_channel(channel) == expected
def test_rss_channel_serializer_placeholder_image(factories):
description = factories["common.Content"]()
channel = factories["audio.Channel"](
artist__set_tags=["pop", "rock"],
artist__description=description,
artist__attachment_cover=None,
)
setattr(
channel.artist,
"_prefetched_tagged_items",
channel.artist.tagged_items.order_by("tag__name"),
)
expected = [
{
"href": federation_utils.full_url(
static("images/podcasts-cover-placeholder.png")
)
}
]
assert serializers.rss_serialize_channel(channel)["itunes:image"] == expected
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
def test_serialize_full_channel(factories):
channel = factories["audio.Channel"]()
upload1 = factories["music.Upload"](playable=True)
upload2 = factories["music.Upload"](playable=True)
expected = serializers.rss_serialize_channel(channel)
expected["item"] = [
serializers.rss_serialize_item(upload1),
serializers.rss_serialize_item(upload2),
]
expected = {"channel": expected}
result = serializers.rss_serialize_channel_full(
channel=channel, uploads=[upload1, upload2]
)
assert result == expected
@pytest.mark.parametrize(
"seconds, expected",
[
(0, "00:00:00"),
(None, "00:00:00"),
(61, "00:01:01"),
(3601, "01:00:01"),
(7345, "02:02:25"),
],
)
def test_rss_duration(seconds, expected):
assert serializers.rss_duration(seconds) == expected
@pytest.mark.parametrize(
"dt, expected",
[
(
datetime.datetime(2020, 1, 30, 6, 0, 49, tzinfo=pytz.UTC),
"Thu, 30 Jan 2020 06:00:49 +0000",
),
],
)
def test_rss_date(dt, expected):
assert serializers.rss_date(dt) == expected
def test_channel_metadata_serializer_validation():
payload = {
"language": "fr",
"copyright": "Me",
"owner_email": "contact@me.com",
"owner_name": "Me",
"itunes_category": "Health & Fitness",
"itunes_subcategory": "Sexuality",
"unknown_key": "noop",
}
serializer = serializers.ChannelMetadataSerializer(data=payload)
assert serializer.is_valid(raise_exception=True) is True
payload.pop("unknown_key")
assert serializer.validated_data == payload