2017-12-24 18:15:21 +00:00
|
|
|
import json
|
2018-03-18 20:33:02 +00:00
|
|
|
import pytest
|
|
|
|
|
2017-12-24 18:15:21 +00:00
|
|
|
from django.urls import reverse
|
|
|
|
from django.core.exceptions import ValidationError
|
|
|
|
from django.utils import timezone
|
|
|
|
|
|
|
|
from funkwhale_api.playlists import models
|
2018-03-18 20:51:49 +00:00
|
|
|
from funkwhale_api.playlists import serializers
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
2018-03-16 22:31:08 +00:00
|
|
|
def test_can_create_playlist_via_api(logged_in_api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:playlists-list")
|
|
|
|
data = {"name": "test", "privacy_level": "everyone"}
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-03-16 22:31:08 +00:00
|
|
|
response = logged_in_api_client.post(url, data)
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
playlist = logged_in_api_client.user.playlists.latest("id")
|
|
|
|
assert playlist.name == "test"
|
|
|
|
assert playlist.privacy_level == "everyone"
|
2017-12-24 18:15:21 +00:00
|
|
|
|
|
|
|
|
2018-03-19 18:07:45 +00:00
|
|
|
def test_serializer_includes_tracks_count(factories, logged_in_api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
playlist = factories["playlists.Playlist"]()
|
|
|
|
plt = factories["playlists.PlaylistTrack"](playlist=playlist)
|
2018-03-19 18:07:45 +00:00
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
|
2018-03-19 18:07:45 +00:00
|
|
|
response = logged_in_api_client.get(url)
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response.data["tracks_count"] == 1
|
2018-03-19 18:07:45 +00:00
|
|
|
|
|
|
|
|
2018-03-16 22:31:08 +00:00
|
|
|
def test_playlist_inherits_user_privacy(logged_in_api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
url = reverse("api:v1:playlists-list")
|
2018-03-16 22:31:08 +00:00
|
|
|
user = logged_in_api_client.user
|
2018-06-09 13:36:16 +00:00
|
|
|
user.privacy_level = "me"
|
2018-03-16 22:31:08 +00:00
|
|
|
user.save()
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
data = {"name": "test"}
|
2018-03-16 22:31:08 +00:00
|
|
|
|
|
|
|
response = logged_in_api_client.post(url, data)
|
2018-06-09 13:36:16 +00:00
|
|
|
playlist = user.playlists.latest("id")
|
2018-03-16 22:31:08 +00:00
|
|
|
assert playlist.privacy_level == user.privacy_level
|
|
|
|
|
|
|
|
|
|
|
|
def test_can_add_playlist_track_via_api(factories, logged_in_api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
tracks = factories["music.Track"].create_batch(5)
|
|
|
|
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
|
|
|
url = reverse("api:v1:playlist-tracks-list")
|
|
|
|
data = {"playlist": playlist.pk, "track": tracks[0].pk}
|
2017-12-24 18:15:21 +00:00
|
|
|
|
2018-03-16 22:31:08 +00:00
|
|
|
response = logged_in_api_client.post(url, data)
|
2018-03-18 20:51:49 +00:00
|
|
|
assert response.status_code == 201
|
2018-06-09 13:36:16 +00:00
|
|
|
plts = logged_in_api_client.user.playlists.latest("id").playlist_tracks.all()
|
2017-12-24 18:15:21 +00:00
|
|
|
assert plts.first().track == tracks[0]
|
2018-03-18 20:33:02 +00:00
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
@pytest.mark.parametrize(
|
|
|
|
"name,method",
|
|
|
|
[("api:v1:playlist-tracks-list", "post"), ("api:v1:playlists-list", "post")],
|
|
|
|
)
|
2018-03-18 20:33:02 +00:00
|
|
|
def test_url_requires_login(name, method, factories, api_client):
|
|
|
|
url = reverse(name)
|
|
|
|
|
|
|
|
response = getattr(api_client, method)(url, {})
|
|
|
|
|
|
|
|
assert response.status_code == 401
|
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
def test_only_can_add_track_on_own_playlist_via_api(factories, logged_in_api_client):
|
|
|
|
track = factories["music.Track"]()
|
|
|
|
playlist = factories["playlists.Playlist"]()
|
|
|
|
url = reverse("api:v1:playlist-tracks-list")
|
|
|
|
data = {"playlist": playlist.pk, "track": track.pk}
|
2018-03-18 20:33:02 +00:00
|
|
|
|
|
|
|
response = logged_in_api_client.post(url, data)
|
|
|
|
assert response.status_code == 400
|
|
|
|
assert playlist.playlist_tracks.count() == 0
|
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
def test_deleting_plt_updates_indexes(mocker, factories, logged_in_api_client):
|
|
|
|
remove = mocker.spy(models.Playlist, "remove")
|
|
|
|
track = factories["music.Track"]()
|
|
|
|
plt = factories["playlists.PlaylistTrack"](
|
|
|
|
index=0, playlist__user=logged_in_api_client.user
|
|
|
|
)
|
|
|
|
url = reverse("api:v1:playlist-tracks-detail", kwargs={"pk": plt.pk})
|
2018-03-19 14:28:33 +00:00
|
|
|
|
|
|
|
response = logged_in_api_client.delete(url)
|
|
|
|
|
|
|
|
assert response.status_code == 204
|
|
|
|
remove.assert_called_once_with(plt.playlist, 0)
|
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
@pytest.mark.parametrize("level", ["instance", "me", "followers"])
|
2018-03-28 21:48:34 +00:00
|
|
|
def test_playlist_privacy_respected_in_list_anon(
|
2018-06-09 13:36:16 +00:00
|
|
|
preferences, level, factories, api_client
|
|
|
|
):
|
|
|
|
preferences["common__api_authentication_required"] = False
|
|
|
|
factories["playlists.Playlist"](privacy_level=level)
|
|
|
|
url = reverse("api:v1:playlists-list")
|
2018-03-18 20:33:02 +00:00
|
|
|
response = api_client.get(url)
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response.data["count"] == 0
|
2018-03-18 20:33:02 +00:00
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
@pytest.mark.parametrize("method", ["PUT", "PATCH", "DELETE"])
|
2018-03-28 21:48:34 +00:00
|
|
|
def test_only_owner_can_edit_playlist(method, factories, logged_in_api_client):
|
2018-06-09 13:36:16 +00:00
|
|
|
playlist = factories["playlists.Playlist"]()
|
|
|
|
url = reverse("api:v1:playlists-detail", kwargs={"pk": playlist.pk})
|
2018-03-28 21:48:34 +00:00
|
|
|
response = getattr(logged_in_api_client, method.lower())(url)
|
2018-03-18 20:33:02 +00:00
|
|
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
@pytest.mark.parametrize("method", ["PUT", "PATCH", "DELETE"])
|
|
|
|
def test_only_owner_can_edit_playlist_track(method, factories, logged_in_api_client):
|
|
|
|
plt = factories["playlists.PlaylistTrack"]()
|
|
|
|
url = reverse("api:v1:playlist-tracks-detail", kwargs={"pk": plt.pk})
|
2018-03-28 21:48:34 +00:00
|
|
|
response = getattr(logged_in_api_client, method.lower())(url)
|
2018-03-18 20:33:02 +00:00
|
|
|
|
|
|
|
assert response.status_code == 404
|
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
@pytest.mark.parametrize("level", ["instance", "me", "followers"])
|
2018-03-18 20:33:02 +00:00
|
|
|
def test_playlist_track_privacy_respected_in_list_anon(
|
2018-06-09 13:36:16 +00:00
|
|
|
level, factories, api_client, preferences
|
|
|
|
):
|
|
|
|
preferences["common__api_authentication_required"] = False
|
|
|
|
factories["playlists.PlaylistTrack"](playlist__privacy_level=level)
|
|
|
|
url = reverse("api:v1:playlist-tracks-list")
|
2018-03-18 20:33:02 +00:00
|
|
|
response = api_client.get(url)
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response.data["count"] == 0
|
2018-03-18 20:51:49 +00:00
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
@pytest.mark.parametrize("level", ["instance", "me", "followers"])
|
|
|
|
def test_can_list_tracks_from_playlist(level, factories, logged_in_api_client):
|
|
|
|
plt = factories["playlists.PlaylistTrack"](playlist__user=logged_in_api_client.user)
|
|
|
|
url = reverse("api:v1:playlists-tracks", kwargs={"pk": plt.playlist.pk})
|
2018-03-18 20:51:49 +00:00
|
|
|
response = logged_in_api_client.get(url)
|
|
|
|
serialized_plt = serializers.PlaylistTrackSerializer(plt).data
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response.data["count"] == 1
|
|
|
|
assert response.data["results"][0] == serialized_plt
|
2018-03-20 18:56:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
def test_can_add_multiple_tracks_at_once_via_api(
|
2018-06-09 13:36:16 +00:00
|
|
|
factories, mocker, logged_in_api_client
|
|
|
|
):
|
|
|
|
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
|
|
|
tracks = factories["music.Track"].create_batch(size=5)
|
2018-03-20 18:56:42 +00:00
|
|
|
track_ids = [t.id for t in tracks]
|
2018-06-09 13:36:16 +00:00
|
|
|
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})
|
2018-03-20 18:56:42 +00:00
|
|
|
|
|
|
|
assert response.status_code == 201
|
|
|
|
assert playlist.playlist_tracks.count() == len(track_ids)
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
for plt in playlist.playlist_tracks.order_by("index"):
|
|
|
|
assert response.data["results"][plt.index]["id"] == plt.id
|
2018-03-20 18:56:42 +00:00
|
|
|
assert plt.track == tracks[plt.index]
|
2018-03-21 11:19:07 +00:00
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
def test_can_clear_playlist_from_api(factories, mocker, logged_in_api_client):
|
|
|
|
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
|
|
|
plts = factories["playlists.PlaylistTrack"].create_batch(size=5, playlist=playlist)
|
|
|
|
url = reverse("api:v1:playlists-clear", kwargs={"pk": playlist.pk})
|
2018-03-21 11:19:07 +00:00
|
|
|
response = logged_in_api_client.delete(url)
|
|
|
|
|
|
|
|
assert response.status_code == 204
|
|
|
|
assert playlist.playlist_tracks.count() == 0
|
2018-03-21 18:03:57 +00:00
|
|
|
|
|
|
|
|
2018-06-09 13:36:16 +00:00
|
|
|
def test_update_playlist_from_api(factories, mocker, logged_in_api_client):
|
|
|
|
playlist = factories["playlists.Playlist"](user=logged_in_api_client.user)
|
|
|
|
plts = 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"})
|
2018-03-21 18:03:57 +00:00
|
|
|
playlist.refresh_from_db()
|
|
|
|
|
|
|
|
assert response.status_code == 200
|
2018-06-09 13:36:16 +00:00
|
|
|
assert response.data["user"]["username"] == playlist.user.username
|