import json import pytest from django.urls import reverse from django.core.exceptions import ValidationError from django.utils import timezone from funkwhale_api.playlists import models from funkwhale_api.playlists import serializers def test_can_create_playlist_via_api(logged_in_api_client): url = reverse('api:v1:playlists-list') data = { 'name': 'test', 'privacy_level': 'everyone' } response = logged_in_api_client.post(url, data) playlist = logged_in_api_client.user.playlists.latest('id') assert playlist.name == 'test' assert playlist.privacy_level == 'everyone' def test_serializer_includes_tracks_count(factories, logged_in_api_client): playlist = factories['playlists.Playlist']() plt = factories['playlists.PlaylistTrack'](playlist=playlist) url = reverse('api:v1:playlists-detail', kwargs={'pk': playlist.pk}) response = logged_in_api_client.get(url) assert response.data['tracks_count'] == 1 def test_playlist_inherits_user_privacy(logged_in_api_client): url = reverse('api:v1:playlists-list') user = logged_in_api_client.user user.privacy_level = 'me' user.save() data = { 'name': 'test', } response = logged_in_api_client.post(url, data) playlist = user.playlists.latest('id') assert playlist.privacy_level == user.privacy_level def test_can_add_playlist_track_via_api(factories, logged_in_api_client): 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 } response = logged_in_api_client.post(url, data) assert response.status_code == 201 plts = logged_in_api_client.user.playlists.latest('id').playlist_tracks.all() assert plts.first().track == tracks[0] @pytest.mark.parametrize('name,method', [ ('api:v1:playlist-tracks-list', 'post'), ('api:v1:playlists-list', 'post'), ]) def test_url_requires_login(name, method, factories, api_client): url = reverse(name) response = getattr(api_client, method)(url, {}) assert response.status_code == 401 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 } response = logged_in_api_client.post(url, data) assert response.status_code == 400 assert playlist.playlist_tracks.count() == 0 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}) response = logged_in_api_client.delete(url) assert response.status_code == 204 remove.assert_called_once_with(plt.playlist, 0) @pytest.mark.parametrize('level', ['instance', 'me', 'followers']) def test_playlist_privacy_respected_in_list_anon( settings, level, factories, api_client): settings.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): 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 @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}) response = getattr(logged_in_api_client, method.lower())(url) assert response.status_code == 404 @pytest.mark.parametrize('level', ['instance', 'me', 'followers']) def test_playlist_track_privacy_respected_in_list_anon( level, factories, api_client, settings): settings.API_AUTHENTICATION_REQUIRED = False factories['playlists.PlaylistTrack'](playlist__privacy_level=level) url = reverse('api:v1:playlist-tracks-list') response = api_client.get(url) assert response.data['count'] == 0 @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}) response = logged_in_api_client.get(url) serialized_plt = serializers.PlaylistTrackSerializer(plt).data assert response.data['count'] == 1 assert response.data['results'][0] == serialized_plt def test_can_add_multiple_tracks_at_once_via_api( factories, mocker, logged_in_api_client): playlist = factories['playlists.Playlist'](user=logged_in_api_client.user) 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]['id'] == plt.id assert plt.track == tracks[plt.index] 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}) response = logged_in_api_client.delete(url) assert response.status_code == 204 assert playlist.playlist_tracks.count() == 0 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'}) playlist.refresh_from_db() assert response.status_code == 200 assert response.data['user']['username'] == playlist.user.username