Skip to content
Snippets Groups Projects
test_serializers.py 14 KiB
Newer Older
  • Learn to ignore specific revisions
  • from django.urls import reverse
    
    from django.core.paginator import Paginator
    
    from funkwhale_api.federation import actors
    
    from funkwhale_api.federation import keys
    
    from funkwhale_api.federation import models
    
    from funkwhale_api.federation import serializers
    
    from funkwhale_api.federation import utils
    
    def test_actor_serializer_from_ap(db):
        payload = {
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            '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',
            'url': 'https://test.federation/@user',
            'manuallyApprovesFollowers': False,
            'publicKey': {
                'id': 'https://test.federation/user#main-key',
                'owner': 'https://test.federation/user',
                'publicKeyPem': 'yolo'
            },
            'endpoints': {
                'sharedInbox': 'https://test.federation/inbox'
            },
    
        }
    
        serializer = serializers.ActorSerializer(data=payload)
    
        assert serializer.is_valid(raise_exception=True)
    
    
        actor = serializer.build()
    
        assert actor.url == payload['id']
        assert actor.inbox_url == payload['inbox']
        assert actor.outbox_url == payload['outbox']
        assert actor.shared_inbox_url == payload['endpoints']['sharedInbox']
        assert actor.followers_url == payload['followers']
        assert actor.following_url == payload['following']
        assert actor.public_key == payload['publicKey']['publicKeyPem']
        assert actor.preferred_username == payload['preferredUsername']
        assert actor.name == payload['name']
        assert actor.domain == 'test.federation'
        assert actor.summary == payload['summary']
        assert actor.type == 'Person'
        assert actor.manually_approves_followers == payload['manuallyApprovesFollowers']
    
    
    def test_actor_serializer_only_mandatory_field_from_ap(db):
        payload = {
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            '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.url == 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 == 'test.federation'
        assert actor.type == 'Person'
        assert actor.manually_approves_followers is None
    
    
    def test_actor_serializer_to_ap():
    
        expected = {
            '@context': [
                'https://www.w3.org/ns/activitystreams',
                'https://w3id.org/security/v1',
                {},
            ],
    
    Eliot Berriot's avatar
    Eliot Berriot committed
            '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(
            url=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='test.federation',
            summary=expected['summary'],
            type='Person',
            manually_approves_followers=False,
    
        )
        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(
            url=expected['links'][0]['href'],
            preferred_username='service',
            domain='test.federation',
        )
        serializer = serializers.ActorWebfingerSerializer(actor)
    
        assert serializer.data == expected
    
    Eliot Berriot's avatar
    Eliot Berriot committed
    
    
    def test_follow_serializer_to_ap(factories):
        follow = factories['federation.Follow'](local=True)
        serializer = serializers.FollowSerializer(follow)
    
        expected = {
            '@context': [
                'https://www.w3.org/ns/activitystreams',
                'https://w3id.org/security/v1',
                {},
            ],
            'id': follow.get_federation_url(),
            'type': 'Follow',
            'actor': follow.actor.url,
            'object': follow.target.url,
        }
    
        assert serializer.data == expected
    
    
    
    def test_paginated_collection_serializer(factories):
        tfs = factories['music.TrackFile'].create_batch(size=5)
        actor = factories['federation.Actor'](local=True)
    
        conf = {
            'id': 'https://test.federation/test',
            'items': tfs,
    
            'item_serializer': serializers.AudioSerializer,
    
            'actor': actor,
            'page_size': 2,
        }
        expected = {
            '@context': [
                'https://www.w3.org/ns/activitystreams',
                'https://w3id.org/security/v1',
                {},
            ],
            'type': 'Collection',
            'id': conf['id'],
            'actor': actor.url,
            'totalItems': len(tfs),
            '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 = {
            'type': 'Collection',
            'id': 'https://test.federation/test',
            'totalItems': 5,
            'actor': 'http://test.actor',
            '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']
        assert serializer.validated_data['actor'] == data['actor']
    
    
    
    Eliot Berriot's avatar
    Eliot Berriot committed
    def test_collection_page_serializer_validation():
    
        base = 'https://test.federation/test'
        data = {
            'type': 'CollectionPage',
            'id': base + '?page=2',
            'totalItems': 5,
            'actor': 'https://test.actor',
            'items': [],
            '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['actor'] == 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(factories):
        tfs = factories['music.TrackFile'].create_batch(size=5)
        actor = factories['federation.Actor'](local=True)
    
        conf = {
            'id': 'https://test.federation/test',
    
            'item_serializer': serializers.AudioSerializer,
    
            'actor': actor,
            'page': Paginator(tfs, 2).page(2),
        }
        expected = {
            '@context': [
                'https://www.w3.org/ns/activitystreams',
                'https://w3id.org/security/v1',
                {},
            ],
            'type': 'CollectionPage',
            'id': conf['id'] + '?page=2',
            'actor': actor.url,
            'totalItems': len(tfs),
            '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_activity_pub_audio_serializer_to_library_track(factories):
        remote_library = factories['federation.Library']()
        audio = factories['federation.Audio']()
        serializer = serializers.AudioSerializer(
            data=audio, context={'library': remote_library})
    
        assert serializer.is_valid(raise_exception=True)
    
        lt = serializer.save()
    
        assert lt.pk is not None
        assert lt.url == audio['id']
        assert lt.library == remote_library
        assert lt.audio_url == audio['url']['href']
        assert lt.audio_mimetype == audio['url']['mediaType']
        assert lt.metadata == audio['metadata']
        assert lt.title == audio['metadata']['recording']['title']
        assert lt.artist_name == audio['metadata']['artist']['name']
        assert lt.album_title == audio['metadata']['release']['title']
        assert lt.published_date == arrow.get(audio['published'])
    
    
    
    def test_activity_pub_audio_serializer_to_library_track_no_duplicate(
            factories):
        remote_library = factories['federation.Library']()
        audio = factories['federation.Audio']()
        serializer1 = serializers.AudioSerializer(
            data=audio, context={'library': remote_library})
        serializer2 = serializers.AudioSerializer(
            data=audio, context={'library': remote_library})
    
        assert serializer1.is_valid() is True
        assert serializer2.is_valid() is True
    
        lt1 = serializer1.save()
        lt2 = serializer2.save()
    
        assert lt1 == lt2
        assert models.LibraryTrack.objects.count() == 1
    
    
    
    def test_activity_pub_audio_serializer_to_ap(factories):
        tf = factories['music.TrackFile'](mimetype='audio/mp3')
        library = actors.SYSTEM_ACTORS['library'].get_actor_instance()
        expected = {
            '@context': serializers.AP_CONTEXT,
            'type': 'Audio',
            'id': tf.get_federation_url(),
            'name': tf.track.full_name,
            'published': tf.creation_date.isoformat(),
            'updated': tf.modification_date.isoformat(),
            'metadata': {
                'artist': {
                    'musicbrainz_id': tf.track.artist.mbid,
                    'name': tf.track.artist.name,
                },
                'release': {
                    'musicbrainz_id': tf.track.album.mbid,
                    'title': tf.track.album.title,
                },
                'recording': {
                    'musicbrainz_id': tf.track.mbid,
                    'title': tf.track.title,
                },
            },
            'url': {
                'href': utils.full_url(tf.path),
                'type': 'Link',
                'mediaType': 'audio/mp3'
            },
            'attributedTo': [
                library.url
            ]
        }
    
        serializer = serializers.AudioSerializer(tf, context={'actor': library})
    
        assert serializer.data == expected
    
    
    def test_activity_pub_audio_serializer_to_ap_no_mbid(factories):
        tf = factories['music.TrackFile'](
            mimetype='audio/mp3',
            track__mbid=None,
            track__album__mbid=None,
            track__album__artist__mbid=None,
        )
        library = actors.SYSTEM_ACTORS['library'].get_actor_instance()
        expected = {
            '@context': serializers.AP_CONTEXT,
            'type': 'Audio',
            'id': tf.get_federation_url(),
            'name': tf.track.full_name,
            'published': tf.creation_date.isoformat(),
            'updated': tf.modification_date.isoformat(),
            'metadata': {
                'artist': {
                    'name': tf.track.artist.name,
                    'musicbrainz_id': None,
                },
                'release': {
                    'title': tf.track.album.title,
                    'musicbrainz_id': None,
                },
                'recording': {
                    'title': tf.track.title,
                    'musicbrainz_id': None,
                },
            },
            'url': {
                'href': utils.full_url(tf.path),
                'type': 'Link',
                'mediaType': 'audio/mp3'
            },
            'attributedTo': [
                library.url
            ]
        }
    
        serializer = serializers.AudioSerializer(tf, context={'actor': library})
    
        assert serializer.data == expected
    
    
    def test_collection_serializer_to_ap(factories):
        tf1 = factories['music.TrackFile'](mimetype='audio/mp3')
        tf2 = factories['music.TrackFile'](mimetype='audio/ogg')
        library = actors.SYSTEM_ACTORS['library'].get_actor_instance()
        expected = {
            '@context': serializers.AP_CONTEXT,
            'id': 'https://test.id',
            'actor': library.url,
            'totalItems': 2,
            'type': 'Collection',
            'items': [
                serializers.AudioSerializer(
                    tf1, context={'actor': library, 'include_ap_context': False}
                ).data,
                serializers.AudioSerializer(
                    tf2, context={'actor': library, 'include_ap_context': False}
                ).data,
            ]
        }
    
        collection = {
            'id': expected['id'],
            'actor': library,
            'items': [tf1, tf2],
            'item_serializer': serializers.AudioSerializer
        }
        serializer = serializers.CollectionSerializer(
            collection, context={'actor': library, 'id': 'https://test.id'})
    
        assert serializer.data == expected