mirror of
https://code.eliotberriot.com/funkwhale/funkwhale.git
synced 2025-10-06 03:50:06 +02:00
Audio federation
This commit is contained in:
parent
6992c567fb
commit
e49a460203
85 changed files with 2598 additions and 1204 deletions
|
@ -1,21 +1,31 @@
|
|||
|
||||
import pytest
|
||||
import uuid
|
||||
|
||||
from funkwhale_api.federation import activity, api_serializers, serializers, tasks
|
||||
from django.db.models import Q
|
||||
from django.urls import reverse
|
||||
|
||||
from funkwhale_api.federation import (
|
||||
activity,
|
||||
models,
|
||||
api_serializers,
|
||||
serializers,
|
||||
tasks,
|
||||
)
|
||||
|
||||
|
||||
def test_receive_validates_basic_attributes_and_stores_activity(factories, now, mocker):
|
||||
mocked_dispatch = mocker.patch("funkwhale_api.common.utils.on_commit")
|
||||
local_actor = factories["users.User"]().create_actor()
|
||||
local_to_actor = factories["users.User"]().create_actor()
|
||||
local_cc_actor = factories["users.User"]().create_actor()
|
||||
remote_actor = factories["federation.Actor"]()
|
||||
another_actor = factories["federation.Actor"]()
|
||||
a = {
|
||||
"@context": [],
|
||||
"actor": remote_actor.fid,
|
||||
"type": "Noop",
|
||||
"id": "https://test.activity",
|
||||
"to": [local_actor.fid],
|
||||
"cc": [another_actor.fid, activity.PUBLIC_ADDRESS],
|
||||
"to": [local_to_actor.fid, remote_actor.fid],
|
||||
"cc": [local_cc_actor.fid, activity.PUBLIC_ADDRESS],
|
||||
}
|
||||
|
||||
copy = activity.receive(activity=a, on_behalf_of=remote_actor)
|
||||
|
@ -29,8 +39,60 @@ def test_receive_validates_basic_attributes_and_stores_activity(factories, now,
|
|||
tasks.dispatch_inbox.delay, activity_id=copy.pk
|
||||
)
|
||||
|
||||
inbox_item = copy.inbox_items.get(actor__fid=local_actor.fid)
|
||||
assert inbox_item.is_delivered is False
|
||||
assert models.InboxItem.objects.count() == 2
|
||||
for actor, t in [(local_to_actor, "to"), (local_cc_actor, "cc")]:
|
||||
ii = models.InboxItem.objects.get(actor=actor)
|
||||
assert ii.type == t
|
||||
assert ii.activity == copy
|
||||
assert ii.is_read is False
|
||||
|
||||
|
||||
def test_get_actors_from_audience_urls(settings, db):
|
||||
settings.FEDERATION_HOSTNAME = "federation.hostname"
|
||||
library_uuid1 = uuid.uuid4()
|
||||
library_uuid2 = uuid.uuid4()
|
||||
|
||||
urls = [
|
||||
"https://wrong.url",
|
||||
"https://federation.hostname"
|
||||
+ reverse("federation:actors-detail", kwargs={"preferred_username": "kevin"}),
|
||||
"https://federation.hostname"
|
||||
+ reverse("federation:actors-detail", kwargs={"preferred_username": "alice"}),
|
||||
"https://federation.hostname"
|
||||
+ reverse("federation:actors-detail", kwargs={"preferred_username": "bob"}),
|
||||
"https://federation.hostname"
|
||||
+ reverse("federation:music:libraries-detail", kwargs={"uuid": library_uuid1}),
|
||||
"https://federation.hostname"
|
||||
+ reverse("federation:music:libraries-detail", kwargs={"uuid": library_uuid2}),
|
||||
activity.PUBLIC_ADDRESS,
|
||||
]
|
||||
followed_query = Q(target__followers_url=urls[0])
|
||||
for url in urls[1:-1]:
|
||||
followed_query |= Q(target__followers_url=url)
|
||||
actor_follows = models.Follow.objects.filter(followed_query, approved=True)
|
||||
library_follows = models.LibraryFollow.objects.filter(followed_query, approved=True)
|
||||
expected = models.Actor.objects.filter(
|
||||
Q(fid__in=urls[0:-1])
|
||||
| Q(pk__in=actor_follows.values_list("actor", flat=True))
|
||||
| Q(pk__in=library_follows.values_list("actor", flat=True))
|
||||
)
|
||||
assert str(activity.get_actors_from_audience(urls).query) == str(expected.query)
|
||||
|
||||
|
||||
def test_get_inbox_urls(factories):
|
||||
a1 = factories["federation.Actor"](
|
||||
shared_inbox_url=None, inbox_url="https://a1.inbox"
|
||||
)
|
||||
a2 = factories["federation.Actor"](
|
||||
shared_inbox_url="https://shared.inbox", inbox_url="https://a2.inbox"
|
||||
)
|
||||
factories["federation.Actor"](
|
||||
shared_inbox_url="https://shared.inbox", inbox_url="https://a3.inbox"
|
||||
)
|
||||
|
||||
expected = sorted(set([a1.inbox_url, a2.shared_inbox_url]))
|
||||
|
||||
assert activity.get_inbox_urls(a1.__class__.objects.all()) == expected
|
||||
|
||||
|
||||
def test_receive_invalid_data(factories):
|
||||
|
@ -97,8 +159,6 @@ def test_inbox_routing_send_to_channel(factories, mocker):
|
|||
|
||||
ii.refresh_from_db()
|
||||
|
||||
assert ii.is_delivered is True
|
||||
|
||||
group_send.assert_called_once_with(
|
||||
"user.{}.inbox".format(ii.actor.user.pk),
|
||||
{
|
||||
|
@ -118,6 +178,16 @@ def test_inbox_routing_send_to_channel(factories, mocker):
|
|||
({"type": "Follow"}, {"type": "Follow"}, True),
|
||||
({"type": "Follow"}, {"type": "Noop"}, False),
|
||||
({"type": "Follow"}, {"type": "Follow", "id": "https://hello"}, True),
|
||||
(
|
||||
{"type": "Create", "object.type": "Audio"},
|
||||
{"type": "Create", "object": {"type": "Note"}},
|
||||
False,
|
||||
),
|
||||
(
|
||||
{"type": "Create", "object.type": "Audio"},
|
||||
{"type": "Create", "object": {"type": "Audio"}},
|
||||
True,
|
||||
),
|
||||
],
|
||||
)
|
||||
def test_route_matching(route, payload, expected):
|
||||
|
@ -126,7 +196,6 @@ def test_route_matching(route, payload, expected):
|
|||
|
||||
def test_outbox_router_dispatch(mocker, factories, now):
|
||||
router = activity.OutboxRouter()
|
||||
recipient = factories["federation.Actor"]()
|
||||
actor = factories["federation.Actor"]()
|
||||
r1 = factories["federation.Actor"]()
|
||||
r2 = factories["federation.Actor"]()
|
||||
|
@ -144,6 +213,9 @@ def test_outbox_router_dispatch(mocker, factories, now):
|
|||
"actor": actor,
|
||||
}
|
||||
|
||||
expected_deliveries_url = activity.get_inbox_urls(
|
||||
models.Actor.objects.filter(pk__in=[r1.pk, r2.pk])
|
||||
)
|
||||
router.connect({"type": "Noop"}, handler)
|
||||
activities = router.dispatch({"type": "Noop"}, {"summary": "hello"})
|
||||
a = activities[0]
|
||||
|
@ -163,9 +235,112 @@ def test_outbox_router_dispatch(mocker, factories, now):
|
|||
assert a.creation_date >= now
|
||||
assert a.uuid is not None
|
||||
|
||||
for recipient, type in [(r1, "to"), (r2, "cc")]:
|
||||
item = a.inbox_items.get(actor=recipient)
|
||||
assert item.is_delivered is False
|
||||
assert item.last_delivery_date is None
|
||||
assert item.delivery_attempts == 0
|
||||
assert item.type == type
|
||||
assert a.deliveries.count() == 2
|
||||
for url in expected_deliveries_url:
|
||||
delivery = a.deliveries.get(inbox_url=url)
|
||||
assert delivery.is_delivered is False
|
||||
|
||||
|
||||
def test_prepare_deliveries_and_inbox_items(factories):
|
||||
local_actor1 = factories["federation.Actor"](
|
||||
local=True, shared_inbox_url="https://testlocal.inbox"
|
||||
)
|
||||
local_actor2 = factories["federation.Actor"](
|
||||
local=True, shared_inbox_url=local_actor1.shared_inbox_url
|
||||
)
|
||||
local_actor3 = factories["federation.Actor"](local=True, shared_inbox_url=None)
|
||||
|
||||
remote_actor1 = factories["federation.Actor"](
|
||||
shared_inbox_url="https://testremote.inbox"
|
||||
)
|
||||
remote_actor2 = factories["federation.Actor"](
|
||||
shared_inbox_url=remote_actor1.shared_inbox_url
|
||||
)
|
||||
remote_actor3 = factories["federation.Actor"](shared_inbox_url=None)
|
||||
|
||||
library = factories["music.Library"]()
|
||||
library_follower_local = factories["federation.LibraryFollow"](
|
||||
target=library, actor__local=True, approved=True
|
||||
).actor
|
||||
library_follower_remote = factories["federation.LibraryFollow"](
|
||||
target=library, actor__local=False, approved=True
|
||||
).actor
|
||||
# follow not approved
|
||||
factories["federation.LibraryFollow"](
|
||||
target=library, actor__local=False, approved=False
|
||||
)
|
||||
|
||||
followed_actor = factories["federation.Actor"]()
|
||||
actor_follower_local = factories["federation.Follow"](
|
||||
target=followed_actor, actor__local=True, approved=True
|
||||
).actor
|
||||
actor_follower_remote = factories["federation.Follow"](
|
||||
target=followed_actor, actor__local=False, approved=True
|
||||
).actor
|
||||
# follow not approved
|
||||
factories["federation.Follow"](
|
||||
target=followed_actor, actor__local=False, approved=False
|
||||
)
|
||||
|
||||
recipients = [
|
||||
local_actor1,
|
||||
local_actor2,
|
||||
local_actor3,
|
||||
remote_actor1,
|
||||
remote_actor2,
|
||||
remote_actor3,
|
||||
activity.PUBLIC_ADDRESS,
|
||||
{"type": "followers", "target": library},
|
||||
{"type": "followers", "target": followed_actor},
|
||||
]
|
||||
|
||||
inbox_items, deliveries, urls = activity.prepare_deliveries_and_inbox_items(
|
||||
recipients, "to"
|
||||
)
|
||||
expected_inbox_items = sorted(
|
||||
[
|
||||
models.InboxItem(actor=local_actor1, type="to"),
|
||||
models.InboxItem(actor=local_actor2, type="to"),
|
||||
models.InboxItem(actor=local_actor3, type="to"),
|
||||
models.InboxItem(actor=library_follower_local, type="to"),
|
||||
models.InboxItem(actor=actor_follower_local, type="to"),
|
||||
],
|
||||
key=lambda v: v.actor.pk,
|
||||
)
|
||||
|
||||
expected_deliveries = sorted(
|
||||
[
|
||||
models.Delivery(inbox_url=remote_actor1.shared_inbox_url),
|
||||
models.Delivery(inbox_url=remote_actor3.inbox_url),
|
||||
models.Delivery(inbox_url=library_follower_remote.inbox_url),
|
||||
models.Delivery(inbox_url=actor_follower_remote.inbox_url),
|
||||
],
|
||||
key=lambda v: v.inbox_url,
|
||||
)
|
||||
|
||||
expected_urls = [
|
||||
local_actor1.fid,
|
||||
local_actor2.fid,
|
||||
local_actor3.fid,
|
||||
remote_actor1.fid,
|
||||
remote_actor2.fid,
|
||||
remote_actor3.fid,
|
||||
activity.PUBLIC_ADDRESS,
|
||||
library.followers_url,
|
||||
followed_actor.followers_url,
|
||||
]
|
||||
|
||||
assert urls == expected_urls
|
||||
assert len(expected_inbox_items) == len(inbox_items)
|
||||
assert len(expected_deliveries) == len(deliveries)
|
||||
|
||||
for delivery, expected_delivery in zip(
|
||||
sorted(deliveries, key=lambda v: v.inbox_url), expected_deliveries
|
||||
):
|
||||
assert delivery.inbox_url == expected_delivery.inbox_url
|
||||
|
||||
for inbox_item, expected_inbox_item in zip(
|
||||
sorted(inbox_items, key=lambda v: v.actor.pk), expected_inbox_items
|
||||
):
|
||||
assert inbox_item.actor == expected_inbox_item.actor
|
||||
assert inbox_item.type == "to"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue