2064 lines
73 KiB
Python
2064 lines
73 KiB
Python
import io
|
|
import uuid
|
|
|
|
import pytest
|
|
from django.core.paginator import Paginator
|
|
from django.urls import reverse
|
|
from django.utils import timezone
|
|
|
|
from funkwhale_api.common import utils as common_utils
|
|
from funkwhale_api.federation import (
|
|
actors,
|
|
contexts,
|
|
jsonld,
|
|
keys,
|
|
models,
|
|
serializers,
|
|
utils,
|
|
)
|
|
from funkwhale_api.moderation import serializers as moderation_serializers
|
|
from funkwhale_api.music import licenses
|
|
|
|
|
|
def test_actor_serializer_from_ap(db):
|
|
private, public = keys.get_key_pair()
|
|
actor_url = "https://test.federation/actor"
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": actor_url,
|
|
"type": "Person",
|
|
"outbox": "https://test.com/outbox",
|
|
"inbox": "https://test.com/inbox",
|
|
"following": "https://test.com/following",
|
|
"followers": "https://test.com/followers",
|
|
"preferredUsername": "test",
|
|
"name": "Test",
|
|
"summary": "Hello world",
|
|
"manuallyApprovesFollowers": True,
|
|
"url": "http://hello.world/path",
|
|
"publicKey": {
|
|
"publicKeyPem": public.decode("utf-8"),
|
|
"owner": actor_url,
|
|
"id": actor_url + "#main-key",
|
|
},
|
|
"endpoints": {"sharedInbox": "https://noop.url/federation/shared/inbox"},
|
|
"icon": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": "https://image.example/image.png",
|
|
},
|
|
}
|
|
|
|
serializer = serializers.ActorSerializer(data=payload)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
actor = serializer.save()
|
|
|
|
assert actor.fid == actor_url
|
|
assert actor.url == payload["url"]
|
|
assert actor.inbox_url == payload["inbox"]
|
|
assert actor.shared_inbox_url == payload["endpoints"]["sharedInbox"]
|
|
assert actor.outbox_url == payload["outbox"]
|
|
assert actor.following_url == payload["following"]
|
|
assert actor.followers_url == payload["followers"]
|
|
assert actor.followers_url == payload["followers"]
|
|
assert actor.type == "Person"
|
|
assert actor.preferred_username == payload["preferredUsername"]
|
|
assert actor.name == payload["name"]
|
|
assert actor.summary_obj.text == payload["summary"]
|
|
assert actor.summary_obj.content_type == "text/html"
|
|
assert actor.fid == actor_url
|
|
assert actor.manually_approves_followers is True
|
|
assert actor.private_key is None
|
|
assert actor.public_key == payload["publicKey"]["publicKeyPem"]
|
|
assert actor.domain_id == "test.federation"
|
|
assert actor.attachment_icon.url == payload["icon"]["url"]
|
|
assert actor.attachment_icon.mimetype == payload["icon"]["mediaType"]
|
|
|
|
|
|
def test_actor_serializer_from_ap_no_icon_mediaType(db):
|
|
private, public = keys.get_key_pair()
|
|
actor_url = "https://test.federation/actor"
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": actor_url,
|
|
"type": "Person",
|
|
"inbox": "https://test.com/inbox",
|
|
"following": "https://test.com/following",
|
|
"followers": "https://test.com/followers",
|
|
"preferredUsername": "test",
|
|
"manuallyApprovesFollowers": True,
|
|
"url": "http://hello.world/path",
|
|
"publicKey": {
|
|
"publicKeyPem": public.decode("utf-8"),
|
|
"owner": actor_url,
|
|
"id": actor_url + "#main-key",
|
|
},
|
|
"endpoints": {"sharedInbox": "https://noop.url/federation/shared/inbox"},
|
|
"icon": {"type": "Image", "url": "https://image.example/image.png"},
|
|
}
|
|
|
|
serializer = serializers.ActorSerializer(data=payload)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
actor = serializer.save()
|
|
|
|
assert actor.attachment_icon.url == payload["icon"]["url"]
|
|
assert actor.attachment_icon.mimetype is None
|
|
|
|
|
|
def test_actor_serializer_only_mandatory_field_from_ap(db):
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": "https://test.federation/user",
|
|
"type": "Person",
|
|
"following": "https://test.federation/user/following",
|
|
"followers": "https://test.federation/user/followers",
|
|
"inbox": "https://test.federation/user/inbox",
|
|
"outbox": "https://test.federation/user/outbox",
|
|
"preferredUsername": "user",
|
|
}
|
|
|
|
serializer = serializers.ActorSerializer(data=payload)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
actor = serializer.build()
|
|
|
|
assert actor.fid == payload["id"]
|
|
assert actor.inbox_url == payload["inbox"]
|
|
assert actor.outbox_url == payload["outbox"]
|
|
assert actor.followers_url == payload["followers"]
|
|
assert actor.following_url == payload["following"]
|
|
assert actor.preferred_username == payload["preferredUsername"]
|
|
assert actor.domain.pk == "test.federation"
|
|
assert actor.type == "Person"
|
|
assert actor.manually_approves_followers is None
|
|
|
|
|
|
def test_actor_serializer_to_ap(factories):
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": "https://test.federation/user",
|
|
"type": "Person",
|
|
"following": "https://test.federation/user/following",
|
|
"followers": "https://test.federation/user/followers",
|
|
"inbox": "https://test.federation/user/inbox",
|
|
"outbox": "https://test.federation/user/outbox",
|
|
"preferredUsername": "user",
|
|
"name": "Real User",
|
|
"url": [{"type": "Link", "href": "https://test.url", "mediaType": "text/html"}],
|
|
"manuallyApprovesFollowers": False,
|
|
"publicKey": {
|
|
"id": "https://test.federation/user#main-key",
|
|
"owner": "https://test.federation/user",
|
|
"publicKeyPem": "yolo",
|
|
},
|
|
"endpoints": {"sharedInbox": "https://test.federation/inbox"},
|
|
}
|
|
ac = models.Actor.objects.create(
|
|
fid=expected["id"],
|
|
inbox_url=expected["inbox"],
|
|
outbox_url=expected["outbox"],
|
|
url=expected["url"][0]["href"],
|
|
shared_inbox_url=expected["endpoints"]["sharedInbox"],
|
|
followers_url=expected["followers"],
|
|
following_url=expected["following"],
|
|
public_key=expected["publicKey"]["publicKeyPem"],
|
|
preferred_username=expected["preferredUsername"],
|
|
name=expected["name"],
|
|
domain=models.Domain.objects.create(pk="test.domain"),
|
|
type="Person",
|
|
manually_approves_followers=False,
|
|
attachment_icon=factories["common.Attachment"](),
|
|
)
|
|
|
|
content = common_utils.attach_content(
|
|
ac, "summary_obj", {"text": "hello world", "content_type": "text/markdown"}
|
|
)
|
|
expected["summary"] = content.rendered
|
|
expected["icon"] = {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": utils.full_url(ac.attachment_icon.file.url),
|
|
}
|
|
serializer = serializers.ActorSerializer(ac)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_webfinger_serializer():
|
|
expected = {
|
|
"subject": "acct:service@test.federation",
|
|
"links": [
|
|
{
|
|
"rel": "self",
|
|
"href": "https://test.federation/federation/instance/actor",
|
|
"type": "application/activity+json",
|
|
}
|
|
],
|
|
"aliases": ["https://test.federation/federation/instance/actor"],
|
|
}
|
|
actor = models.Actor(
|
|
fid=expected["links"][0]["href"],
|
|
preferred_username="service",
|
|
domain=models.Domain(pk="test.federation"),
|
|
)
|
|
serializer = serializers.ActorWebfingerSerializer(actor)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_follow_serializer_to_ap(factories):
|
|
follow = factories["federation.Follow"](local=True)
|
|
serializer = serializers.FollowSerializer(follow)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id(),
|
|
"type": "Follow",
|
|
"actor": follow.actor.fid,
|
|
"object": follow.target.fid,
|
|
}
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_follow_serializer_save(factories):
|
|
actor = factories["federation.Actor"]()
|
|
target = factories["federation.Actor"]()
|
|
|
|
data = {
|
|
"id": "https://test.follow",
|
|
"type": "Follow",
|
|
"actor": actor.fid,
|
|
"object": target.fid,
|
|
}
|
|
serializer = serializers.FollowSerializer(data=data)
|
|
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
follow = serializer.save()
|
|
|
|
assert follow.pk is not None
|
|
assert follow.actor == actor
|
|
assert follow.target == target
|
|
assert follow.approved is None
|
|
|
|
|
|
def test_follow_serializer_save_validates_on_context(factories):
|
|
actor = factories["federation.Actor"]()
|
|
target = factories["federation.Actor"]()
|
|
impostor = factories["federation.Actor"]()
|
|
|
|
data = {
|
|
"id": "https://test.follow",
|
|
"type": "Follow",
|
|
"actor": actor.fid,
|
|
"object": target.fid,
|
|
}
|
|
serializer = serializers.FollowSerializer(
|
|
data=data, context={"follow_actor": impostor, "follow_target": impostor}
|
|
)
|
|
|
|
assert serializer.is_valid() is False
|
|
|
|
assert "actor" in serializer.errors
|
|
assert "object" in serializer.errors
|
|
|
|
|
|
def test_accept_follow_serializer_representation(factories):
|
|
follow = factories["federation.Follow"](approved=None)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/accept",
|
|
"type": "Accept",
|
|
"actor": follow.target.fid,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.AcceptFollowSerializer(follow)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_accept_follow_serializer_save(factories):
|
|
follow = factories["federation.Follow"](approved=None)
|
|
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/accept",
|
|
"type": "Accept",
|
|
"actor": follow.target.fid,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.AcceptFollowSerializer(data=data)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
serializer.save()
|
|
|
|
follow.refresh_from_db()
|
|
|
|
assert follow.approved is True
|
|
|
|
|
|
def test_accept_follow_serializer_validates_on_context(factories):
|
|
follow = factories["federation.Follow"](approved=None)
|
|
impostor = factories["federation.Actor"]()
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/accept",
|
|
"type": "Accept",
|
|
"actor": impostor.url,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.AcceptFollowSerializer(
|
|
data=data, context={"follow_actor": impostor, "follow_target": impostor}
|
|
)
|
|
|
|
assert serializer.is_valid() is False
|
|
assert "actor" in serializer.errors["object"]
|
|
assert "object" in serializer.errors["object"]
|
|
|
|
|
|
def test_reject_follow_serializer_representation(factories):
|
|
follow = factories["federation.Follow"](approved=None)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/reject",
|
|
"type": "Reject",
|
|
"actor": follow.target.fid,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.RejectFollowSerializer(follow)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_undo_follow_serializer_representation(factories):
|
|
follow = factories["federation.Follow"](approved=True)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/undo",
|
|
"type": "Undo",
|
|
"actor": follow.actor.fid,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.UndoFollowSerializer(follow)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_undo_follow_serializer_save(factories):
|
|
follow = factories["federation.Follow"](approved=True)
|
|
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/undo",
|
|
"type": "Undo",
|
|
"actor": follow.actor.fid,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.UndoFollowSerializer(data=data)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
serializer.save()
|
|
|
|
with pytest.raises(models.Follow.DoesNotExist):
|
|
follow.refresh_from_db()
|
|
|
|
|
|
def test_undo_follow_serializer_validates_on_context(factories):
|
|
follow = factories["federation.Follow"](approved=True)
|
|
impostor = factories["federation.Actor"]()
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": follow.get_federation_id() + "/undo",
|
|
"type": "Undo",
|
|
"actor": impostor.url,
|
|
"object": serializers.FollowSerializer(follow).data,
|
|
}
|
|
|
|
serializer = serializers.UndoFollowSerializer(
|
|
data=data, context={"follow_actor": impostor, "follow_target": impostor}
|
|
)
|
|
|
|
assert serializer.is_valid() is False
|
|
assert "actor" in serializer.errors["object"]
|
|
assert "object" in serializer.errors["object"]
|
|
|
|
|
|
def test_paginated_collection_serializer(factories):
|
|
uploads = factories["music.Upload"].create_batch(size=5)
|
|
actor = factories["federation.Actor"](local=True)
|
|
|
|
conf = {
|
|
"id": "https://test.federation/test",
|
|
"items": uploads,
|
|
"item_serializer": serializers.UploadSerializer,
|
|
"actor": actor,
|
|
"page_size": 2,
|
|
}
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Collection",
|
|
"id": conf["id"],
|
|
"attributedTo": actor.fid,
|
|
"totalItems": len(uploads),
|
|
"current": conf["id"] + "?page=1",
|
|
"last": conf["id"] + "?page=3",
|
|
"first": conf["id"] + "?page=1",
|
|
}
|
|
|
|
serializer = serializers.PaginatedCollectionSerializer(conf)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_paginated_collection_serializer_validation():
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Collection",
|
|
"id": "https://test.federation/test",
|
|
"totalItems": 5,
|
|
"actor": "http://test.actor",
|
|
"attributedTo": "http://test.actor",
|
|
"first": "https://test.federation/test?page=1",
|
|
"last": "https://test.federation/test?page=1",
|
|
"items": [],
|
|
}
|
|
|
|
serializer = serializers.PaginatedCollectionSerializer(data=data)
|
|
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
assert serializer.validated_data["totalItems"] == 5
|
|
assert serializer.validated_data["id"] == data["id"]
|
|
|
|
|
|
def test_collection_page_serializer_validation():
|
|
base = "https://test.federation/test"
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "CollectionPage",
|
|
"id": base + "?page=2",
|
|
"totalItems": 5,
|
|
"actor": "https://test.actor",
|
|
"attributedTo": "https://test.actor",
|
|
"items": [],
|
|
"first": "https://test.federation/test?page=1",
|
|
"last": "https://test.federation/test?page=3",
|
|
"prev": base + "?page=1",
|
|
"next": base + "?page=3",
|
|
"partOf": base,
|
|
}
|
|
|
|
serializer = serializers.CollectionPageSerializer(data=data)
|
|
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
assert serializer.validated_data["totalItems"] == 5
|
|
assert serializer.validated_data["id"] == data["id"]
|
|
assert serializer.validated_data["attributedTo"] == data["actor"]
|
|
assert serializer.validated_data["items"] == []
|
|
assert serializer.validated_data["prev"] == data["prev"]
|
|
assert serializer.validated_data["next"] == data["next"]
|
|
assert serializer.validated_data["partOf"] == data["partOf"]
|
|
|
|
|
|
def test_collection_page_serializer_can_validate_child():
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "CollectionPage",
|
|
"id": "https://test.page?page=2",
|
|
"attributedTo": "https://test.actor",
|
|
"first": "https://test.page?page=1",
|
|
"last": "https://test.page?page=3",
|
|
"partOf": "https://test.page",
|
|
"totalItems": 1,
|
|
"items": [{"in": "valid"}],
|
|
}
|
|
|
|
serializer = serializers.CollectionPageSerializer(
|
|
data=data, context={"item_serializer": serializers.UploadSerializer}
|
|
)
|
|
|
|
# child are validated but not included in data if not valid
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
assert len(serializer.validated_data["items"]) == 0
|
|
|
|
|
|
def test_collection_page_serializer(factories):
|
|
uploads = factories["music.Upload"].create_batch(size=5)
|
|
actor = factories["federation.Actor"](local=True)
|
|
|
|
conf = {
|
|
"id": "https://test.federation/test",
|
|
"item_serializer": serializers.UploadSerializer,
|
|
"actor": actor,
|
|
"page": Paginator(uploads, 2).page(2),
|
|
}
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "CollectionPage",
|
|
"id": conf["id"] + "?page=2",
|
|
"attributedTo": actor.fid,
|
|
"totalItems": len(uploads),
|
|
"partOf": conf["id"],
|
|
"prev": conf["id"] + "?page=1",
|
|
"next": conf["id"] + "?page=3",
|
|
"first": conf["id"] + "?page=1",
|
|
"last": conf["id"] + "?page=3",
|
|
"items": [
|
|
conf["item_serializer"](
|
|
i, context={"actor": actor, "include_ap_context": False}
|
|
).data
|
|
for i in conf["page"].object_list
|
|
],
|
|
}
|
|
|
|
serializer = serializers.CollectionPageSerializer(conf)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_music_library_serializer_to_ap(factories):
|
|
library = factories["music.Library"](privacy_level="everyone")
|
|
# pending, errored and skippednot included
|
|
factories["music.Upload"](import_status="pending")
|
|
factories["music.Upload"](import_status="errored")
|
|
factories["music.Upload"](import_status="finished")
|
|
serializer = serializers.LibrarySerializer(library)
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"audience": "https://www.w3.org/ns/activitystreams#Public",
|
|
"type": "Library",
|
|
"id": library.fid,
|
|
"name": library.name,
|
|
"summary": library.description,
|
|
"attributedTo": library.actor.fid,
|
|
"totalItems": 0,
|
|
"current": library.fid + "?page=1",
|
|
"last": library.fid + "?page=1",
|
|
"first": library.fid + "?page=1",
|
|
"followers": library.followers_url,
|
|
}
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_music_library_serializer_from_public(factories, mocker):
|
|
actor = factories["federation.Actor"]()
|
|
retrieve = mocker.patch(
|
|
"funkwhale_api.federation.utils.retrieve_ap_object", return_value=actor
|
|
)
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"audience": "https://www.w3.org/ns/activitystreams#Public",
|
|
"name": "Hello",
|
|
"summary": "World",
|
|
"type": "Library",
|
|
"id": "https://library.id",
|
|
"followers": "https://library.id/followers",
|
|
"attributedTo": actor.fid,
|
|
"totalItems": 12,
|
|
"first": "https://library.id?page=1",
|
|
"last": "https://library.id?page=2",
|
|
}
|
|
serializer = serializers.LibrarySerializer(data=data)
|
|
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
library = serializer.save()
|
|
|
|
assert library.actor == actor
|
|
assert library.fid == data["id"]
|
|
assert library.uploads_count == data["totalItems"]
|
|
assert library.privacy_level == "everyone"
|
|
assert library.name == "Hello"
|
|
assert library.description == "World"
|
|
assert library.followers_url == data["followers"]
|
|
|
|
retrieve.assert_called_once_with(
|
|
actor.fid,
|
|
actor=None,
|
|
queryset=actor.__class__,
|
|
serializer_class=serializers.ActorSerializer,
|
|
)
|
|
|
|
|
|
def test_music_library_serializer_from_private(factories, mocker):
|
|
actor = factories["federation.Actor"]()
|
|
retrieve = mocker.patch(
|
|
"funkwhale_api.federation.utils.retrieve_ap_object", return_value=actor
|
|
)
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"audience": "",
|
|
"name": "Hello",
|
|
"summary": "World",
|
|
"type": "Library",
|
|
"id": "https://library.id",
|
|
"followers": "https://library.id/followers",
|
|
"attributedTo": actor.fid,
|
|
"totalItems": 12,
|
|
"first": "https://library.id?page=1",
|
|
"last": "https://library.id?page=2",
|
|
}
|
|
serializer = serializers.LibrarySerializer(data=data)
|
|
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
library = serializer.save()
|
|
|
|
assert library.actor == actor
|
|
assert library.fid == data["id"]
|
|
assert library.uploads_count == data["totalItems"]
|
|
assert library.privacy_level == "me"
|
|
assert library.name == "Hello"
|
|
assert library.description == "World"
|
|
assert library.followers_url == data["followers"]
|
|
retrieve.assert_called_once_with(
|
|
actor.fid,
|
|
actor=None,
|
|
queryset=actor.__class__,
|
|
serializer_class=serializers.ActorSerializer,
|
|
)
|
|
|
|
|
|
def test_music_library_serializer_from_ap_update(factories, mocker):
|
|
actor = factories["federation.Actor"]()
|
|
library = factories["music.Library"]()
|
|
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"audience": "https://www.w3.org/ns/activitystreams#Public",
|
|
"name": "Hello",
|
|
"summary": "World",
|
|
"type": "Library",
|
|
"id": library.fid,
|
|
"followers": "https://library.id/followers",
|
|
"attributedTo": actor.fid,
|
|
"totalItems": 12,
|
|
"first": "https://library.id?page=1",
|
|
"last": "https://library.id?page=2",
|
|
}
|
|
serializer = serializers.LibrarySerializer(library, data=data)
|
|
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
serializer.save()
|
|
library.refresh_from_db()
|
|
|
|
assert library.uploads_count == data["totalItems"]
|
|
assert library.privacy_level == "everyone"
|
|
assert library.name == "Hello"
|
|
assert library.description == "World"
|
|
assert library.followers_url == data["followers"]
|
|
|
|
|
|
def test_activity_pub_artist_serializer_to_ap(factories):
|
|
content = factories["common.Content"]()
|
|
artist = factories["music.Artist"](
|
|
description=content, attributed=True, set_tags=["Punk", "Rock"], with_cover=True
|
|
)
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Artist",
|
|
"id": artist.fid,
|
|
"name": artist.name,
|
|
"musicbrainzId": artist.mbid,
|
|
"published": artist.creation_date.isoformat(),
|
|
"attributedTo": artist.attributed_to.fid,
|
|
"mediaType": "text/html",
|
|
"content": common_utils.render_html(content.text, content.content_type),
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": utils.full_url(artist.attachment_cover.file.url),
|
|
},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
serializer = serializers.ArtistSerializer(artist)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_activity_pub_artist_serializer_from_ap_create(factories, faker, now, mocker):
|
|
actor = factories["federation.Actor"]()
|
|
mocker.patch(
|
|
"funkwhale_api.federation.utils.retrieve_ap_object", return_value=actor
|
|
)
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Artist",
|
|
"id": "https://test.artist",
|
|
"name": "Art",
|
|
"musicbrainzId": faker.uuid4(),
|
|
"published": now.isoformat(),
|
|
"attributedTo": actor.fid,
|
|
"content": "Summary",
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": "https://attachment.file",
|
|
},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
serializer = serializers.ArtistSerializer(data=payload)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
artist = serializer.save()
|
|
|
|
assert artist.fid == payload["id"]
|
|
assert artist.attributed_to == actor
|
|
assert artist.name == payload["name"]
|
|
assert str(artist.mbid) == payload["musicbrainzId"]
|
|
assert artist.description.text == payload["content"]
|
|
assert artist.description.content_type == "text/html"
|
|
assert artist.attachment_cover.url == payload["image"]["url"]
|
|
assert artist.attachment_cover.mimetype == payload["image"]["mediaType"]
|
|
assert artist.get_tags() == ["Punk", "Rock"]
|
|
|
|
|
|
def test_activity_pub_artist_serializer_from_ap_update(factories, faker, now, mocker):
|
|
artist = factories["music.Artist"]()
|
|
actor = factories["federation.Actor"]()
|
|
mocker.patch(
|
|
"funkwhale_api.federation.utils.retrieve_ap_object", return_value=actor
|
|
)
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Artist",
|
|
"id": artist.fid,
|
|
"name": "Art",
|
|
"musicbrainzId": faker.uuid4(),
|
|
"published": now.isoformat(),
|
|
"attributedTo": actor.fid,
|
|
"content": "Summary",
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": "https://attachment.file",
|
|
},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
serializer = serializers.ArtistSerializer(artist, data=payload)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
serializer.save()
|
|
artist.refresh_from_db()
|
|
|
|
assert artist.attributed_to == actor
|
|
assert artist.name == payload["name"]
|
|
assert str(artist.mbid) == payload["musicbrainzId"]
|
|
assert artist.description.text == payload["content"]
|
|
assert artist.description.content_type == "text/html"
|
|
assert artist.attachment_cover.url == payload["image"]["url"]
|
|
assert artist.attachment_cover.mimetype == payload["image"]["mediaType"]
|
|
assert artist.get_tags() == ["Punk", "Rock"]
|
|
|
|
|
|
def test_activity_pub_album_serializer_to_ap(factories):
|
|
content = factories["common.Content"]()
|
|
album = factories["music.Album"](
|
|
description=content, attributed=True, set_tags=["Punk", "Rock"], with_cover=True
|
|
)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Album",
|
|
"id": album.fid,
|
|
"name": album.title,
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": utils.full_url(album.attachment_cover.file.url),
|
|
},
|
|
"musicbrainzId": album.mbid,
|
|
"published": album.creation_date.isoformat(),
|
|
"released": album.release_date.isoformat(),
|
|
"artist_credit": serializers.ArtistCreditSerializer(
|
|
album.artist_credit.all(),
|
|
many=True,
|
|
context={"include_ap_context": False},
|
|
).data,
|
|
"attributedTo": album.attributed_to.fid,
|
|
"mediaType": "text/html",
|
|
"content": common_utils.render_html(content.text, content.content_type),
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
serializer = serializers.AlbumSerializer(album)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_activity_pub_album_serializer_to_ap_channel_artist(factories):
|
|
channel = factories["audio.Channel"]()
|
|
album = factories["music.Album"](
|
|
artist_credit__artist=channel.artist,
|
|
)
|
|
|
|
serializer = serializers.AlbumSerializer(album)
|
|
assert serializer.data["artist_credit"] == [
|
|
{
|
|
"type": "ArtistCredit",
|
|
"id": album.artist_credit.all()[0].fid,
|
|
"artist": {
|
|
"type": "Artist",
|
|
"id": album.artist_credit.all()[0].artist.fid,
|
|
"name": album.artist_credit.all()[0].artist.name,
|
|
"published": album.artist_credit.all()[
|
|
0
|
|
].artist.creation_date.isoformat(),
|
|
"musicbrainzId": str(album.artist_credit.all()[0].artist.mbid),
|
|
"attributedTo": album.artist_credit.all()[0].artist.attributed_to.fid,
|
|
"tag": [],
|
|
"image": None,
|
|
},
|
|
"joinphrase": "",
|
|
"credit": album.artist_credit.all()[0].credit,
|
|
"index": None,
|
|
"published": album.artist_credit.all()[0].creation_date.isoformat(),
|
|
}
|
|
]
|
|
|
|
|
|
def test_activity_pub_album_serializer_from_ap_create(factories, faker, now):
|
|
actor = factories["federation.Actor"]()
|
|
artist = factories["music.Artist"]()
|
|
artist_credit = factories["music.ArtistCredit"](artist=artist)
|
|
|
|
released = faker.date_object()
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Album",
|
|
"id": "https://album.example",
|
|
"name": faker.sentence(),
|
|
"image": {"type": "Link", "mediaType": "image/jpeg", "href": faker.url()},
|
|
"musicbrainzId": faker.uuid4(),
|
|
"published": now.isoformat(),
|
|
"released": released.isoformat(),
|
|
"artist_credit": serializers.ArtistCreditSerializer(
|
|
[artist_credit], many=True, context={"include_ap_context": False}
|
|
).data,
|
|
"attributedTo": actor.fid,
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
serializer = serializers.AlbumSerializer(data=payload)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
album = serializer.save()
|
|
|
|
assert album.title == payload["name"]
|
|
assert str(album.mbid) == payload["musicbrainzId"]
|
|
assert album.release_date == released
|
|
assert album.artist_credit.all()[0].artist == artist
|
|
assert album.attachment_cover.url == payload["image"]["href"]
|
|
assert album.attachment_cover.mimetype == payload["image"]["mediaType"]
|
|
assert sorted(album.tagged_items.values_list("tag__name", flat=True)) == [
|
|
"Punk",
|
|
"Rock",
|
|
]
|
|
|
|
|
|
def test_activity_pub_album_serializer_from_ap_create_channel_artist(
|
|
factories, faker, now, mocker
|
|
):
|
|
actor = factories["federation.Actor"]()
|
|
channel = factories["audio.Channel"]()
|
|
ac = factories["music.ArtistCredit"](artist=channel.artist)
|
|
|
|
released = faker.date_object()
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Album",
|
|
"id": "https://album.example",
|
|
"name": faker.sentence(),
|
|
"published": now.isoformat(),
|
|
"released": released.isoformat(),
|
|
"artist_credit": [
|
|
{
|
|
"artist": {
|
|
"type": "Artist",
|
|
"mediaType": "text/plain",
|
|
"content": "Artist summary",
|
|
"id": "http://hello.artist",
|
|
"name": "John Smith",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"published": now.isoformat(),
|
|
"attributedTo": "https://cover.image/album-artist.pn",
|
|
"tag": [{"type": "Hashtag", "name": "AlbumArtistTag"}],
|
|
"image": {
|
|
"type": "Image",
|
|
"url": "https://cover.image/album-artist.png",
|
|
"mediaType": "image/png",
|
|
},
|
|
},
|
|
"joinphrase": "",
|
|
"name": "John Smith",
|
|
"published": now.isoformat(),
|
|
"id": "http://hello.artistcredit",
|
|
}
|
|
],
|
|
"attributedTo": actor.fid,
|
|
}
|
|
|
|
mocker.patch.object(utils, "retrieve_ap_object", return_value=ac)
|
|
serializer = serializers.AlbumSerializer(data=payload)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
album = serializer.save()
|
|
|
|
assert album.artist_credit.all()[0].artist == channel.artist
|
|
|
|
|
|
def test_activity_pub_album_serializer_from_ap_update(factories, faker):
|
|
album = factories["music.Album"](attributed=True)
|
|
released = faker.date_object()
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Album",
|
|
"id": album.fid,
|
|
"name": faker.sentence(),
|
|
"image": {"type": "Link", "mediaType": "image/jpeg", "href": faker.url()},
|
|
"musicbrainzId": faker.uuid4(),
|
|
"published": album.creation_date.isoformat(),
|
|
"released": released.isoformat(),
|
|
"artist_credit": serializers.ArtistCreditSerializer(
|
|
album.artist_credit.all(),
|
|
many=True,
|
|
context={"include_ap_context": False},
|
|
).data,
|
|
"attributedTo": album.attributed_to.fid,
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
serializer = serializers.AlbumSerializer(album, data=payload)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
serializer.save()
|
|
|
|
album.refresh_from_db()
|
|
|
|
assert album.title == payload["name"]
|
|
assert str(album.mbid) == payload["musicbrainzId"]
|
|
assert album.release_date == released
|
|
assert album.attachment_cover.url == payload["image"]["href"]
|
|
assert album.attachment_cover.mimetype == payload["image"]["mediaType"]
|
|
assert sorted(album.tagged_items.values_list("tag__name", flat=True)) == [
|
|
"Punk",
|
|
"Rock",
|
|
]
|
|
|
|
|
|
def test_activity_pub_track_serializer_to_ap(factories):
|
|
content = factories["common.Content"]()
|
|
track = factories["music.Track"](
|
|
description=content,
|
|
license="cc-by-4.0",
|
|
copyright="test",
|
|
disc_number=3,
|
|
attributed=True,
|
|
set_tags=["Punk", "Rock"],
|
|
with_cover=True,
|
|
)
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"published": track.creation_date.isoformat(),
|
|
"type": "Track",
|
|
"musicbrainzId": track.mbid,
|
|
"id": track.fid,
|
|
"name": track.title,
|
|
"position": track.position,
|
|
"disc": track.disc_number,
|
|
"license": track.license.conf["identifiers"][0],
|
|
"copyright": "test",
|
|
"artist_credit": serializers.ArtistCreditSerializer(
|
|
track.artist_credit.all(),
|
|
many=True,
|
|
context={"include_ap_context": False},
|
|
).data,
|
|
"album": serializers.AlbumSerializer(
|
|
track.album, context={"include_ap_context": False}
|
|
).data,
|
|
"attributedTo": track.attributed_to.fid,
|
|
"mediaType": "text/html",
|
|
"content": common_utils.render_html(content.text, content.content_type),
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": utils.full_url(track.attachment_cover.file.url),
|
|
},
|
|
}
|
|
serializer = serializers.TrackSerializer(track)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_activity_pub_track_serializer_from_ap(factories, r_mock, mocker):
|
|
add_tags = mocker.patch("funkwhale_api.tags.models.add_tags")
|
|
track_attributed_to = factories["federation.Actor"]()
|
|
album_attributed_to = factories["federation.Actor"]()
|
|
album_artist_attributed_to = factories["federation.Actor"]()
|
|
artist_attributed_to = factories["federation.Actor"]()
|
|
|
|
activity = factories["federation.Activity"]()
|
|
published = timezone.now()
|
|
released = timezone.now().date()
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Track",
|
|
"id": "http://hello.track",
|
|
"published": published.isoformat(),
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"name": "Black in back",
|
|
"position": 5,
|
|
"disc": 1,
|
|
"content": "Hello there",
|
|
"attributedTo": track_attributed_to.fid,
|
|
"image": {
|
|
"type": "Image",
|
|
"url": "https://cover.image/track.png",
|
|
"mediaType": "image/png",
|
|
},
|
|
"album": {
|
|
"type": "Album",
|
|
"id": "http://hello.album",
|
|
"name": "Purple album",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"published": published.isoformat(),
|
|
"released": released.isoformat(),
|
|
"content": "Album summary",
|
|
"mediaType": "text/markdown",
|
|
"attributedTo": album_attributed_to.fid,
|
|
"image": {
|
|
"type": "Link",
|
|
"href": "https://cover.image/test.png",
|
|
"mediaType": "image/png",
|
|
},
|
|
"tag": [{"type": "Hashtag", "name": "AlbumTag"}],
|
|
"artist_credit": [
|
|
{
|
|
"artist": {
|
|
"type": "Artist",
|
|
"mediaType": "text/plain",
|
|
"content": "Artist summary",
|
|
"id": "http://hello.artist",
|
|
"name": "John Smith",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"published": published.isoformat(),
|
|
"attributedTo": album_artist_attributed_to.fid,
|
|
"tag": [{"type": "Hashtag", "name": "AlbumArtistTag"}],
|
|
"image": {
|
|
"type": "Image",
|
|
"url": "https://cover.image/album-artist.png",
|
|
"mediaType": "image/png",
|
|
},
|
|
},
|
|
"joinphrase": "",
|
|
"credit": "John Smith Credit",
|
|
"published": published.isoformat(),
|
|
"id": "http://hello.artistcredit",
|
|
}
|
|
],
|
|
},
|
|
"artist_credit": [
|
|
{
|
|
"artist": {
|
|
"type": "Artist",
|
|
"id": "http://hello.trackartist",
|
|
"name": "Bob Smith",
|
|
"mediaType": "text/plain",
|
|
"content": "Other artist summary",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"attributedTo": artist_attributed_to.fid,
|
|
"published": published.isoformat(),
|
|
"tag": [{"type": "Hashtag", "name": "ArtistTag"}],
|
|
"image": {
|
|
"type": "Image",
|
|
"url": "https://cover.image/artist.png",
|
|
"mediaType": "image/png",
|
|
},
|
|
},
|
|
"joinphrase": "",
|
|
"credit": "Credit Name",
|
|
"published": published.isoformat(),
|
|
"id": "http://hello.artistcredit",
|
|
}
|
|
],
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Hello"},
|
|
{"type": "Hashtag", "name": "World"},
|
|
],
|
|
}
|
|
serializer = serializers.TrackSerializer(data=data, context={"activity": activity})
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
track = serializer.save()
|
|
album = track.album
|
|
artist = track.artist_credit.all()[0].artist
|
|
album_artist = track.album.artist_credit.all()[0].artist
|
|
|
|
assert track.from_activity == activity
|
|
assert track.fid == data["id"]
|
|
assert track.title == data["name"]
|
|
assert track.position == data["position"]
|
|
assert track.disc_number == data["disc"]
|
|
assert track.creation_date == published
|
|
assert track.attributed_to == track_attributed_to
|
|
assert str(track.mbid) == data["musicbrainzId"]
|
|
assert track.description.text == data["content"]
|
|
assert track.description.content_type == "text/html"
|
|
assert track.attachment_cover.url == data["image"]["url"]
|
|
assert track.attachment_cover.mimetype == data["image"]["mediaType"]
|
|
|
|
assert album.from_activity == activity
|
|
assert album.attachment_cover.url == data["album"]["image"]["href"]
|
|
assert album.attachment_cover.mimetype == data["album"]["image"]["mediaType"]
|
|
assert album.title == data["album"]["name"]
|
|
assert album.fid == data["album"]["id"]
|
|
assert str(album.mbid) == data["album"]["musicbrainzId"]
|
|
assert album.creation_date == published
|
|
assert album.release_date == released
|
|
assert album.attributed_to == album_attributed_to
|
|
assert album.description.text == data["album"]["content"]
|
|
assert album.description.content_type == data["album"]["mediaType"]
|
|
|
|
assert artist.from_activity == activity
|
|
assert artist.name == data["artist_credit"][0]["artist"]["name"]
|
|
assert track.artist_credit.all()[0].credit == data["artist_credit"][0]["credit"]
|
|
assert (
|
|
album.artist_credit.all()[0].credit
|
|
== data["album"]["artist_credit"][0]["credit"]
|
|
)
|
|
|
|
assert artist.fid == data["artist_credit"][0]["artist"]["id"]
|
|
assert str(artist.mbid) == data["artist_credit"][0]["artist"]["musicbrainzId"]
|
|
assert artist.creation_date == published
|
|
assert artist.attributed_to == artist_attributed_to
|
|
assert artist.description.text == data["artist_credit"][0]["artist"]["content"]
|
|
assert (
|
|
artist.description.content_type
|
|
== data["artist_credit"][0]["artist"]["mediaType"]
|
|
)
|
|
assert (
|
|
artist.attachment_cover.url
|
|
== data["artist_credit"][0]["artist"]["image"]["url"]
|
|
)
|
|
assert (
|
|
artist.attachment_cover.mimetype
|
|
== data["artist_credit"][0]["artist"]["image"]["mediaType"]
|
|
)
|
|
|
|
assert album_artist.from_activity == activity
|
|
assert album_artist.name == data["album"]["artist_credit"][0]["artist"]["name"]
|
|
assert album_artist.fid == data["album"]["artist_credit"][0]["artist"]["id"]
|
|
assert (
|
|
str(album_artist.mbid)
|
|
== data["album"]["artist_credit"][0]["artist"]["musicbrainzId"]
|
|
)
|
|
assert album_artist.creation_date == published
|
|
assert album_artist.attributed_to == album_artist_attributed_to
|
|
assert (
|
|
album_artist.description.text
|
|
== data["album"]["artist_credit"][0]["artist"]["content"]
|
|
)
|
|
assert (
|
|
album_artist.description.content_type
|
|
== data["album"]["artist_credit"][0]["artist"]["mediaType"]
|
|
)
|
|
assert (
|
|
album_artist.attachment_cover.url
|
|
== data["album"]["artist_credit"][0]["artist"]["image"]["url"]
|
|
)
|
|
assert (
|
|
album_artist.attachment_cover.mimetype
|
|
== data["album"]["artist_credit"][0]["artist"]["image"]["mediaType"]
|
|
)
|
|
|
|
add_tags.assert_any_call(track, *["Hello", "World"])
|
|
add_tags.assert_any_call(album, *["AlbumTag"])
|
|
add_tags.assert_any_call(album_artist, *["AlbumArtistTag"])
|
|
add_tags.assert_any_call(artist, *["ArtistTag"])
|
|
|
|
|
|
def test_activity_pub_track_serializer_from_ap_update(factories, r_mock, mocker, faker):
|
|
set_tags = mocker.patch("funkwhale_api.tags.models.set_tags")
|
|
content = factories["common.Content"]()
|
|
track_attributed_to = factories["federation.Actor"]()
|
|
track = factories["music.Track"](description=content)
|
|
|
|
published = timezone.now()
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Track",
|
|
"id": track.fid,
|
|
"published": published.isoformat(),
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"name": "Black in back",
|
|
"position": 5,
|
|
"disc": 2,
|
|
"content": "hello there",
|
|
"attributedTo": track_attributed_to.fid,
|
|
"album": serializers.AlbumSerializer(track.album).data,
|
|
"artist_credit": serializers.ArtistCreditSerializer(
|
|
track.artist_credit.all(), many=True
|
|
).data,
|
|
"image": {"type": "Image", "mediaType": "image/jpeg", "url": faker.url()},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Hello"},
|
|
# Ensure we can handle tags without a leading #
|
|
{"type": "Hashtag", "name": "World"},
|
|
],
|
|
}
|
|
serializer = serializers.TrackSerializer(track, data=data)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
|
|
serializer.save()
|
|
track.refresh_from_db()
|
|
|
|
assert track.fid == data["id"]
|
|
assert track.title == data["name"]
|
|
assert track.position == data["position"]
|
|
assert track.disc_number == data["disc"]
|
|
assert track.attributed_to == track_attributed_to
|
|
assert track.description.content_type == "text/html"
|
|
assert track.description.text == "hello there"
|
|
assert str(track.mbid) == data["musicbrainzId"]
|
|
assert track.attachment_cover.url == data["image"]["url"]
|
|
assert track.attachment_cover.mimetype == data["image"]["mediaType"]
|
|
|
|
set_tags.assert_called_once_with(track, *["Hello", "World"])
|
|
|
|
with pytest.raises(content.DoesNotExist):
|
|
content.refresh_from_db()
|
|
|
|
|
|
def test_activity_pub_upload_serializer_from_ap(factories, mocker, r_mock):
|
|
activity = factories["federation.Activity"]()
|
|
library = factories["music.Library"]()
|
|
|
|
published = timezone.now()
|
|
updated = timezone.now()
|
|
released = timezone.now().date()
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Audio",
|
|
"id": "https://track.file",
|
|
"name": "Ignored",
|
|
"published": published.isoformat(),
|
|
"updated": updated.isoformat(),
|
|
"duration": 43,
|
|
"bitrate": 42,
|
|
"size": 66,
|
|
"url": {"href": "https://audio.file", "type": "Link", "mediaType": "audio/mp3"},
|
|
"library": library.fid,
|
|
"track": {
|
|
"type": "Track",
|
|
"id": "http://hello.track",
|
|
"published": published.isoformat(),
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"name": "Black in back",
|
|
"position": 5,
|
|
"album": {
|
|
"type": "Album",
|
|
"id": "http://hello.album",
|
|
"name": "Purple album",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"published": published.isoformat(),
|
|
"released": released.isoformat(),
|
|
"image": {
|
|
"type": "Link",
|
|
"href": "https://cover.image/test.png",
|
|
"mediaType": "image/png",
|
|
},
|
|
"artist_credit": [
|
|
{
|
|
"artist": {
|
|
"type": "Artist",
|
|
"id": "http://hello.artist",
|
|
"name": "John Smith",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"published": published.isoformat(),
|
|
},
|
|
"joinphrase": "",
|
|
"credit": "John Smith Credit",
|
|
"published": published.isoformat(),
|
|
"id": "http://hello.artistcredit",
|
|
}
|
|
],
|
|
},
|
|
"artist_credit": [
|
|
{
|
|
"artist": {
|
|
"type": "Artist",
|
|
"id": "http://hello.trackartist",
|
|
"name": "Bob Smith",
|
|
"musicbrainzId": str(uuid.uuid4()),
|
|
"published": published.isoformat(),
|
|
},
|
|
"joinphrase": "",
|
|
"credit": "Bob Smith Credit",
|
|
"published": published.isoformat(),
|
|
"id": "http://hello.artistcredit",
|
|
}
|
|
],
|
|
},
|
|
}
|
|
r_mock.get(data["track"]["album"]["image"]["href"], body=io.BytesIO(b"coucou"))
|
|
|
|
serializer = serializers.UploadSerializer(data=data, context={"activity": activity})
|
|
assert serializer.is_valid(raise_exception=True)
|
|
track_create = mocker.spy(serializers.TrackSerializer, "create")
|
|
upload = serializer.save()
|
|
|
|
assert upload.track.from_activity == activity
|
|
assert upload.from_activity == activity
|
|
assert track_create.call_count == 1
|
|
assert upload.fid == data["id"]
|
|
assert upload.track.fid == data["track"]["id"]
|
|
assert upload.duration == data["duration"]
|
|
assert upload.size == data["size"]
|
|
assert upload.bitrate == data["bitrate"]
|
|
assert upload.source == data["url"]["href"]
|
|
assert upload.mimetype == data["url"]["mediaType"]
|
|
assert upload.creation_date == published
|
|
assert upload.import_status == "finished"
|
|
assert upload.modification_date == updated
|
|
|
|
|
|
def test_activity_pub_upload_serializer_from_ap_update(factories, mocker, now, r_mock):
|
|
library = factories["music.Library"]()
|
|
upload = factories["music.Upload"](library=library, track__album__with_cover=True)
|
|
data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Audio",
|
|
"id": upload.fid,
|
|
"name": "Ignored",
|
|
"published": now.isoformat(),
|
|
"updated": now.isoformat(),
|
|
"duration": 42,
|
|
"bitrate": 42,
|
|
"size": 66,
|
|
"url": {
|
|
"href": "https://audio.file/url",
|
|
"type": "Link",
|
|
"mediaType": "audio/mp3",
|
|
},
|
|
"library": library.fid,
|
|
"track": serializers.TrackSerializer(upload.track).data,
|
|
}
|
|
r_mock.get(data["track"]["album"]["image"]["url"], body=io.BytesIO(b"coucou"))
|
|
|
|
serializer = serializers.UploadSerializer(upload, data=data)
|
|
assert serializer.is_valid(raise_exception=True)
|
|
serializer.save()
|
|
upload.refresh_from_db()
|
|
|
|
assert upload.fid == data["id"]
|
|
assert upload.duration == data["duration"]
|
|
assert upload.size == data["size"]
|
|
assert upload.bitrate == data["bitrate"]
|
|
assert upload.source == data["url"]["href"]
|
|
assert upload.mimetype == data["url"]["mediaType"]
|
|
|
|
|
|
def test_activity_pub_upload_serializer_validtes_library_actor(factories, mocker):
|
|
library = factories["music.Library"]()
|
|
usurpator = factories["federation.Actor"]()
|
|
|
|
serializer = serializers.UploadSerializer(data={}, context={"actor": usurpator})
|
|
|
|
with pytest.raises(serializers.serializers.ValidationError):
|
|
serializer.validate_library(library.fid)
|
|
|
|
|
|
def test_activity_pub_audio_serializer_to_ap(factories):
|
|
upload = factories["music.Upload"](
|
|
mimetype="audio/mp3",
|
|
bitrate=42,
|
|
duration=43,
|
|
size=44,
|
|
library__privacy_level="everyone",
|
|
)
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Audio",
|
|
"id": upload.fid,
|
|
"name": upload.track.full_name,
|
|
"published": upload.creation_date.isoformat(),
|
|
"updated": upload.modification_date.isoformat(),
|
|
"duration": upload.duration,
|
|
"bitrate": upload.bitrate,
|
|
"size": upload.size,
|
|
"to": contexts.AS.Public,
|
|
"attributedTo": upload.library.actor.fid,
|
|
"url": [
|
|
{
|
|
"href": utils.full_url(upload.listen_url_no_download),
|
|
"type": "Link",
|
|
"mediaType": "audio/mp3",
|
|
},
|
|
{
|
|
"type": "Link",
|
|
"mediaType": "text/html",
|
|
"href": utils.full_url(upload.track.get_absolute_url()),
|
|
},
|
|
],
|
|
"library": upload.library.fid,
|
|
"track": serializers.TrackSerializer(
|
|
upload.track, context={"include_ap_context": False}
|
|
).data,
|
|
}
|
|
|
|
serializer = serializers.UploadSerializer(upload)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_local_actor_serializer_to_ap(factories, settings):
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": "https://test.federation/user",
|
|
"type": "Person",
|
|
"following": "https://test.federation/user/following",
|
|
"followers": "https://test.federation/user/followers",
|
|
"inbox": "https://test.federation/user/inbox",
|
|
"outbox": "https://test.federation/user/outbox",
|
|
"preferredUsername": "user",
|
|
"name": "Real User",
|
|
"summary": "Hello world",
|
|
"manuallyApprovesFollowers": False,
|
|
"publicKey": {
|
|
"id": "https://test.federation/user#main-key",
|
|
"owner": "https://test.federation/user",
|
|
"publicKeyPem": "yolo",
|
|
},
|
|
"endpoints": {"sharedInbox": "https://test.federation/inbox"},
|
|
}
|
|
ac = models.Actor.objects.create(
|
|
fid=expected["id"],
|
|
inbox_url=expected["inbox"],
|
|
outbox_url=expected["outbox"],
|
|
shared_inbox_url=expected["endpoints"]["sharedInbox"],
|
|
followers_url=expected["followers"],
|
|
following_url=expected["following"],
|
|
public_key=expected["publicKey"]["publicKeyPem"],
|
|
preferred_username=expected["preferredUsername"],
|
|
name=expected["name"],
|
|
domain=models.Domain.objects.create(pk="test.federation"),
|
|
type="Person",
|
|
manually_approves_followers=False,
|
|
attachment_icon=factories["common.Attachment"](),
|
|
)
|
|
content = common_utils.attach_content(
|
|
ac, "summary_obj", {"text": "hello world", "content_type": "text/markdown"}
|
|
)
|
|
user = factories["users.User"]()
|
|
user.actor = ac
|
|
user.save()
|
|
ac.refresh_from_db()
|
|
expected["summary"] = content.rendered
|
|
expected["url"] = [
|
|
{
|
|
"type": "Link",
|
|
"href": "https://{}/@{}".format(
|
|
settings.FUNKWHALE_HOSTNAME, ac.preferred_username
|
|
),
|
|
"mediaType": "text/html",
|
|
}
|
|
]
|
|
expected["icon"] = {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": utils.full_url(ac.attachment_icon.file.url),
|
|
}
|
|
serializer = serializers.ActorSerializer(ac)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_activity_serializer_validate_recipients_empty(db):
|
|
s = serializers.BaseActivitySerializer()
|
|
|
|
with pytest.raises(serializers.serializers.ValidationError):
|
|
s.validate_recipients({}, {})
|
|
|
|
with pytest.raises(serializers.serializers.ValidationError):
|
|
s.validate_recipients({"to": []}, {})
|
|
|
|
with pytest.raises(serializers.serializers.ValidationError):
|
|
s.validate_recipients({"cc": []}, {})
|
|
|
|
|
|
def test_activity_serializer_validate_recipients_context(db):
|
|
s = serializers.BaseActivitySerializer(context={"recipients": ["dummy"]})
|
|
|
|
assert s.validate_recipients({}, {}) is None
|
|
|
|
|
|
def test_track_serializer_update_license(factories):
|
|
licenses.load(licenses.LICENSES)
|
|
|
|
obj = factories["music.Track"](license=None)
|
|
|
|
serializer = serializers.TrackSerializer(obj)
|
|
serializer.update(obj, {"license": "http://creativecommons.org/licenses/by/2.0/"})
|
|
|
|
obj.refresh_from_db()
|
|
|
|
assert obj.license_id == "cc-by-2.0"
|
|
|
|
|
|
def test_channel_actor_serializer(factories):
|
|
channel = factories["audio.Channel"](
|
|
actor__attachment_icon=None,
|
|
artist__with_cover=True,
|
|
artist__set_tags=["punk", "rock"],
|
|
)
|
|
|
|
serializer = serializers.ActorSerializer(channel.actor)
|
|
expected_url = [
|
|
{
|
|
"type": "Link",
|
|
"href": channel.actor.get_absolute_url(),
|
|
"mediaType": "text/html",
|
|
},
|
|
{
|
|
"type": "Link",
|
|
"href": channel.get_rss_url(),
|
|
"mediaType": "application/rss+xml",
|
|
},
|
|
]
|
|
expected_icon = {
|
|
"type": "Image",
|
|
"mediaType": channel.artist.attachment_cover.mimetype,
|
|
"url": channel.artist.attachment_cover.download_url_original,
|
|
}
|
|
assert serializer.data["url"] == expected_url
|
|
assert serializer.data["icon"] == expected_icon
|
|
assert serializer.data["attributedTo"] == channel.attributed_to.fid
|
|
assert serializer.data["category"] == channel.artist.content_category
|
|
assert serializer.data["tag"] == [
|
|
{"type": "Hashtag", "name": "#punk"},
|
|
{"type": "Hashtag", "name": "#rock"},
|
|
]
|
|
|
|
|
|
def test_channel_actor_serializer_from_ap_create(mocker, factories):
|
|
domain = factories["federation.Domain"](name="test.pod")
|
|
attributed_to = factories["federation.Actor"](domain=domain)
|
|
get_actor = mocker.patch.object(actors, "get_actor", return_value=attributed_to)
|
|
actor_data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"followers": "https://test.pod/federation/actors/mychannel/followers",
|
|
"preferredUsername": "mychannel",
|
|
"id": "https://test.pod/federation/actors/mychannel",
|
|
"endpoints": {"sharedInbox": "https://test.pod/federation/shared/inbox"},
|
|
"name": "mychannel",
|
|
"following": "https://test.pod/federation/actors/mychannel/following",
|
|
"outbox": "https://test.pod/federation/actors/mychannel/outbox",
|
|
"url": [
|
|
{
|
|
"mediaType": "text/html",
|
|
"href": "https://test.pod/channels/mychannel",
|
|
"type": "Link",
|
|
},
|
|
{
|
|
"mediaType": "application/rss+xml",
|
|
"href": "https://test.pod/api/v1/channels/mychannel/rss",
|
|
"type": "Link",
|
|
},
|
|
],
|
|
"type": "Person",
|
|
"category": "podcast",
|
|
"attributedTo": attributed_to.fid,
|
|
"manuallyApprovesFollowers": False,
|
|
"inbox": "https://test.pod/federation/actors/mychannel/inbox",
|
|
"icon": {
|
|
"mediaType": "image/jpeg",
|
|
"type": "Image",
|
|
"url": "https://test.pod/media/attachments/dd/ce/b2/nosmile.jpeg",
|
|
},
|
|
"summary": "<p>content</p>",
|
|
"publicKey": {
|
|
"owner": "https://test.pod/federation/actors/mychannel",
|
|
"publicKeyPem": "-----BEGIN RSA PUBLIC KEY-----\n+KwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
"id": "https://test.pod/federation/actors/mychannel#main-key",
|
|
},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Indie"},
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
|
|
serializer = serializers.ActorSerializer(data=actor_data)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
actor = serializer.save()
|
|
|
|
get_actor.assert_called_once_with(actor_data["attributedTo"])
|
|
assert actor.preferred_username == actor_data["preferredUsername"]
|
|
assert actor.fid == actor_data["id"]
|
|
assert actor.name == actor_data["name"]
|
|
assert actor.type == actor_data["type"]
|
|
assert actor.public_key == actor_data["publicKey"]["publicKeyPem"]
|
|
assert actor.outbox_url == actor_data["outbox"]
|
|
assert actor.inbox_url == actor_data["inbox"]
|
|
assert actor.shared_inbox_url == actor_data["endpoints"]["sharedInbox"]
|
|
assert actor.channel.attributed_to == attributed_to
|
|
assert actor.channel.rss_url == actor_data["url"][1]["href"]
|
|
assert actor.channel.artist.attributed_to == attributed_to
|
|
assert actor.channel.artist.content_category == actor_data["category"]
|
|
assert actor.channel.artist.name == actor_data["name"]
|
|
assert actor.channel.artist.get_tags() == ["Indie", "Punk", "Rock"]
|
|
assert actor.channel.artist.description.text == actor_data["summary"]
|
|
assert actor.channel.artist.description.content_type == "text/html"
|
|
assert actor.channel.artist.attachment_cover.url == actor_data["icon"]["url"]
|
|
assert (
|
|
actor.channel.artist.attachment_cover.mimetype
|
|
== actor_data["icon"]["mediaType"]
|
|
)
|
|
assert actor.channel.library.fid is not None
|
|
assert actor.channel.library.actor == attributed_to
|
|
assert actor.channel.library.privacy_level == "everyone"
|
|
assert actor.channel.library.name == actor_data["name"]
|
|
|
|
|
|
def test_channel_actor_serializer_from_ap_update(mocker, factories):
|
|
domain = factories["federation.Domain"](name="test.pod")
|
|
attributed_to = factories["federation.Actor"](domain=domain)
|
|
actor = factories["federation.Actor"](domain=domain)
|
|
channel = factories["audio.Channel"](actor=actor, attributed_to=attributed_to)
|
|
get_actor = mocker.patch.object(actors, "get_actor", return_value=attributed_to)
|
|
library = channel.library
|
|
actor_data = {
|
|
"@context": jsonld.get_default_context(),
|
|
"followers": "https://test.pod/federation/actors/mychannel/followers",
|
|
"preferredUsername": "mychannel",
|
|
"id": actor.fid,
|
|
"endpoints": {"sharedInbox": "https://test.pod/federation/shared/inbox"},
|
|
"name": "mychannel",
|
|
"following": "https://test.pod/federation/actors/mychannel/following",
|
|
"outbox": "https://test.pod/federation/actors/mychannel/outbox",
|
|
"url": [
|
|
{
|
|
"mediaType": "text/html",
|
|
"href": "https://test.pod/channels/mychannel",
|
|
"type": "Link",
|
|
},
|
|
{
|
|
"mediaType": "application/rss+xml",
|
|
"href": "https://test.pod/api/v1/channels/mychannel/rss",
|
|
"type": "Link",
|
|
},
|
|
],
|
|
"type": "Person",
|
|
"category": "podcast",
|
|
"attributedTo": attributed_to.fid,
|
|
"manuallyApprovesFollowers": False,
|
|
"inbox": "https://test.pod/federation/actors/mychannel/inbox",
|
|
"icon": {
|
|
"mediaType": "image/jpeg",
|
|
"type": "Image",
|
|
"url": "https://test.pod/media/attachments/dd/ce/b2/nosmile.jpeg",
|
|
},
|
|
"summary": "<p>content</p>",
|
|
"publicKey": {
|
|
"owner": "https://test.pod/federation/actors/mychannel",
|
|
"publicKeyPem": "-----BEGIN RSA PUBLIC KEY-----\n+KwIDAQAB\n-----END RSA PUBLIC KEY-----\n",
|
|
"id": "https://test.pod/federation/actors/mychannel#main-key",
|
|
},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Indie"},
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
|
|
serializer = serializers.ActorSerializer(data=actor_data)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
serializer.save()
|
|
channel.refresh_from_db()
|
|
get_actor.assert_called_once_with(actor_data["attributedTo"])
|
|
assert channel.actor == actor
|
|
assert channel.attributed_to == attributed_to
|
|
assert channel.rss_url == actor_data["url"][1]["href"]
|
|
assert channel.artist.attributed_to == attributed_to
|
|
assert channel.artist.content_category == actor_data["category"]
|
|
assert channel.artist.name == actor_data["name"]
|
|
assert channel.artist.get_tags() == ["Indie", "Punk", "Rock"]
|
|
assert channel.artist.description.text == actor_data["summary"]
|
|
assert channel.artist.description.content_type == "text/html"
|
|
assert channel.artist.attachment_cover.url == actor_data["icon"]["url"]
|
|
assert channel.artist.attachment_cover.mimetype == actor_data["icon"]["mediaType"]
|
|
assert channel.library.actor == attributed_to
|
|
assert channel.library.privacy_level == library.privacy_level
|
|
assert channel.library.name == library.name
|
|
|
|
|
|
def test_channel_actor_outbox_serializer(factories):
|
|
channel = factories["audio.Channel"]()
|
|
uploads = factories["music.Upload"].create_batch(
|
|
5,
|
|
track__artist_credit__artist=channel.artist,
|
|
library=channel.library,
|
|
import_status="finished",
|
|
)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "OrderedCollection",
|
|
"id": channel.actor.outbox_url,
|
|
"attributedTo": channel.actor.fid,
|
|
"totalItems": len(uploads),
|
|
"first": channel.actor.outbox_url + "?page=1",
|
|
"last": channel.actor.outbox_url + "?page=1",
|
|
"current": channel.actor.outbox_url + "?page=1",
|
|
}
|
|
|
|
serializer = serializers.ChannelOutboxSerializer(channel)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_channel_upload_serializer(factories):
|
|
channel = factories["audio.Channel"](library__privacy_level="everyone")
|
|
content = factories["common.Content"]()
|
|
cover = factories["common.Attachment"]()
|
|
upload = factories["music.Upload"](
|
|
playable=True,
|
|
bitrate=543,
|
|
size=543,
|
|
duration=54,
|
|
library=channel.library,
|
|
import_status="finished",
|
|
track__set_tags=["Punk"],
|
|
track__attachment_cover=cover,
|
|
track__description=content,
|
|
track__disc_number=3,
|
|
track__position=12,
|
|
track__license="cc0-1.0",
|
|
track__copyright="Copyright something",
|
|
track__album__set_tags=["Rock"],
|
|
track__artist_credit__artist__set_tags=["Indie"],
|
|
track__artist_credit__artist__local=True,
|
|
)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Audio",
|
|
"id": upload.fid,
|
|
"name": upload.track.title,
|
|
"summary": "#Indie #Punk #Rock",
|
|
"attributedTo": channel.actor.fid,
|
|
"published": upload.creation_date.isoformat(),
|
|
"mediaType": "text/html",
|
|
"content": common_utils.render_html(content.text, content.content_type),
|
|
"to": "https://www.w3.org/ns/activitystreams#Public",
|
|
"position": upload.track.position,
|
|
"duration": upload.duration,
|
|
"album": upload.track.album.fid,
|
|
"disc": upload.track.disc_number,
|
|
"copyright": upload.track.copyright,
|
|
"license": upload.track.local_license["identifiers"][0],
|
|
"url": [
|
|
{
|
|
"type": "Link",
|
|
"mediaType": "text/html",
|
|
"href": utils.full_url(upload.track.get_absolute_url()),
|
|
},
|
|
{
|
|
"type": "Link",
|
|
"mediaType": upload.mimetype,
|
|
"href": utils.full_url(upload.listen_url_no_download),
|
|
"bitrate": upload.bitrate,
|
|
"size": upload.size,
|
|
},
|
|
],
|
|
"image": {
|
|
"type": "Image",
|
|
"url": upload.track.attachment_cover.download_url_original,
|
|
"mediaType": upload.track.attachment_cover.mimetype,
|
|
},
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Indie"},
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
}
|
|
|
|
serializer = serializers.ChannelUploadSerializer(upload)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_channel_upload_serializer_from_ap_create(factories, now, mocker):
|
|
channel = factories["audio.Channel"](library__privacy_level="everyone")
|
|
album = factories["music.Album"](artist_credit__artist=channel.artist)
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Audio",
|
|
"id": "https://test.pod/uuid",
|
|
"name": "My test track",
|
|
"summary": "#Indie #Punk #Rock",
|
|
"attributedTo": channel.actor.fid,
|
|
"published": now.isoformat(),
|
|
"mediaType": "text/html",
|
|
"content": "<p>Hello</p>",
|
|
"duration": 543,
|
|
"position": 4,
|
|
"disc": 2,
|
|
"album": album.fid,
|
|
"to": "https://www.w3.org/ns/activitystreams#Public",
|
|
"copyright": "Copyright test",
|
|
"license": "http://creativecommons.org/publicdomain/zero/1.0/",
|
|
"url": [
|
|
{
|
|
"type": "Link",
|
|
"mediaType": "text/html",
|
|
"href": "https://test.pod/track",
|
|
},
|
|
{
|
|
"type": "Link",
|
|
"mediaType": "audio/mpeg",
|
|
"href": "https://test.pod/file.mp3",
|
|
"bitrate": 192000,
|
|
"size": 15492738,
|
|
},
|
|
],
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Indie"},
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": "https://image.example/image.png",
|
|
},
|
|
}
|
|
mocker.patch.object(utils, "retrieve_ap_object", return_value=album)
|
|
|
|
serializer = serializers.ChannelUploadSerializer(
|
|
data=payload, context={"channel": channel}
|
|
)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
upload = serializer.save(channel=channel)
|
|
|
|
assert upload.library == channel.library
|
|
assert upload.import_status == "finished"
|
|
assert upload.creation_date == now
|
|
assert upload.fid == payload["id"]
|
|
assert upload.source == payload["url"][1]["href"]
|
|
assert upload.mimetype == payload["url"][1]["mediaType"]
|
|
assert upload.size == payload["url"][1]["size"]
|
|
assert upload.bitrate == payload["url"][1]["bitrate"]
|
|
assert upload.duration == payload["duration"]
|
|
assert upload.track.artist_credit.all()[0].artist == channel.artist
|
|
assert upload.track.position == payload["position"]
|
|
assert upload.track.disc_number == payload["disc"]
|
|
assert upload.track.attributed_to == channel.attributed_to
|
|
assert upload.track.title == payload["name"]
|
|
assert upload.track.creation_date == now
|
|
assert upload.track.description.content_type == payload["mediaType"]
|
|
assert upload.track.description.text == payload["content"]
|
|
assert upload.track.fid == payload["id"]
|
|
assert upload.track.license.pk == "cc0-1.0"
|
|
assert upload.track.copyright == payload["copyright"]
|
|
assert upload.track.get_tags() == ["Indie", "Punk", "Rock"]
|
|
assert upload.track.attachment_cover.mimetype == payload["image"]["mediaType"]
|
|
assert upload.track.attachment_cover.url == payload["image"]["url"]
|
|
assert upload.track.album == album
|
|
|
|
|
|
def test_channel_upload_serializer_from_ap_update(factories, now, mocker):
|
|
channel = factories["audio.Channel"](library__privacy_level="everyone")
|
|
album = factories["music.Album"](artist_credit__artist=channel.artist)
|
|
upload = factories["music.Upload"](
|
|
track__album=album, track__artist_credit__artist=channel.artist
|
|
)
|
|
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Audio",
|
|
"id": upload.fid,
|
|
"name": "Hello there",
|
|
"attributedTo": channel.actor.fid,
|
|
"published": now.isoformat(),
|
|
"mediaType": "text/html",
|
|
"content": "<p>Hello</p>",
|
|
"duration": 543,
|
|
"position": 4,
|
|
"disc": 2,
|
|
"album": album.fid,
|
|
"to": "https://www.w3.org/ns/activitystreams#Public",
|
|
"copyright": "Copyright test",
|
|
"license": "http://creativecommons.org/publicdomain/zero/1.0/",
|
|
"url": [
|
|
{
|
|
"type": "Link",
|
|
"mediaType": "text/html",
|
|
"href": "https://test.pod/track",
|
|
},
|
|
{
|
|
"type": "Link",
|
|
"mediaType": "audio/mpeg",
|
|
"href": "https://test.pod/file.mp3",
|
|
"bitrate": 192000,
|
|
"size": 15492738,
|
|
},
|
|
],
|
|
"tag": [
|
|
{"type": "Hashtag", "name": "#Indie"},
|
|
{"type": "Hashtag", "name": "#Punk"},
|
|
{"type": "Hashtag", "name": "#Rock"},
|
|
],
|
|
"image": {
|
|
"type": "Image",
|
|
"mediaType": "image/jpeg",
|
|
"url": "https://image.example/image.png",
|
|
},
|
|
}
|
|
mocker.patch.object(utils, "retrieve_ap_object", return_value=album)
|
|
|
|
serializer = serializers.ChannelUploadSerializer(
|
|
data=payload, context={"channel": channel}
|
|
)
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
serializer.save(channel=channel)
|
|
upload.refresh_from_db()
|
|
|
|
assert upload.library == channel.library
|
|
assert upload.import_status == "finished"
|
|
assert upload.creation_date == now
|
|
assert upload.fid == payload["id"]
|
|
assert upload.source == payload["url"][1]["href"]
|
|
assert upload.mimetype == payload["url"][1]["mediaType"]
|
|
assert upload.size == payload["url"][1]["size"]
|
|
assert upload.bitrate == payload["url"][1]["bitrate"]
|
|
assert upload.duration == payload["duration"]
|
|
assert upload.track.artist_credit.all()[0].artist == channel.artist
|
|
assert upload.track.position == payload["position"]
|
|
assert upload.track.disc_number == payload["disc"]
|
|
assert upload.track.attributed_to == channel.attributed_to
|
|
assert upload.track.title == payload["name"]
|
|
assert upload.track.creation_date == now
|
|
assert upload.track.description.content_type == payload["mediaType"]
|
|
assert upload.track.description.text == payload["content"]
|
|
assert upload.track.fid == payload["id"]
|
|
assert upload.track.license.pk == "cc0-1.0"
|
|
assert upload.track.copyright == payload["copyright"]
|
|
assert upload.track.get_tags() == ["Indie", "Punk", "Rock"]
|
|
assert upload.track.attachment_cover.mimetype == payload["image"]["mediaType"]
|
|
assert upload.track.attachment_cover.url == payload["image"]["url"]
|
|
assert upload.track.album == album
|
|
|
|
|
|
def test_channel_create_upload_serializer(factories):
|
|
channel = factories["audio.Channel"]()
|
|
upload = factories["music.Upload"](
|
|
playable=True, library=channel.library, import_status="finished"
|
|
)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Create",
|
|
"id": utils.full_url(
|
|
reverse("federation:music:uploads-activity", kwargs={"uuid": upload.uuid})
|
|
),
|
|
"actor": upload.library.channel.actor.fid,
|
|
"object": serializers.ChannelUploadSerializer(
|
|
upload, context={"include_ap_context": False}
|
|
).data,
|
|
}
|
|
|
|
serializer = serializers.ChannelCreateUploadSerializer(upload)
|
|
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_report_serializer_from_ap_create(factories, faker, now, mocker):
|
|
actor = factories["federation.Actor"]()
|
|
obj = factories["music.Artist"](local=True)
|
|
payload = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Flag",
|
|
"id": "https://test.report",
|
|
"actor": actor.fid,
|
|
"content": "hello world",
|
|
"object": [obj.fid],
|
|
"tag": [{"type": "Hashtag", "name": "#offensive_content"}],
|
|
}
|
|
serializer = serializers.FlagSerializer(data=payload, context={"actor": actor})
|
|
assert serializer.is_valid(raise_exception=True) is True
|
|
|
|
report = serializer.save()
|
|
|
|
assert report.fid == payload["id"]
|
|
assert report.summary == payload["content"]
|
|
assert report.submitter == actor
|
|
assert report.target == obj
|
|
assert report.target_state == moderation_serializers.get_target_state(obj)
|
|
assert report.target_owner == moderation_serializers.get_target_owner(obj)
|
|
assert report.type == "offensive_content"
|
|
|
|
|
|
def test_report_serializer_to_ap(factories):
|
|
report = factories["moderation.Report"](local=True)
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"type": "Flag",
|
|
"id": report.fid,
|
|
"actor": actors.get_service_actor().fid,
|
|
"content": report.summary,
|
|
"object": [report.target.fid],
|
|
"tag": [{"type": "Hashtag", "name": f"#{report.type}"}],
|
|
}
|
|
serializer = serializers.FlagSerializer(report)
|
|
assert serializer.data == expected
|
|
|
|
|
|
def test_artist_credit_serializer_to_ap(factories):
|
|
ac = factories["music.ArtistCredit"](artist__local=True)
|
|
serializer = serializers.ArtistCreditSerializer(ac)
|
|
|
|
expected = {
|
|
"@context": jsonld.get_default_context(),
|
|
"id": ac.get_federation_id(),
|
|
"type": "ArtistCredit",
|
|
"artist": serializers.ArtistSerializer(
|
|
ac.artist, context={"include_ap_context": False}
|
|
).data,
|
|
"joinphrase": ac.joinphrase,
|
|
"credit": ac.credit,
|
|
"index": ac.index,
|
|
"published": ac.creation_date.isoformat(),
|
|
}
|
|
|
|
assert serializer.data == expected
|