2062 lines
		
	
	
		
			73 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			2062 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)
 | |
|     factories["audio.Channel"](actor=follow.target)
 | |
| 
 | |
|     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
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     (
 | |
|         "followed_name",
 | |
|         "follow_factory",
 | |
|     ),
 | |
|     [("audio.Channel", "federation.Follow"), ("users.User", "federation.Follow")],
 | |
| )
 | |
| def test_undo_follow_serializer_save(factories, followed_name, follow_factory):
 | |
|     follow = factories[follow_factory](approved=True)
 | |
|     factories[followed_name](actor=follow.target)
 | |
| 
 | |
|     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()
 | |
|     if followed_name == "audio.Channel":
 | |
|         with pytest.raises(models.Follow.DoesNotExist):
 | |
|             follow.refresh_from_db()
 | |
|     else:
 | |
|         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,
 | |
|         "attributedTo": library.actor.fid,
 | |
|         "totalItems": 0,
 | |
|         "current": library.fid + "?page=1",
 | |
|         "last": library.fid + "?page=1",
 | |
|         "first": library.fid + "?page=1",
 | |
|     }
 | |
| 
 | |
|     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",
 | |
|         "type": "Library",
 | |
|         "id": "https://library.id",
 | |
|         "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"
 | |
| 
 | |
|     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",
 | |
|         "type": "Library",
 | |
|         "id": "https://library.id",
 | |
|         "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"
 | |
|     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",
 | |
|         "type": "Library",
 | |
|         "id": library.fid,
 | |
|         "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"
 | |
| 
 | |
| 
 | |
| 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
 |