See #170: Funkwhale federation

This commit is contained in:
Eliot Berriot 2020-03-25 15:32:10 +01:00
commit 9aa12db62e
20 changed files with 3719 additions and 122 deletions

View file

@ -148,19 +148,17 @@ def test_channel_delete(logged_in_api_client, factories, mocker):
channel = factories["audio.Channel"](attributed_to=actor)
url = reverse("api:v1:channels-detail", kwargs={"composite": channel.uuid})
dispatch = mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
on_commit = mocker.patch("funkwhale_api.common.utils.on_commit")
response = logged_in_api_client.delete(url)
assert response.status_code == 204
on_commit.assert_called_once_with(
views.federation_tasks.remove_actor.delay, actor_id=channel.actor.pk
)
with pytest.raises(channel.DoesNotExist):
channel.refresh_from_db()
dispatch.assert_called_once_with(
{"type": "Delete", "object": {"type": channel.actor.type}},
context={"actor": channel.actor},
)
def test_channel_delete_permission(logged_in_api_client, factories):
logged_in_api_client.user.create_actor()
@ -218,6 +216,38 @@ def test_channel_unsubscribe(factories, logged_in_api_client):
subscription.refresh_from_db()
def test_channel_subscribe_remote(factories, logged_in_api_client, mocker):
dispatch = mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
actor = logged_in_api_client.user.create_actor()
channel_actor = factories["federation.Actor"]()
channel = factories["audio.Channel"](artist__description=None, actor=channel_actor)
url = reverse("api:v1:channels-subscribe", kwargs={"composite": channel.uuid})
response = logged_in_api_client.post(url)
assert response.status_code == 201
subscription = actor.emitted_follows.latest("id")
dispatch.assert_called_once_with(
{"type": "Follow"}, context={"follow": subscription}
)
def test_channel_unsubscribe_remote(factories, logged_in_api_client, mocker):
dispatch = mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
actor = logged_in_api_client.user.create_actor()
channel_actor = factories["federation.Actor"]()
channel = factories["audio.Channel"](actor=channel_actor)
subscription = factories["audio.Subscription"](target=channel.actor, actor=actor)
url = reverse("api:v1:channels-unsubscribe", kwargs={"composite": channel.uuid})
response = logged_in_api_client.post(url)
assert response.status_code == 204
dispatch.assert_called_once_with(
{"type": "Undo", "object": {"type": "Follow"}}, context={"follow": subscription}
)
def test_subscriptions_list(factories, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
channel = factories["audio.Channel"](

View file

@ -167,6 +167,7 @@ def test_fetch_serializer_no_obj(factories, to_api_date):
("music.Track", "track", "id"),
("music.Library", "library", "uuid"),
("music.Upload", "upload", "uuid"),
("audio.Channel", "channel", "uuid"),
("federation.Actor", "account", "full_username"),
],
)

View file

@ -26,6 +26,7 @@ from funkwhale_api.moderation import serializers as moderation_serializers
routes.inbox_delete_library,
),
({"type": "Delete", "object": {"type": "Audio"}}, routes.inbox_delete_audio),
({"type": "Delete", "object": {"type": "Album"}}, routes.inbox_delete_album),
({"type": "Undo", "object": {"type": "Follow"}}, routes.inbox_undo_follow),
({"type": "Update", "object": {"type": "Artist"}}, routes.inbox_update_artist),
({"type": "Update", "object": {"type": "Album"}}, routes.inbox_update_album),
@ -58,6 +59,7 @@ def test_inbox_routes(route, handler):
routes.outbox_delete_library,
),
({"type": "Delete", "object": {"type": "Audio"}}, routes.outbox_delete_audio),
({"type": "Delete", "object": {"type": "Album"}}, routes.outbox_delete_album),
({"type": "Undo", "object": {"type": "Follow"}}, routes.outbox_undo_follow),
({"type": "Update", "object": {"type": "Track"}}, routes.outbox_update_track),
(
@ -349,6 +351,34 @@ def test_inbox_create_audio(factories, mocker):
assert save.call_count == 1
def test_inbox_create_audio_channel(factories, mocker):
activity = factories["federation.Activity"]()
channel = factories["audio.Channel"]()
album = factories["music.Album"](artist=channel.artist)
upload = factories["music.Upload"](track__album=album, library=channel.library,)
payload = {
"@context": jsonld.get_default_context(),
"type": "Create",
"actor": channel.actor.fid,
"object": serializers.ChannelUploadSerializer(upload).data,
}
upload.delete()
init = mocker.spy(serializers.ChannelUploadSerializer, "__init__")
save = mocker.spy(serializers.ChannelUploadSerializer, "save")
result = routes.inbox_create_audio(
payload,
context={"actor": channel.actor, "raise_exception": True, "activity": activity},
)
assert channel.library.uploads.count() == 1
assert result == {"object": channel.library.uploads.latest("id"), "target": channel}
assert init.call_count == 1
args = init.call_args
assert args[1]["data"] == payload["object"]
assert args[1]["context"] == {"channel": channel}
assert save.call_count == 1
def test_inbox_delete_library(factories):
activity = factories["federation.Activity"]()
@ -368,6 +398,73 @@ def test_inbox_delete_library(factories):
library.refresh_from_db()
def test_inbox_delete_album(factories):
album = factories["music.Album"](attributed=True)
payload = {
"type": "Delete",
"actor": album.attributed_to.fid,
"object": {"type": "Album", "id": album.fid},
}
routes.inbox_delete_album(
payload,
context={
"actor": album.attributed_to,
"raise_exception": True,
"activity": activity,
},
)
with pytest.raises(album.__class__.DoesNotExist):
album.refresh_from_db()
def test_inbox_delete_album_channel(factories):
channel = factories["audio.Channel"]()
album = factories["music.Album"](artist=channel.artist)
payload = {
"type": "Delete",
"actor": channel.actor.fid,
"object": {"type": "Album", "id": album.fid},
}
routes.inbox_delete_album(
payload,
context={"actor": channel.actor, "raise_exception": True, "activity": activity},
)
with pytest.raises(album.__class__.DoesNotExist):
album.refresh_from_db()
def test_outbox_delete_album(factories):
album = factories["music.Album"](attributed=True)
a = list(routes.outbox_delete_album({"album": album}))[0]
expected = serializers.ActivitySerializer(
{"type": "Delete", "object": {"type": "Album", "id": album.fid}}
).data
expected["to"] = [activity.PUBLIC_ADDRESS, {"type": "instances_with_followers"}]
assert dict(a["payload"]) == dict(expected)
assert a["actor"] == album.attributed_to
def test_outbox_delete_album_channel(factories):
channel = factories["audio.Channel"]()
album = factories["music.Album"](artist=channel.artist)
a = list(routes.outbox_delete_album({"album": album}))[0]
expected = serializers.ActivitySerializer(
{"type": "Delete", "object": {"type": "Album", "id": album.fid}}
).data
expected["to"] = [activity.PUBLIC_ADDRESS, {"type": "instances_with_followers"}]
assert dict(a["payload"]) == dict(expected)
assert a["actor"] == channel.actor
def test_inbox_delete_library_impostor(factories):
activity = factories["federation.Activity"]()
impostor = factories["federation.Actor"]()
@ -469,6 +566,25 @@ def test_inbox_delete_audio(factories):
upload.refresh_from_db()
def test_inbox_delete_audio_channel(factories):
activity = factories["federation.Activity"]()
channel = factories["audio.Channel"]()
upload = factories["music.Upload"](track__artist=channel.artist)
payload = {
"type": "Delete",
"actor": channel.actor.fid,
"object": {"type": "Audio", "id": [upload.fid]},
}
routes.inbox_delete_audio(
payload,
context={"actor": channel.actor, "raise_exception": True, "activity": activity},
)
with pytest.raises(upload.__class__.DoesNotExist):
upload.refresh_from_db()
def test_inbox_delete_audio_impostor(factories):
activity = factories["federation.Activity"]()
impostor = factories["federation.Actor"]()

View file

@ -795,6 +795,17 @@ def test_activity_pub_album_serializer_to_ap(factories):
assert serializer.data == expected
def test_activity_pub_album_serializer_to_ap_channel_artist(factories):
channel = factories["audio.Channel"]()
album = factories["music.Album"](artist=channel.artist,)
serializer = serializers.AlbumSerializer(album)
assert serializer.data["artists"] == [
{"type": channel.actor.type, "id": channel.actor.fid}
]
def test_activity_pub_album_serializer_from_ap_create(factories, faker, now):
actor = factories["federation.Actor"]()
artist = factories["music.Artist"]()
@ -836,6 +847,30 @@ def test_activity_pub_album_serializer_from_ap_create(factories, faker, now):
]
def test_activity_pub_album_serializer_from_ap_create_channel_artist(
factories, faker, now
):
actor = factories["federation.Actor"]()
channel = factories["audio.Channel"]()
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(),
"artists": [{"type": channel.actor.type, "id": channel.actor.fid}],
"attributedTo": actor.fid,
}
serializer = serializers.AlbumSerializer(data=payload)
assert serializer.is_valid(raise_exception=True) is True
album = serializer.save()
assert album.artist == channel.artist
def test_activity_pub_album_serializer_from_ap_update(factories, faker):
album = factories["music.Album"](attributed=True)
released = faker.date_object()
@ -1395,7 +1430,9 @@ def test_track_serializer_update_license(factories):
def test_channel_actor_serializer(factories):
channel = factories["audio.Channel"](
actor__attachment_icon=None, artist__with_cover=True
actor__attachment_icon=None,
artist__with_cover=True,
artist__set_tags=["punk", "rock"],
)
serializer = serializers.ActorSerializer(channel.actor)
@ -1418,6 +1455,164 @@ def test_channel_actor_serializer(factories):
}
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):
@ -1449,12 +1644,21 @@ def test_channel_actor_outbox_serializer(factories):
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__set_tags=["Indie"],
)
@ -1463,25 +1667,38 @@ def test_channel_upload_serializer(factories):
"@context": jsonld.get_default_context(),
"type": "Audio",
"id": upload.fid,
"name": upload.track.full_name,
"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": upload.mimetype,
"href": utils.full_url(upload.listen_url_no_download),
},
{
"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"},
@ -1494,6 +1711,166 @@ def test_channel_upload_serializer(factories):
assert serializer.data == expected
def test_channel_upload_serializer_from_ap_create(factories, now):
channel = factories["audio.Channel"](library__privacy_level="everyone")
album = factories["music.Album"](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",
},
}
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 == 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):
channel = factories["audio.Channel"](library__privacy_level="everyone")
album = factories["music.Album"](artist=channel.artist)
upload = factories["music.Upload"](track__album=album, track__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",
},
}
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 == 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"](

View file

@ -491,6 +491,21 @@ def test_fetch_url(factory_name, serializer_class, factories, r_mock, mocker):
assert save.call_count == 1
def test_fetch_channel_actor_returns_channel(factories, r_mock):
obj = factories["audio.Channel"]()
fetch = factories["federation.Fetch"](url=obj.actor.fid)
payload = serializers.ActorSerializer(obj.actor).data
r_mock.get(obj.fid, json=payload)
tasks.fetch(fetch_id=fetch.pk)
fetch.refresh_from_db()
assert fetch.status == "finished"
assert fetch.object == obj
def test_fetch_honor_instance_policy_domain(factories):
domain = factories["moderation.InstancePolicy"](
block_all=True, for_domain=True

View file

@ -1407,7 +1407,8 @@ def test_channel_owner_can_create_album(factories, logged_in_api_client):
assert album.description.text == "hello world"
def test_channel_owner_can_delete_album(factories, logged_in_api_client):
def test_channel_owner_can_delete_album(factories, logged_in_api_client, mocker):
dispatch = mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
actor = logged_in_api_client.user.create_actor()
channel = factories["audio.Channel"](attributed_to=actor)
album = factories["music.Album"](artist=channel.artist)
@ -1416,6 +1417,10 @@ def test_channel_owner_can_delete_album(factories, logged_in_api_client):
response = logged_in_api_client.delete(url)
assert response.status_code == 204
dispatch.assert_called_once_with(
{"type": "Delete", "object": {"type": "Album"}}, context={"album": album}
)
with pytest.raises(album.DoesNotExist):
album.refresh_from_db()
@ -1452,15 +1457,22 @@ def test_other_user_cannot_delete_album(factories, logged_in_api_client):
album.refresh_from_db()
def test_channel_owner_can_delete_track(factories, logged_in_api_client):
def test_channel_owner_can_delete_track(factories, logged_in_api_client, mocker):
dispatch = mocker.patch("funkwhale_api.federation.routes.outbox.dispatch")
actor = logged_in_api_client.user.create_actor()
channel = factories["audio.Channel"](attributed_to=actor)
track = factories["music.Track"](artist=channel.artist)
upload1 = factories["music.Upload"](track=track)
upload2 = factories["music.Upload"](track=track)
url = reverse("api:v1:tracks-detail", kwargs={"pk": track.pk})
response = logged_in_api_client.delete(url)
assert response.status_code == 204
dispatch.assert_called_once_with(
{"type": "Delete", "object": {"type": "Audio"}},
context={"uploads": [upload1, upload2]},
)
with pytest.raises(track.DoesNotExist):
track.refresh_from_db()

View file

@ -7,9 +7,10 @@ from funkwhale_api.users import tasks
def test_delete_account(factories, mocker):
user = factories["users.User"]()
actor = user.create_actor()
factories["federation.Follow"](target=actor, approved=True)
library = factories["music.Library"](actor=actor)
unrelated_library = factories["music.Library"]()
dispatch = mocker.patch.object(routes.outbox, "dispatch")
dispatch = mocker.spy(routes.outbox, "dispatch")
tasks.delete_account(user_id=user.pk)
@ -30,3 +31,5 @@ def test_delete_account(factories, mocker):
assert actor.type == "Tombstone"
assert actor.name is None
assert actor.summary is None
# this activity shouldn't be deleted
assert actor.outbox_activities.filter(type="Delete").count() == 1