#!/usr/bin/env python # coding: utf-8 # # This file is part of Supysonic. # Supysonic is a Python implementation of the Subsonic server API. # # Copyright (C) 2017-2018 Alban 'spl0k' FĂ©ron # # Distributed under terms of the GNU AGPLv3 license. import uuid from pony.orm import db_session from supysonic.db import Folder, Artist, Album, Track, Playlist, User from .apitestbase import ApiTestBase class PlaylistTestCase(ApiTestBase): def setUp(self): super(PlaylistTestCase, self).setUp() with db_session: root = Folder(root = True, name = 'Root folder', path = 'tests/assets') artist = Artist(name = 'Artist') album = Album(name = 'Album', artist = artist) songs = {} for num, song in enumerate([ 'One', 'Two', 'Three', 'Four' ]): track = Track( disc = 1, number = num, title = song, duration = 2, album = album, artist = artist, bitrate = 320, path = 'tests/assets/' + song, content_type = 'audio/mpeg', last_modification = 0, root_folder = root, folder = root ) songs[song] = track users = { u.name: u for u in User.select() } playlist = Playlist(user = users['alice'], name = "Alice's") playlist.add(songs['One']) playlist.add(songs['Three']) playlist = Playlist(user = users['alice'], public = True, name = "Alice's public") playlist.add(songs['One']) playlist.add(songs['Two']) playlist = Playlist(user = users['bob'], name = "Bob's") playlist.add(songs['Two']) playlist.add(songs['Four']) def test_get_playlists(self): # get own playlists rv, child = self._make_request('getPlaylists', tag = 'playlists') self.assertEqual(len(child), 2) self.assertEqual(child[0].get('owner'), 'alice') self.assertEqual(child[1].get('owner'), 'alice') # get own and public rv, child = self._make_request('getPlaylists', { 'u': 'bob', 'p': 'B0b' }, tag = 'playlists') self.assertEqual(len(child), 2) self.assertTrue(child[0].get('owner') == 'alice' or child[1].get('owner') == 'alice') self.assertTrue(child[0].get('owner') == 'bob' or child[1].get('owner') == 'bob') self.assertIsNotNone(self._find(child, "./playlist[@owner='alice'][@public='true']")) # get other rv, child = self._make_request('getPlaylists', { 'username': 'bob' }, tag = 'playlists') self.assertEqual(len(child), 1) self.assertEqual(child[0].get('owner'), 'bob') # get other when not admin self._make_request('getPlaylists', { 'u': 'bob', 'p': 'B0b', 'username': 'alice' }, error = 50) # get from unknown user self._make_request('getPlaylists', { 'username': 'johndoe' }, error = 70) def test_get_playlist(self): # missing param self._make_request('getPlaylist', error = 10) # invalid id self._make_request('getPlaylist', { 'id': 1234 }, error = 0) # unknown self._make_request('getPlaylist', { 'id': str(uuid.uuid4()) }, error = 70) # other's private from non admin with db_session: playlist = Playlist.get(lambda p: not p.public and p.user.name == 'alice') self._make_request('getPlaylist', { 'u': 'bob', 'p': 'B0b', 'id': str(playlist.id) }, error = 50) # standard rv, child = self._make_request('getPlaylists', tag = 'playlists') self._make_request('getPlaylist', { 'id': child[0].get('id') }, tag = 'playlist') rv, child = self._make_request('getPlaylist', { 'id': child[1].get('id') }, tag = 'playlist') self.assertEqual(child.get('songCount'), '2') self.assertEqual(self._xpath(child, 'count(./entry)'), 2) # don't count children, there may be 'allowedUser's (even though not supported by supysonic) self.assertEqual(child.get('duration'), '4') self.assertEqual(child[0].get('title'), 'One') self.assertTrue(child[1].get('title') == 'Two' or child[1].get('title') == 'Three') # depending on 'getPlaylists' result ordering def test_create_playlist(self): self._make_request('createPlaylist', error = 10) self._make_request('createPlaylist', { 'name': 'wrongId', 'songId': 'abc' }, error = 0) self._make_request('createPlaylist', { 'name': 'unknownId', 'songId': str(uuid.uuid4()) }, error = 70) self._make_request('createPlaylist', { 'playlistId': 'abc' }, error = 0) self._make_request('createPlaylist', { 'playlistId': str(uuid.uuid4()) }, error = 70) # create self._make_request('createPlaylist', { 'name': 'new playlist' }, skip_post = True) rv, child = self._make_request('getPlaylists', tag = 'playlists') self.assertEqual(len(child), 3) playlist = self._find(child, "./playlist[@name='new playlist']") self.assertEqual(len(playlist), 0) # "update" newly created self._make_request('createPlaylist', { 'playlistId': playlist.get('id') }) rv, child = self._make_request('getPlaylists', tag = 'playlists') self.assertEqual(len(child), 3) # renaming self._make_request('createPlaylist', { 'playlistId': playlist.get('id'), 'name': 'renamed' }) rv, child = self._make_request('getPlaylists', tag = 'playlists') self.assertEqual(len(child), 3) self.assertIsNone(self._find(child, "./playlist[@name='new playlist']")) playlist = self._find(child, "./playlist[@name='renamed']") self.assertIsNotNone(playlist) # update from other user self._make_request('createPlaylist', { 'u': 'bob', 'p': 'B0b', 'playlistId': playlist.get('id') }, error = 50) # create more useful playlist with db_session: songs = { s.title: str(s.id) for s in Track.select() } self._make_request('createPlaylist', { 'name': 'songs', 'songId': list(map(lambda s: songs[s], [ 'Three', 'One', 'Two' ])) }, skip_post = True) with db_session: playlist = Playlist.get(name = 'songs') self.assertIsNotNone(playlist) rv, child = self._make_request('getPlaylist', { 'id': str(playlist.id) }, tag = 'playlist') self.assertEqual(child.get('songCount'), '3') self.assertEqual(self._xpath(child, 'count(./entry)'), 3) self.assertEqual(child[0].get('title'), 'Three') self.assertEqual(child[1].get('title'), 'One') self.assertEqual(child[2].get('title'), 'Two') # update self._make_request('createPlaylist', { 'playlistId': str(playlist.id), 'songId': songs['Two'] }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': str(playlist.id) }, tag = 'playlist') self.assertEqual(child.get('songCount'), '1') self.assertEqual(self._xpath(child, 'count(./entry)'), 1) self.assertEqual(child[0].get('title'), 'Two') @db_session def assertPlaylistCountEqual(self, count): self.assertEqual(Playlist.select().count(), count) def test_delete_playlist(self): # check params self._make_request('deletePlaylist', error = 10) self._make_request('deletePlaylist', { 'id': 'string' }, error = 0) self._make_request('deletePlaylist', { 'id': str(uuid.uuid4()) }, error = 70) # delete unowned when not admin with db_session: playlist = Playlist.select(lambda p: p.user.name == 'alice').first() self._make_request('deletePlaylist', { 'u': 'bob', 'p': 'B0b', 'id': str(playlist.id) }, error = 50) self.assertPlaylistCountEqual(3); # delete owned self._make_request('deletePlaylist', { 'id': str(playlist.id) }, skip_post = True) self.assertPlaylistCountEqual(2); self._make_request('deletePlaylist', { 'id': str(playlist.id) }, error = 70) self.assertPlaylistCountEqual(2); # delete unowned when admin with db_session: playlist = Playlist.get(lambda p: p.user.name == 'bob') self._make_request('deletePlaylist', { 'id': str(playlist.id) }, skip_post = True) self.assertPlaylistCountEqual(1); def test_update_playlist(self): self._make_request('updatePlaylist', error = 10) self._make_request('updatePlaylist', { 'playlistId': 1234 }, error = 0) self._make_request('updatePlaylist', { 'playlistId': str(uuid.uuid4()) }, error = 70) with db_session: playlist = Playlist.select(lambda p: p.user.name == 'alice').order_by(Playlist.created).first() pid = str(playlist.id) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIdToAdd': 'string' }, error = 0) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIndexToRemove': 'string' }, error = 0) name = str(playlist.name) self._make_request('updatePlaylist', { 'u': 'bob', 'p': 'B0b', 'playlistId': pid, 'name': 'new name' }, error = 50) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertEqual(child.get('name'), name) self.assertEqual(self._xpath(child, 'count(./entry)'), 2) self._make_request('updatePlaylist', { 'playlistId': pid, 'name': 'new name' }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertEqual(child.get('name'), 'new name') self.assertEqual(self._xpath(child, 'count(./entry)'), 2) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIndexToRemove': [ -1, 2 ] }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertEqual(self._xpath(child, 'count(./entry)'), 2) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIndexToRemove': [ 0, 2 ] }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertEqual(self._xpath(child, 'count(./entry)'), 1) self.assertEqual(self._find(child, './entry').get('title'), 'Three') with db_session: songs = { s.title: str(s.id) for s in Track.select() } self._make_request('updatePlaylist', { 'playlistId': pid, 'songIdToAdd': [ songs['One'], songs['Two'], songs['Two'] ] }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertSequenceEqual(self._xpath(child, './entry/@title'), [ 'Three', 'One', 'Two', 'Two' ]) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIndexToRemove': [ 2, 1 ] }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertSequenceEqual(self._xpath(child, './entry/@title'), [ 'Three', 'Two' ]) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIdToAdd': songs['One'] }, skip_post = True) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIndexToRemove': [ 1, 1 ] }, skip_post = True) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertSequenceEqual(self._xpath(child, './entry/@title'), [ 'Three', 'One' ]) self._make_request('updatePlaylist', { 'playlistId': pid, 'songIdToAdd': str(uuid.uuid4()) }, error = 70) rv, child = self._make_request('getPlaylist', { 'id': pid }, tag = 'playlist') self.assertEqual(self._xpath(child, 'count(./entry)'), 2) if __name__ == '__main__': unittest.main()