funkwhale/api/tests/playlists/test_views.py

292 lines
11 KiB
Python

import pytest
from django.urls import reverse
from funkwhale_api.playlists import models
def test_can_create_playlist_via_api(logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
url = reverse("api:v1:playlists-list")
data = {"name": "test", "privacy_level": "everyone"}
logged_in_api_client.post(url, data)
playlist = models.Playlist.objects.latest("id")
assert playlist.name == "test"
assert playlist.actor == actor
assert playlist.privacy_level == "everyone"
def test_serializer_includes_tracks_count(factories, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
factories["playlists.PlaylistTrack"](playlist=playlist)
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
response = logged_in_api_client.get(url, content_type="application/json")
assert response.data["tracks_count"] == 1
def test_serializer_includes_tracks_count_986(factories, logged_in_api_client):
logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](privacy_level="everyone")
plt = factories["playlists.PlaylistTrack"](playlist=playlist)
factories["music.Upload"].create_batch(
3, track=plt.track, library__privacy_level="everyone", import_status="finished"
)
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
response = logged_in_api_client.get(url, content_type="application/json")
assert response.data["tracks_count"] == 1
def test_serializer_includes_is_playable(factories, logged_in_api_client):
logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"]()
factories["playlists.PlaylistTrack"](playlist=playlist)
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
response = logged_in_api_client.get(url, content_type="application/json")
assert response.data["is_playable"] is False
def test_playlist_inherits_user_privacy(logged_in_api_client):
url = reverse("api:v1:playlists-list")
user = logged_in_api_client.user
user.create_actor()
user.privacy_level = "me"
user.save()
data = {"name": "test"}
logged_in_api_client.post(url, data)
playlist = models.Playlist.objects.filter(actor=user.actor).latest("id")
assert playlist.privacy_level == user.privacy_level
@pytest.mark.parametrize(
"name,method",
[("api:v1:playlists-list", "post")],
)
def test_url_requires_login(name, method, factories, api_client):
url = reverse(name)
response = getattr(api_client, method.lower())(url, {})
assert response.status_code == 401
def test_only_can_add_track_on_own_playlist_via_api(factories, logged_in_api_client):
logged_in_api_client.user.create_actor()
track = factories["music.Track"]()
playlist = factories["playlists.Playlist"]()
url = reverse("api:v1:playlists-add", kwargs={"pk": playlist.pk})
data = {"tracks": [track.pk]}
response = logged_in_api_client.post(url, data, content_type="application/json")
assert response.status_code == 404
assert playlist.playlist_tracks.count() == 0
def test_deleting_plt_updates_indexes(mocker, factories, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
remove = mocker.spy(models.Playlist, "remove")
factories["music.Track"]()
playlist = factories["playlists.Playlist"](actor=actor)
plt0 = factories["playlists.PlaylistTrack"](index=0, playlist=playlist)
plt1 = factories["playlists.PlaylistTrack"](index=1, playlist=playlist)
url = reverse("api:v1:playlists-remove", kwargs={"pk": playlist.pk})
response = logged_in_api_client.delete(url, {"index": 0})
assert response.status_code == 204
remove.assert_called_once_with(plt0.playlist, 0)
with pytest.raises(plt0.DoesNotExist):
plt0.refresh_from_db()
plt1.refresh_from_db()
assert plt1.index == 0
def test_deleting_plt_updates_pl_lib(mocker, factories, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
factories["music.Track"]()
playlist = factories["playlists.Playlist"](actor=actor)
# playlist.library.uploads.add(plt0.track.uploads.all()[0])
# playlist.library.uploads.add(plt1.track.uploads.all()[0])
tracks = factories["music.Track"].create_batch(size=5)
for track in tracks:
factories["music.Upload"](track=track, library__actor=actor)
not_user_actor = factories["federation.Actor"]()
not_user_upload = factories["music.Upload"](
track=tracks[0], library__actor=not_user_actor
)
track_ids = [t.id for t in tracks]
url = reverse("api:v1:playlists-add", kwargs={"pk": playlist.pk})
logged_in_api_client.post(url, {"tracks": track_ids})
assert not_user_upload not in playlist.library.uploads.all()
for plt in playlist.playlist_tracks.all():
for upload in playlist.library.uploads.all():
assert upload.tracks.filter(id=plt.track.id).exists()
url = reverse("api:v1:playlists-remove", kwargs={"pk": playlist.pk})
logged_in_api_client.delete(url, {"index": 0})
playlist.library.refresh_from_db()
for plt in playlist.playlist_tracks.all():
for upload in playlist.library.uploads.all():
assert not upload.tracks.filter(id=plt.track.id).exists()
@pytest.mark.parametrize("level", ["instance", "me", "followers"])
def test_playlist_privacy_respected_in_list_anon(
preferences, level, factories, api_client
):
preferences["common__api_authentication_required"] = False
factories["playlists.Playlist"](privacy_level=level)
url = reverse("api:v1:playlists-list")
response = api_client.get(url)
assert response.data["count"] == 0
@pytest.mark.parametrize("method", ["PUT", "PATCH", "DELETE"])
def test_only_owner_can_edit_playlist(method, factories, logged_in_api_client):
logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"]()
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
response = getattr(logged_in_api_client, method.lower())(url)
assert response.status_code == 404
def test_can_add_multiple_tracks_at_once_via_api(
factories, mocker, logged_in_api_client
):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
tracks = factories["music.Track"].create_batch(size=5)
track_ids = [t.id for t in tracks]
mocker.spy(playlist, "insert_many")
url = reverse("api:v1:playlists-add", kwargs={"pk": playlist.pk})
response = logged_in_api_client.post(url, {"tracks": track_ids})
assert response.status_code == 201
assert playlist.playlist_tracks.count() == len(track_ids)
for plt in playlist.playlist_tracks.order_by("index"):
assert response.data["results"][plt.index]["index"] == plt.index
assert plt.track == tracks[plt.index]
def test_add_multiple_tracks_at_once_update_pl_library(
factories, mocker, logged_in_api_client
):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
tracks = factories["music.Track"].create_batch(size=5)
not_user_actor = factories["federation.Actor"]()
not_user_track = factories["music.Track"]()
not_user_upload = factories["music.Upload"](
size=5, track=not_user_track, library__actor=not_user_actor
)
for track in tracks:
factories["music.Upload"](track=track, library__actor=actor)
track_already_in_playlist = factories["music.Track"]()
upload_already_in_playlist = factories["music.Upload"](
track=track_already_in_playlist, library__actor=actor
)
upload_already_in_playlist.playlist_libraries.add(playlist.library)
track_ids = [t.id for t in tracks]
track_ids.append(not_user_track.id)
track_ids.append(track_already_in_playlist.id)
mocker.spy(playlist, "insert_many")
url = reverse("api:v1:playlists-add", kwargs={"pk": playlist.pk})
response = logged_in_api_client.post(url, {"tracks": track_ids})
assert response.status_code == 201
assert playlist.playlist_tracks.count() == len(track_ids)
assert playlist.playlist_tracks.filter(track=not_user_track).exists()
assert not_user_upload not in playlist.library.uploads.all()
for plt in playlist.playlist_tracks.all():
for upload in playlist.library.uploads.all():
assert upload.tracks.filter(id=plt.track.id).exists()
assert len(upload.playlist_libraries.all()) == 1
def test_honor_max_playlist_size(factories, mocker, logged_in_api_client, preferences):
actor = logged_in_api_client.user.create_actor()
preferences["playlists__max_tracks"] = 3
playlist = factories["playlists.Playlist"](actor=actor)
tracks = factories["music.Track"].create_batch(
size=preferences["playlists__max_tracks"] + 1
)
track_ids = [t.id for t in tracks]
mocker.spy(playlist, "insert_many")
url = reverse("api:v1:playlists-add", kwargs={"pk": playlist.pk})
response = logged_in_api_client.post(url, {"tracks": track_ids})
assert response.status_code == 400
def test_can_clear_playlist_from_api(factories, mocker, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
factories["playlists.PlaylistTrack"].create_batch(size=5, playlist=playlist)
url = reverse("api:v1:playlists-clear", kwargs={"pk": playlist.pk})
response = logged_in_api_client.delete(url)
assert response.status_code == 204
assert playlist.playlist_tracks.count() == 0
def test_clear_playlist_from_api_remove_pl_lib_uploads(
factories, mocker, logged_in_api_client
):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
factories["playlists.PlaylistTrack"].create_batch(size=5, playlist=playlist)
for upload in playlist.library.uploads.all():
assert upload.playlist_libraries.filter(playlist=playlist).exists()
assert upload.playlist_libraries.get(playlist=playlist).actor == actor
url = reverse("api:v1:playlists-clear", kwargs={"pk": playlist.pk})
response = logged_in_api_client.delete(url)
assert response.status_code == 204
assert not playlist.library.uploads.all()
def test_update_playlist_from_api(factories, mocker, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
factories["playlists.PlaylistTrack"].create_batch(size=5, playlist=playlist)
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
response = logged_in_api_client.patch(url, {"name": "test"})
playlist.refresh_from_db()
assert response.status_code == 200
assert response.data["actor"]["full_username"] == playlist.actor.full_username
def test_move_plt_updates_indexes(mocker, factories, logged_in_api_client):
actor = logged_in_api_client.user.create_actor()
playlist = factories["playlists.Playlist"](actor=actor)
plt0 = factories["playlists.PlaylistTrack"](index=0, playlist=playlist)
plt1 = factories["playlists.PlaylistTrack"](index=1, playlist=playlist)
url = reverse("api:v1:playlists-move", kwargs={"pk": playlist.pk})
response = logged_in_api_client.post(url, {"from": 1, "to": 0})
assert response.status_code == 204
plt0.refresh_from_db()
plt1.refresh_from_db()
assert plt0.index == 1
assert plt1.index == 0