804 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
	
	
			
		
		
	
	
			804 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
	
	
| import pytest
 | |
| from django.core.paginator import Paginator
 | |
| from django.urls import reverse
 | |
| 
 | |
| from funkwhale_api.common import utils
 | |
| from funkwhale_api.federation import actors, serializers
 | |
| from funkwhale_api.federation import utils as federation_utils
 | |
| from funkwhale_api.federation import views, webfinger
 | |
| 
 | |
| 
 | |
| def test_authenticate_allows_anonymous_actor_fetch_when_allow_list_enabled(
 | |
|     preferences, api_client
 | |
| ):
 | |
|     preferences["moderation__allow_list_enabled"] = True
 | |
|     actor = actors.get_service_actor()
 | |
|     url = reverse(
 | |
|         "federation:actors-detail",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
| 
 | |
| 
 | |
| def test_authenticate_skips_anonymous_fetch_when_allow_list_enabled(
 | |
|     preferences, api_client, factories
 | |
| ):
 | |
|     preferences["moderation__allow_list_enabled"] = True
 | |
|     library = factories["music.Library"]()
 | |
|     url = reverse(
 | |
|         "federation:music:libraries-detail",
 | |
|         kwargs={"uuid": library.uuid},
 | |
|     )
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 403
 | |
| 
 | |
| 
 | |
| def test_wellknown_webfinger_validates_resource(db, api_client, settings, mocker):
 | |
|     clean = mocker.spy(webfinger, "clean_resource")
 | |
|     url = reverse("federation:well-known-webfinger")
 | |
|     response = api_client.get(url, data={"resource": "something"})
 | |
| 
 | |
|     clean.assert_called_once_with("something")
 | |
|     assert url == "/.well-known/webfinger"
 | |
|     assert response.status_code == 400
 | |
|     assert response.data["errors"]["resource"] == ("Missing webfinger resource type")
 | |
| 
 | |
| 
 | |
| def test_wellknown_nodeinfo(db, preferences, api_client, settings):
 | |
|     expected = {
 | |
|         "links": [
 | |
|             {
 | |
|                 "rel": "http://nodeinfo.diaspora.software/ns/schema/2.0",
 | |
|                 "href": "{}{}".format(
 | |
|                     settings.FUNKWHALE_URL, reverse("api:v1:instance:nodeinfo-2.0")
 | |
|                 ),
 | |
|             }
 | |
|         ]
 | |
|     }
 | |
|     url = reverse("federation:well-known-nodeinfo")
 | |
|     response = api_client.get(url, HTTP_ACCEPT="application/jrd+json")
 | |
|     assert response.status_code == 200
 | |
|     assert response["Content-Type"] == "application/jrd+json"
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_local_actor_detail(factories, api_client):
 | |
|     user = factories["users.User"](with_actor=True)
 | |
|     url = reverse(
 | |
|         "federation:actors-detail",
 | |
|         kwargs={"preferred_username": user.actor.preferred_username},
 | |
|     )
 | |
|     serializer = serializers.ActorSerializer(user.actor)
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == serializer.data
 | |
| 
 | |
| 
 | |
| def test_service_actor_detail(factories, api_client):
 | |
|     actor = actors.get_service_actor()
 | |
|     url = reverse(
 | |
|         "federation:actors-detail",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     serializer = serializers.ActorSerializer(actor)
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == serializer.data
 | |
| 
 | |
| 
 | |
| def test_local_actor_inbox_post_requires_auth(factories, api_client):
 | |
|     user = factories["users.User"](with_actor=True)
 | |
|     url = reverse(
 | |
|         "federation:actors-inbox",
 | |
|         kwargs={"preferred_username": user.actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.post(url, {"hello": "world"})
 | |
| 
 | |
|     assert response.status_code == 403
 | |
| 
 | |
| 
 | |
| def test_local_actor_inbox_post(factories, api_client, mocker, authenticated_actor):
 | |
|     patched_receive = mocker.patch("funkwhale_api.federation.activity.receive")
 | |
|     user = factories["users.User"](with_actor=True)
 | |
|     url = reverse(
 | |
|         "federation:actors-inbox",
 | |
|         kwargs={"preferred_username": user.actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.post(url, {"hello": "world"}, format="json")
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     patched_receive.assert_called_once_with(
 | |
|         activity={"hello": "world"},
 | |
|         on_behalf_of=authenticated_actor,
 | |
|         inbox_actor=user.actor,
 | |
|     )
 | |
| 
 | |
| 
 | |
| def test_local_actor_inbox_post_receive(
 | |
|     factories, api_client, mocker, authenticated_actor
 | |
| ):
 | |
|     payload = {
 | |
|         "to": [
 | |
|             "https://test.server/federation/music/libraries/956af6c9-1eb9-4117-8d17-b15e7b34afeb/followers"
 | |
|         ],
 | |
|         "type": "Create",
 | |
|         "actor": authenticated_actor.fid,
 | |
|         "object": {
 | |
|             "id": "https://test.server/federation/music/uploads/fe564a47-b1d4-4596-bf96-008ccf407672",
 | |
|             "type": "Audio",
 | |
|         },
 | |
|         "@context": [
 | |
|             "https://www.w3.org/ns/activitystreams",
 | |
|             "https://w3id.org/security/v1",
 | |
|             {},
 | |
|         ],
 | |
|     }
 | |
|     user = factories["users.User"](with_actor=True)
 | |
|     url = reverse(
 | |
|         "federation:actors-inbox",
 | |
|         kwargs={"preferred_username": user.actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.post(url, payload, format="json")
 | |
| 
 | |
|     assert response.status_code == 200
 | |
| 
 | |
| 
 | |
| def test_shared_inbox_post(factories, api_client, mocker, authenticated_actor):
 | |
|     patched_receive = mocker.patch("funkwhale_api.federation.activity.receive")
 | |
|     url = reverse("federation:shared-inbox")
 | |
|     response = api_client.post(url, {"hello": "world"}, format="json")
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     patched_receive.assert_called_once_with(
 | |
|         activity={"hello": "world"}, on_behalf_of=authenticated_actor
 | |
|     )
 | |
| 
 | |
| 
 | |
| def test_wellknown_webfinger_local(factories, api_client, settings, mocker):
 | |
|     user = factories["users.User"](with_actor=True)
 | |
|     url = reverse("federation:well-known-webfinger")
 | |
|     response = api_client.get(
 | |
|         url,
 | |
|         data={"resource": f"acct:{user.actor.webfinger_subject}"},
 | |
|         HTTP_ACCEPT="application/jrd+json",
 | |
|     )
 | |
|     serializer = serializers.ActorWebfingerSerializer(user.actor)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response["Content-Type"] == "application/jrd+json"
 | |
|     assert response.data == serializer.data
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("privacy_level", ["me", "instance", "everyone"])
 | |
| def test_music_library_retrieve(factories, api_client, privacy_level):
 | |
|     library = factories["music.Library"](privacy_level=privacy_level, actor__local=True)
 | |
|     expected = serializers.LibrarySerializer(library).data
 | |
| 
 | |
|     url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_music_library_retrieve_excludes_channel_libraries(factories, api_client):
 | |
|     channel = factories["audio.Channel"](local=True)
 | |
|     library = channel.library
 | |
| 
 | |
|     url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 404
 | |
| 
 | |
| 
 | |
| def test_actor_retrieve_excludes_channel_with_private_library(factories, api_client):
 | |
|     channel = factories["audio.Channel"](external=True, library__privacy_level="me")
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-detail",
 | |
|         kwargs={"preferred_username": channel.actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 404
 | |
| 
 | |
| 
 | |
| def test_music_library_retrieve_page_public(factories, api_client):
 | |
|     library = factories["music.Library"](privacy_level="everyone", actor__local=True)
 | |
|     upload = factories["music.Upload"](library=library, import_status="finished")
 | |
|     id = library.get_federation_id()
 | |
|     expected = serializers.CollectionPageSerializer(
 | |
|         {
 | |
|             "id": id,
 | |
|             "item_serializer": serializers.UploadSerializer,
 | |
|             "actor": library.actor,
 | |
|             "page": Paginator([upload], 1).page(1),
 | |
|             "name": library.name,
 | |
|         }
 | |
|     ).data
 | |
| 
 | |
|     url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
 | |
|     response = api_client.get(url, {"page": 1})
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_channel_outbox_retrieve(factories, api_client):
 | |
|     channel = factories["audio.Channel"](actor__local=True)
 | |
|     expected = serializers.ChannelOutboxSerializer(channel).data
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-outbox",
 | |
|         kwargs={"preferred_username": channel.actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_channel_outbox_retrieve_page(factories, api_client):
 | |
|     channel = factories["audio.Channel"](actor__local=True)
 | |
|     upload = factories["music.Upload"](library=channel.library, playable=True)
 | |
|     url = reverse(
 | |
|         "federation:actors-outbox",
 | |
|         kwargs={"preferred_username": channel.actor.preferred_username},
 | |
|     )
 | |
| 
 | |
|     expected = serializers.CollectionPageSerializer(
 | |
|         {
 | |
|             "id": channel.actor.outbox_url,
 | |
|             "item_serializer": serializers.ChannelCreateUploadSerializer,
 | |
|             "actor": channel.actor,
 | |
|             "page": Paginator([upload], 1).page(1),
 | |
|         }
 | |
|     ).data
 | |
| 
 | |
|     response = api_client.get(url, {"page": 1})
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_channel_upload_retrieve(factories, api_client):
 | |
|     channel = factories["audio.Channel"](local=True)
 | |
|     upload = factories["music.Upload"](library=channel.library, playable=True)
 | |
|     url = reverse("federation:music:uploads-detail", kwargs={"uuid": upload.uuid})
 | |
| 
 | |
|     expected = serializers.ChannelUploadSerializer(upload).data
 | |
| 
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_channel_upload_retrieve_activity(factories, api_client):
 | |
|     channel = factories["audio.Channel"](local=True)
 | |
|     upload = factories["music.Upload"](library=channel.library, playable=True)
 | |
|     url = reverse("federation:music:uploads-activity", kwargs={"uuid": upload.uuid})
 | |
| 
 | |
|     expected = serializers.ChannelCreateUploadSerializer(upload).data
 | |
| 
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("privacy_level", ["me", "instance"])
 | |
| def test_music_library_retrieve_page_private(factories, api_client, privacy_level):
 | |
|     library = factories["music.Library"](privacy_level=privacy_level, actor__local=True)
 | |
|     url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
 | |
|     response = api_client.get(url, {"page": 1})
 | |
| 
 | |
|     assert response.status_code == 403
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("approved,expected", [(True, 200), (False, 403)])
 | |
| def test_music_library_retrieve_page_follow(
 | |
|     factories, api_client, authenticated_actor, approved, expected
 | |
| ):
 | |
|     library = factories["music.Library"](privacy_level="me", actor__local=True)
 | |
|     factories["federation.LibraryFollow"](
 | |
|         actor=authenticated_actor, target=library, approved=approved
 | |
|     )
 | |
|     url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
 | |
|     response = api_client.get(url, {"page": 1})
 | |
| 
 | |
|     assert response.status_code == expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "factory, serializer_class, namespace",
 | |
|     [
 | |
|         ("music.Artist", serializers.ArtistSerializer, "artists"),
 | |
|         ("music.Album", serializers.AlbumSerializer, "albums"),
 | |
|         ("music.Track", serializers.TrackSerializer, "tracks"),
 | |
|     ],
 | |
| )
 | |
| def test_music_local_entity_detail(
 | |
|     factories, api_client, factory, serializer_class, namespace, settings
 | |
| ):
 | |
|     obj = factories[factory](fid=f"http://{settings.FEDERATION_HOSTNAME}/1")
 | |
|     url = reverse(f"federation:music:{namespace}-detail", kwargs={"uuid": obj.uuid})
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == serializer_class(obj).data
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "factory, namespace",
 | |
|     [("music.Artist", "artists"), ("music.Album", "albums"), ("music.Track", "tracks")],
 | |
| )
 | |
| def test_music_non_local_entity_detail(
 | |
|     factories, api_client, factory, namespace, settings
 | |
| ):
 | |
|     obj = factories[factory](fid="http://wrong-domain/1")
 | |
|     url = reverse(f"federation:music:{namespace}-detail", kwargs={"uuid": obj.uuid})
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 404
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "privacy_level, expected", [("me", 404), ("instance", 404), ("everyone", 200)]
 | |
| )
 | |
| def test_music_upload_detail(factories, api_client, privacy_level, expected):
 | |
|     upload = factories["music.Upload"](
 | |
|         library__privacy_level=privacy_level,
 | |
|         library__actor__local=True,
 | |
|         import_status="finished",
 | |
|     )
 | |
|     url = reverse("federation:music:uploads-detail", kwargs={"uuid": upload.uuid})
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == expected
 | |
|     if expected == 200:
 | |
|         assert response.data == serializers.UploadSerializer(upload).data
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("privacy_level", ["me", "instance"])
 | |
| def test_music_upload_detail_private_approved_follow(
 | |
|     factories, api_client, authenticated_actor, privacy_level
 | |
| ):
 | |
|     upload = factories["music.Upload"](
 | |
|         library__privacy_level=privacy_level,
 | |
|         library__actor__local=True,
 | |
|         import_status="finished",
 | |
|     )
 | |
|     factories["federation.LibraryFollow"](
 | |
|         actor=authenticated_actor, target=upload.library, approved=True
 | |
|     )
 | |
|     url = reverse("federation:music:uploads-detail", kwargs={"uuid": upload.uuid})
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "accept_header,default,expected",
 | |
|     [
 | |
|         ("text/html,application/xhtml+xml", True, True),
 | |
|         ("text/html,application/json", True, True),
 | |
|         ("", True, False),
 | |
|         (None, True, False),
 | |
|         ("application/json", True, False),
 | |
|         ("application/activity+json", True, False),
 | |
|         ("application/json,text/html", True, False),
 | |
|         ("application/activity+json,text/html", True, False),
 | |
|         ("unrelated/ct", True, True),
 | |
|         ("unrelated/ct", False, False),
 | |
|     ],
 | |
| )
 | |
| def test_should_redirect_ap_to_html(accept_header, default, expected):
 | |
|     assert (
 | |
|         federation_utils.should_redirect_ap_to_html(accept_header, default) is expected
 | |
|     )
 | |
| 
 | |
| 
 | |
| def test_music_library_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     library = factories["music.Library"](actor__local=True)
 | |
| 
 | |
|     url = reverse("federation:music:libraries-detail", kwargs={"uuid": library.uuid})
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse("library_library", kwargs={"uuid": library.uuid}),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| def test_actor_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     actor = factories["federation.Actor"](local=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-detail",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse(
 | |
|             "actor_detail", kwargs={"username": actor.preferred_username}
 | |
|         ),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| def test_channel_actor_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     channel = factories["audio.Channel"](local=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-detail",
 | |
|         kwargs={"preferred_username": channel.actor.preferred_username},
 | |
|     )
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse(
 | |
|             "channel_detail", kwargs={"username": channel.actor.preferred_username}
 | |
|         ),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| def test_upload_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     upload = factories["music.Upload"](library__local=True, playable=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:music:uploads-detail",
 | |
|         kwargs={"uuid": upload.uuid},
 | |
|     )
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse("library_track", kwargs={"pk": upload.track.pk}),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| def test_track_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     track = factories["music.Track"](local=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:music:tracks-detail",
 | |
|         kwargs={"uuid": track.uuid},
 | |
|     )
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse("library_track", kwargs={"pk": track.pk}),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| def test_album_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     album = factories["music.Album"](local=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:music:albums-detail",
 | |
|         kwargs={"uuid": album.uuid},
 | |
|     )
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse("library_album", kwargs={"pk": album.pk}),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| def test_artist_retrieve_redirects_to_html_if_header_set(
 | |
|     factories, api_client, settings
 | |
| ):
 | |
|     artist = factories["music.Artist"](local=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:music:artists-detail",
 | |
|         kwargs={"uuid": artist.uuid},
 | |
|     )
 | |
|     response = api_client.get(url, HTTP_ACCEPT="text/html")
 | |
|     expected_url = utils.join_url(
 | |
|         settings.FUNKWHALE_URL,
 | |
|         utils.spa_reverse("library_artist", kwargs={"pk": artist.pk}),
 | |
|     )
 | |
|     assert response.status_code == 302
 | |
|     assert response["Location"] == expected_url
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize("index", ["channels", "libraries"])
 | |
| def test_public_index_disabled(index, api_client, preferences):
 | |
|     preferences["federation__public_index"] = False
 | |
|     url = reverse(f"federation:index:index-{index}")
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 405
 | |
| 
 | |
| 
 | |
| def test_index_channels_retrieve(factories, api_client):
 | |
|     channels = [
 | |
|         factories["audio.Channel"](actor__local=True),
 | |
|         factories["audio.Channel"](actor__local=True),
 | |
|         factories["audio.Channel"](actor__local=True),
 | |
|     ]
 | |
|     expected = serializers.IndexSerializer(
 | |
|         {
 | |
|             "id": federation_utils.full_url(reverse("federation:index:index-channels")),
 | |
|             "items": channels[0].__class__.objects.order_by("creation_date"),
 | |
|             "page_size": 100,
 | |
|         },
 | |
|     ).data
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:index:index-channels",
 | |
|     )
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_index_channels_page(factories, api_client, preferences):
 | |
|     preferences["federation__collection_page_size"] = 1
 | |
|     remote_actor = factories["federation.Actor"]()
 | |
|     channels = [
 | |
|         factories["audio.Channel"](actor__local=True),
 | |
|         factories["audio.Channel"](actor__local=True),
 | |
|         factories["audio.Channel"](actor__local=True),
 | |
|         factories["audio.Channel"](actor=remote_actor),
 | |
|     ]
 | |
|     id = federation_utils.full_url(reverse("federation:index:index-channels"))
 | |
|     expected = serializers.CollectionPageSerializer(
 | |
|         {
 | |
|             "id": id,
 | |
|             "item_serializer": serializers.ActorSerializer,
 | |
|             "page": Paginator([c.actor for c in channels][:3], 1).page(1),
 | |
|             "actor": None,
 | |
|         }
 | |
|     ).data
 | |
| 
 | |
|     url = reverse("federation:index:index-channels")
 | |
|     response = api_client.get(url, {"page": 1})
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_index_libraries_retrieve(factories, api_client):
 | |
|     remote_actor = factories["federation.Actor"]()
 | |
|     libraries = [
 | |
|         factories["music.Library"](actor__local=True, privacy_level="everyone"),
 | |
|         factories["music.Library"](actor__local=True, privacy_level="everyone"),
 | |
|         factories["music.Library"](actor__local=True, privacy_level="me"),
 | |
|         factories["music.Library"](actor=remote_actor, privacy_level="everyone"),
 | |
|     ]
 | |
|     expected = serializers.IndexSerializer(
 | |
|         {
 | |
|             "id": federation_utils.full_url(
 | |
|                 reverse("federation:index:index-libraries")
 | |
|             ),
 | |
|             "items": libraries[0]
 | |
|             .__class__.objects.local()
 | |
|             .filter(privacy_level="everyone")
 | |
|             .order_by("creation_date"),
 | |
|             "page_size": 100,
 | |
|         },
 | |
|     ).data
 | |
| 
 | |
|     url = reverse("federation:index:index-libraries")
 | |
|     response = api_client.get(url)
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_index_libraries_page(factories, api_client, preferences):
 | |
|     preferences["federation__collection_page_size"] = 1
 | |
|     remote_actor = factories["federation.Actor"]()
 | |
|     libraries = [
 | |
|         factories["music.Library"](actor__local=True, privacy_level="everyone"),
 | |
|         factories["music.Library"](actor__local=True, privacy_level="everyone"),
 | |
|         factories["music.Library"](actor__local=True, privacy_level="me"),
 | |
|         factories["music.Library"](actor=remote_actor, privacy_level="everyone"),
 | |
|     ]
 | |
|     id = federation_utils.full_url(reverse("federation:index:index-libraries"))
 | |
|     expected = serializers.CollectionPageSerializer(
 | |
|         {
 | |
|             "id": id,
 | |
|             "item_serializer": serializers.LibrarySerializer,
 | |
|             "page": Paginator(libraries[:2], 1).page(1),
 | |
|             "actor": None,
 | |
|         }
 | |
|     ).data
 | |
| 
 | |
|     url = reverse("federation:index:index-libraries")
 | |
|     response = api_client.get(url, {"page": 1})
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_get_followers(factories, logged_in_api_client):
 | |
|     actor = logged_in_api_client.user.create_actor()
 | |
|     factories["federation.Follow"](target=actor, approved=True)
 | |
|     factories["federation.Follow"](target=actor, approved=True)
 | |
|     factories["federation.Follow"](target=actor, approved=True)
 | |
|     factories["federation.Follow"](target=actor, approved=True)
 | |
|     factories["federation.Follow"](target=actor, approved=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-followers",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.data["totalItems"] == 5
 | |
| 
 | |
| 
 | |
| def test_get_following(factories, logged_in_api_client):
 | |
|     actor = logged_in_api_client.user.create_actor()
 | |
|     factories["federation.Follow"](actor=actor, approved=True)
 | |
|     factories["federation.Follow"](actor=actor, approved=True)
 | |
|     factories["federation.Follow"](actor=actor, approved=True)
 | |
|     factories["federation.Follow"](actor=actor, approved=True)
 | |
|     factories["federation.Follow"](actor=actor, approved=True)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-following",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.data["totalItems"] == 5
 | |
| 
 | |
| 
 | |
| def test_get_likes(factories, logged_in_api_client):
 | |
|     actor = logged_in_api_client.user.create_actor()
 | |
|     factories["favorites.TrackFavorite"](actor=actor)
 | |
|     factories["favorites.TrackFavorite"](actor=actor)
 | |
|     factories["favorites.TrackFavorite"](actor=actor)
 | |
|     factories["favorites.TrackFavorite"](actor=actor)
 | |
|     factories["favorites.TrackFavorite"](actor=actor)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-likes",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.data["totalItems"] == 5
 | |
| 
 | |
| 
 | |
| def test_get_listenings(factories, logged_in_api_client):
 | |
|     actor = logged_in_api_client.user.create_actor()
 | |
|     factories["history.Listening"](actor=actor)
 | |
|     factories["history.Listening"](actor=actor)
 | |
|     factories["history.Listening"](actor=actor)
 | |
|     factories["history.Listening"](actor=actor)
 | |
|     factories["history.Listening"](actor=actor)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-listens",
 | |
|         kwargs={"preferred_username": actor.preferred_username},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.data["totalItems"] == 5
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "privacy_level, expected", [("me", 403), ("instance", 200), ("everyone", 200)]
 | |
| )
 | |
| def test_get_listenings_honours_privacy_level(
 | |
|     factories, logged_in_api_client, privacy_level, expected
 | |
| ):
 | |
|     user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
 | |
|     factories["history.Listening"](actor=user.actor)
 | |
| 
 | |
|     url = reverse(
 | |
|         "federation:actors-listens",
 | |
|         kwargs={"preferred_username": user.actor.preferred_username},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.status_code == expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "privacy_level, expected", [("me", 403), ("instance", 200), ("everyone", 200)]
 | |
| )
 | |
| def test_get_favorite(factories, logged_in_api_client, privacy_level, expected):
 | |
|     user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
 | |
|     favorite = factories["favorites.TrackFavorite"](actor=user.actor, local=True)
 | |
|     url = reverse(
 | |
|         "federation:music:likes-detail",
 | |
|         kwargs={"uuid": favorite.uuid},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.status_code == expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "privacy_level, expected", [("me", 403), ("instance", 403), ("everyone", 200)]
 | |
| )
 | |
| def test_get_favorite_anonymous(factories, api_client, privacy_level, expected):
 | |
|     user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
 | |
|     favorite = factories["favorites.TrackFavorite"](actor=user.actor, local=True)
 | |
|     url = reverse(
 | |
|         "federation:music:likes-detail",
 | |
|         kwargs={"uuid": favorite.uuid},
 | |
|     )
 | |
|     response = api_client.get(url)
 | |
|     assert response.status_code == expected
 | |
| 
 | |
| 
 | |
| @pytest.mark.parametrize(
 | |
|     "privacy_level, expected", [("me", 403), ("instance", 200), ("everyone", 200)]
 | |
| )
 | |
| def test_get_listening(factories, logged_in_api_client, privacy_level, expected):
 | |
|     user = factories["users.User"](with_actor=True, privacy_level=privacy_level)
 | |
|     listening = factories["history.Listening"](actor=user.actor, local=True)
 | |
|     url = reverse(
 | |
|         "federation:music:listenings-detail",
 | |
|         kwargs={"uuid": listening.uuid},
 | |
|     )
 | |
|     response = logged_in_api_client.get(url)
 | |
|     assert response.status_code == expected
 | |
| 
 | |
| 
 | |
| def test_playlist_retrieve(factories, api_client):
 | |
|     playlist = factories["playlists.Playlist"](local=True)
 | |
|     url = reverse("federation:music:playlists-detail", kwargs={"uuid": playlist.uuid})
 | |
|     response = api_client.get(url)
 | |
|     expected = serializers.PlaylistCollectionSerializer(playlist).data
 | |
| 
 | |
|     assert response.status_code == 200
 | |
|     assert response.data == expected
 | |
| 
 | |
| 
 | |
| def test_playlist_get_collection_response(factories, mocker):
 | |
|     actor = factories["federation.Actor"]()
 | |
|     playlist = factories["playlists.Playlist"](actor=actor, local=True)
 | |
|     plt = factories["playlists.PlaylistTrack"](playlist=playlist, index=4, local=True)
 | |
|     factories["playlists.PlaylistTrack"](playlist=playlist, index=0, local=True)
 | |
|     factories["playlists.PlaylistTrack"](playlist=playlist, index=1, local=True)
 | |
|     factories["playlists.PlaylistTrack"](playlist=playlist, index=2, local=True)
 | |
|     factories["playlists.PlaylistTrack"](playlist=playlist, index=3, local=True)
 | |
| 
 | |
|     conf = {
 | |
|         "id": playlist.fid,
 | |
|         "actor": playlist.actor,
 | |
|         "items": playlist.playlist_tracks.order_by("index").prefetch_related(
 | |
|             "track",
 | |
|         ),
 | |
|         "item_serializer": serializers.PlaylistTrackSerializer,
 | |
|     }
 | |
|     playlist_data = views.get_collection_response(
 | |
|         conf=conf,
 | |
|         querystring={"uuid": playlist.uuid, "page": 1},
 | |
|         collection_serializer=serializers.PlaylistCollectionSerializer(playlist),
 | |
|     )
 | |
| 
 | |
|     assert playlist_data.data["totalItems"] == 5
 | |
|     assert playlist_data.data["items"][4]["track"] == plt.track.fid
 |