funkwhale/api/tests/federation/test_actors.py

371 wiersze
12 KiB
Python
Czysty Zwykły widok Historia

import pytest
2018-04-03 17:48:50 +00:00
import uuid
from django.urls import reverse
from django.utils import timezone
from rest_framework import exceptions
from funkwhale_api.federation import actors
from funkwhale_api.federation import models
from funkwhale_api.federation import serializers
2018-03-31 13:57:29 +00:00
from funkwhale_api.federation import utils
def test_actor_fetching(r_mock):
payload = {
'id': 'https://actor.mock/users/actor#main-key',
'owner': 'test',
'publicKeyPem': 'test_pem',
}
actor_url = 'https://actor.mock/'
r_mock.get(actor_url, json=payload)
r = actors.get_actor_data(actor_url)
assert r == payload
def test_get_library(settings, preferences):
preferences['federation__public_key'] = 'public_key'
expected = {
'preferred_username': 'library',
'domain': settings.FEDERATION_HOSTNAME,
'type': 'Person',
'name': '{}\'s library'.format(settings.FEDERATION_HOSTNAME),
'manually_approves_followers': True,
2018-03-31 13:57:29 +00:00
'url': utils.full_url(
reverse(
'federation:instance-actors-detail',
kwargs={'actor': 'library'})),
'shared_inbox_url': utils.full_url(
reverse(
'federation:instance-actors-inbox',
kwargs={'actor': 'library'})),
'inbox_url': utils.full_url(
reverse(
'federation:instance-actors-inbox',
kwargs={'actor': 'library'})),
'outbox_url': utils.full_url(
reverse(
'federation:instance-actors-outbox',
kwargs={'actor': 'library'})),
'public_key': 'public_key',
'summary': 'Bot account to federate with {}\'s library'.format(
settings.FEDERATION_HOSTNAME),
}
actor = actors.SYSTEM_ACTORS['library'].get_actor_instance()
for key, value in expected.items():
assert getattr(actor, key) == value
def test_get_test(settings, preferences):
preferences['federation__public_key'] = 'public_key'
expected = {
'preferred_username': 'test',
'domain': settings.FEDERATION_HOSTNAME,
'type': 'Person',
'name': '{}\'s test account'.format(settings.FEDERATION_HOSTNAME),
'manually_approves_followers': False,
'url': utils.full_url(
reverse(
'federation:instance-actors-detail',
kwargs={'actor': 'test'})),
'shared_inbox_url': utils.full_url(
reverse(
'federation:instance-actors-inbox',
kwargs={'actor': 'test'})),
'inbox_url': utils.full_url(
reverse(
'federation:instance-actors-inbox',
kwargs={'actor': 'test'})),
'outbox_url': utils.full_url(
reverse(
'federation:instance-actors-outbox',
kwargs={'actor': 'test'})),
'public_key': 'public_key',
'summary': 'Bot account to test federation with {}. Send me /ping and I\'ll answer you.'.format(
settings.FEDERATION_HOSTNAME),
}
actor = actors.SYSTEM_ACTORS['test'].get_actor_instance()
for key, value in expected.items():
assert getattr(actor, key) == value
def test_test_get_outbox():
expected = {
"@context": [
"https://www.w3.org/ns/activitystreams",
"https://w3id.org/security/v1",
{}
],
"id": utils.full_url(
reverse(
'federation:instance-actors-outbox',
kwargs={'actor': 'test'})),
"type": "OrderedCollection",
"totalItems": 0,
"orderedItems": []
}
data = actors.SYSTEM_ACTORS['test'].get_outbox({}, actor=None)
assert data == expected
def test_test_post_inbox_requires_authenticated_actor():
with pytest.raises(exceptions.PermissionDenied):
actors.SYSTEM_ACTORS['test'].post_inbox({}, actor=None)
def test_test_post_outbox_validates_actor(nodb_factories):
actor = nodb_factories['federation.Actor']()
data = {
'actor': 'noop'
}
with pytest.raises(exceptions.ValidationError) as exc_info:
actors.SYSTEM_ACTORS['test'].post_inbox(data, actor=actor)
msg = 'The actor making the request do not match'
assert msg in exc_info.value
2018-04-03 17:48:50 +00:00
def test_test_post_inbox_handles_create_note(
2018-04-02 17:15:27 +00:00
settings, mocker, factories):
deliver = mocker.patch(
'funkwhale_api.federation.activity.deliver')
actor = factories['federation.Actor']()
now = timezone.now()
mocker.patch('django.utils.timezone.now', return_value=now)
data = {
'actor': actor.url,
'type': 'Create',
'id': 'http://test.federation/activity',
'object': {
'type': 'Note',
'id': 'http://test.federation/object',
'content': '<p><a>@mention</a> /ping</p>'
}
}
2018-04-02 17:15:27 +00:00
test_actor = actors.SYSTEM_ACTORS['test'].get_actor_instance()
expected_note = factories['federation.Note'](
id='https://test.federation/activities/note/{}'.format(
now.timestamp()
),
content='Pong!',
published=now.isoformat(),
inReplyTo=data['object']['id'],
2018-04-02 17:15:27 +00:00
cc=[],
summary=None,
sensitive=False,
attributedTo=test_actor.url,
attachment=[],
to=[actor.url],
url='https://{}/activities/note/{}'.format(
settings.FEDERATION_HOSTNAME, now.timestamp()
),
tag=[{
'href': actor.url,
'name': actor.mention_username,
'type': 'Mention',
}]
)
expected_activity = {
2018-04-03 21:25:44 +00:00
'@context': serializers.AP_CONTEXT,
'actor': test_actor.url,
2018-04-02 17:15:27 +00:00
'id': 'https://{}/activities/note/{}/activity'.format(
settings.FEDERATION_HOSTNAME, now.timestamp()
),
2018-04-02 17:15:27 +00:00
'to': actor.url,
'type': 'Create',
'published': now.isoformat(),
2018-04-02 17:15:27 +00:00
'object': expected_note,
'cc': [],
}
actors.SYSTEM_ACTORS['test'].post_inbox(data, actor=actor)
deliver.assert_called_once_with(
expected_activity,
to=[actor.url],
on_behalf_of=actors.SYSTEM_ACTORS['test'].get_actor_instance()
)
def test_getting_actor_instance_persists_in_db(db):
test = actors.SYSTEM_ACTORS['test'].get_actor_instance()
from_db = models.Actor.objects.get(url=test.url)
for f in test._meta.fields:
assert getattr(from_db, f.name) == getattr(test, f.name)
@pytest.mark.parametrize('username,domain,expected', [
('test', 'wrongdomain.com', False),
('notsystem', '', False),
('test', '', True),
])
def test_actor_is_system(
username, domain, expected, nodb_factories, settings):
if not domain:
domain = settings.FEDERATION_HOSTNAME
actor = nodb_factories['federation.Actor'](
preferred_username=username,
domain=domain,
)
assert actor.is_system is expected
@pytest.mark.parametrize('username,domain,expected', [
('test', 'wrongdomain.com', None),
('notsystem', '', None),
('test', '', actors.SYSTEM_ACTORS['test']),
])
def test_actor_is_system(
username, domain, expected, nodb_factories, settings):
if not domain:
domain = settings.FEDERATION_HOSTNAME
actor = nodb_factories['federation.Actor'](
preferred_username=username,
domain=domain,
)
assert actor.system_conf == expected
@pytest.mark.parametrize('value', [False, True])
def test_library_actor_manually_approves_based_on_setting(
value, settings):
settings.FEDERATION_MUSIC_NEEDS_APPROVAL = value
library_conf = actors.SYSTEM_ACTORS['library']
assert library_conf.manually_approves_followers is value
2018-04-03 17:48:50 +00:00
def test_system_actor_handle(mocker, nodb_factories):
handler = mocker.patch(
'funkwhale_api.federation.actors.TestActor.handle_create')
actor = nodb_factories['federation.Actor']()
activity = nodb_factories['federation.Activity'](
type='Create', actor=actor.url)
serializer = serializers.ActivitySerializer(
data=activity
)
assert serializer.is_valid()
actors.SYSTEM_ACTORS['test'].handle(activity, actor)
handler.assert_called_once_with(activity, actor)
2018-04-03 17:48:50 +00:00
def test_test_actor_handles_follow(
settings, mocker, factories):
deliver = mocker.patch(
'funkwhale_api.federation.activity.deliver')
actor = factories['federation.Actor']()
now = timezone.now()
mocker.patch('django.utils.timezone.now', return_value=now)
accept_follow = mocker.patch(
'funkwhale_api.federation.activity.accept_follow')
2018-04-03 17:48:50 +00:00
test_actor = actors.SYSTEM_ACTORS['test'].get_actor_instance()
data = {
'actor': actor.url,
'type': 'Follow',
'id': 'http://test.federation/user#follows/267',
'object': test_actor.url,
}
uid = uuid.uuid4()
mocker.patch('uuid.uuid4', return_value=uid)
expected_follow = {
2018-04-03 21:25:44 +00:00
'@context': serializers.AP_CONTEXT,
2018-04-03 17:48:50 +00:00
'actor': test_actor.url,
'id': test_actor.url + '#follows/{}'.format(uid),
'object': actor.url,
'type': 'Follow'
}
actors.SYSTEM_ACTORS['test'].post_inbox(data, actor=actor)
accept_follow.assert_called_once_with(
test_actor, data, actor
)
2018-04-03 17:48:50 +00:00
expected_calls = [
mocker.call(
expected_follow,
to=[actor.url],
on_behalf_of=test_actor,
)
]
deliver.assert_has_calls(expected_calls)
2018-04-03 19:30:15 +00:00
2018-04-03 21:25:44 +00:00
def test_test_actor_handles_undo_follow(
settings, mocker, factories):
deliver = mocker.patch(
'funkwhale_api.federation.activity.deliver')
test_actor = actors.SYSTEM_ACTORS['test'].get_actor_instance()
follow = factories['federation.Follow'](target=test_actor)
reverse_follow = factories['federation.Follow'](
actor=test_actor, target=follow.actor)
follow_serializer = serializers.FollowSerializer(follow)
reverse_follow_serializer = serializers.FollowSerializer(
reverse_follow)
undo = {
'@context': serializers.AP_CONTEXT,
'type': 'Undo',
'id': follow_serializer.data['id'] + '/undo',
'actor': follow.actor.url,
'object': follow_serializer.data,
}
expected_undo = {
'@context': serializers.AP_CONTEXT,
'type': 'Undo',
'id': reverse_follow_serializer.data['id'] + '/undo',
'actor': reverse_follow.actor.url,
'object': reverse_follow_serializer.data,
}
actors.SYSTEM_ACTORS['test'].post_inbox(undo, actor=follow.actor)
deliver.assert_called_once_with(
expected_undo,
to=[follow.actor.url],
on_behalf_of=test_actor,)
assert models.Follow.objects.count() == 0
def test_library_actor_handles_follow_manual_approval(
settings, mocker, factories):
settings.FEDERATION_MUSIC_NEEDS_APPROVAL = True
actor = factories['federation.Actor']()
now = timezone.now()
mocker.patch('django.utils.timezone.now', return_value=now)
library_actor = actors.SYSTEM_ACTORS['library'].get_actor_instance()
data = {
'actor': actor.url,
'type': 'Follow',
'id': 'http://test.federation/user#follows/267',
'object': library_actor.url,
}
library_actor.system_conf.post_inbox(data, actor=actor)
fr = library_actor.received_follow_requests.first()
assert library_actor.received_follow_requests.count() == 1
assert fr.target == library_actor
assert fr.actor == actor
assert fr.approved is None
def test_library_actor_handles_follow_auto_approval(
settings, mocker, factories):
2018-04-04 21:12:41 +00:00
settings.FEDERATION_MUSIC_NEEDS_APPROVAL = False
actor = factories['federation.Actor']()
accept_follow = mocker.patch(
'funkwhale_api.federation.activity.accept_follow')
library_actor = actors.SYSTEM_ACTORS['library'].get_actor_instance()
data = {
'actor': actor.url,
'type': 'Follow',
'id': 'http://test.federation/user#follows/267',
'object': library_actor.url,
}
library_actor.system_conf.post_inbox(data, actor=actor)
2018-04-04 21:12:41 +00:00
assert library_actor.received_follow_requests.count() == 0
accept_follow.assert_called_once_with(
library_actor, data, actor
)