|
|
|
@@ -1,7 +1,11 @@
|
|
|
|
|
package e2e
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
"github.com/navidrome/navidrome/conf"
|
|
|
|
|
"github.com/navidrome/navidrome/model"
|
|
|
|
|
"github.com/navidrome/navidrome/model/criteria"
|
|
|
|
|
"github.com/navidrome/navidrome/server/subsonic/responses"
|
|
|
|
|
. "github.com/onsi/ginkgo/v2"
|
|
|
|
|
. "github.com/onsi/gomega"
|
|
|
|
@@ -15,9 +19,9 @@ var _ = Describe("Playlist Endpoints", Ordered, func() {
|
|
|
|
|
setupTestDB()
|
|
|
|
|
|
|
|
|
|
// Look up song IDs from scanned data for playlist operations
|
|
|
|
|
songs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{Sort: "title", Max: 3})
|
|
|
|
|
songs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{Sort: "title", Max: 6})
|
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
|
Expect(len(songs)).To(BeNumerically(">=", 3))
|
|
|
|
|
Expect(len(songs)).To(BeNumerically(">=", 5))
|
|
|
|
|
for _, s := range songs {
|
|
|
|
|
songIDs = append(songIDs, s.ID)
|
|
|
|
|
}
|
|
|
|
@@ -32,24 +36,30 @@ var _ = Describe("Playlist Endpoints", Ordered, func() {
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("createPlaylist creates a new playlist with songs", func() {
|
|
|
|
|
resp := doReq("createPlaylist", "name", "Test Playlist", "songId", songIDs[0], "songId", songIDs[1])
|
|
|
|
|
resp := doReq("createPlaylist", "name", "Test Playlist",
|
|
|
|
|
"songId", songIDs[0], "songId", songIDs[1], "songId", songIDs[2])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Test Playlist"))
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(3)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(3))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[0]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[1]))
|
|
|
|
|
Expect(resp.Playlist.Entry[2].Id).To(Equal(songIDs[2]))
|
|
|
|
|
playlistID = resp.Playlist.Id
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("getPlaylist returns playlist with tracks", func() {
|
|
|
|
|
It("getPlaylist returns playlist with tracks in order", func() {
|
|
|
|
|
resp := doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Test Playlist"))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(2))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(3))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[0]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[1]))
|
|
|
|
|
Expect(resp.Playlist.Entry[2].Id).To(Equal(songIDs[2]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("createPlaylist without name or playlistId returns error", func() {
|
|
|
|
@@ -59,40 +69,150 @@ var _ = Describe("Playlist Endpoints", Ordered, func() {
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("createPlaylist with playlistId replaces tracks on existing playlist", func() {
|
|
|
|
|
// Replace tracks: the playlist had [song0, song1, song2], replace with [song3, song4]
|
|
|
|
|
resp := doReq("createPlaylist", "playlistId", playlistID,
|
|
|
|
|
"songId", songIDs[3], "songId", songIDs[4])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Playlist.Id).To(Equal(playlistID))
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(2))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[3]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[4]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can rename the playlist", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID, "name", "Renamed Playlist")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
// Verify the rename
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Renamed Playlist"))
|
|
|
|
|
// Tracks should be unchanged
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can set comment", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID, "comment", "My favorite songs")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.Comment).To(Equal("My favorite songs"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can set public visibility", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID, "public", "true")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.Public).To(BeTrue())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can add songs", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID, "songIdToAdd", songIDs[2])
|
|
|
|
|
|
|
|
|
|
// Playlist currently has [song3, song4], add song0
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID, "songIdToAdd", songIDs[0])
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
// Verify the song was added
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(3)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(3))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[3]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[4]))
|
|
|
|
|
Expect(resp.Playlist.Entry[2].Id).To(Equal(songIDs[0]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can remove songs by index", func() {
|
|
|
|
|
// Remove the first song (index 0)
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID, "songIndexToRemove", "0")
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can add multiple songs at once", func() {
|
|
|
|
|
// Playlist currently has [song3, song4, song0], add song1 and song2
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID,
|
|
|
|
|
"songIdToAdd", songIDs[1], "songIdToAdd", songIDs[2])
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
// Verify the song was removed
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(5)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(5))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can remove songs by index and verifies correct songs remain", func() {
|
|
|
|
|
// Playlist has [song3, song4, song0, song1, song2]
|
|
|
|
|
// Remove index 0 (song3) and index 2 (song0)
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID,
|
|
|
|
|
"songIndexToRemove", "0", "songIndexToRemove", "2")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(3)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(3))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[4]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[1]))
|
|
|
|
|
Expect(resp.Playlist.Entry[2].Id).To(Equal(songIDs[2]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can remove and add songs in a single call", func() {
|
|
|
|
|
// Playlist has [song4, song1, song2]
|
|
|
|
|
// Remove index 1 (song1) and add song3
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID,
|
|
|
|
|
"songIndexToRemove", "1", "songIdToAdd", songIDs[3])
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(3)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(3))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[4]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[2]))
|
|
|
|
|
Expect(resp.Playlist.Entry[2].Id).To(Equal(songIDs[3]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can combine metadata change with track removal", func() {
|
|
|
|
|
// Playlist has [song4, song2, song3]
|
|
|
|
|
// Rename + remove index 0 (song4)
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID,
|
|
|
|
|
"name", "Final Playlist", "songIndexToRemove", "0")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Final Playlist"))
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(2))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[2]))
|
|
|
|
|
Expect(resp.Playlist.Entry[1].Id).To(Equal(songIDs[3]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can remove all songs from playlist", func() {
|
|
|
|
|
// Playlist has [song2, song3] — remove both
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID,
|
|
|
|
|
"songIndexToRemove", "0", "songIndexToRemove", "1")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(0)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(BeEmpty())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist can add songs to an empty playlist", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", playlistID,
|
|
|
|
|
"songIdToAdd", songIDs[0])
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", playlistID)
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(1)))
|
|
|
|
|
Expect(resp.Playlist.Entry).To(HaveLen(1))
|
|
|
|
|
Expect(resp.Playlist.Entry[0].Id).To(Equal(songIDs[0]))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist without playlistId returns error", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "name", "No ID")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("getPlaylists shows the playlist", func() {
|
|
|
|
|
resp := doReq("getPlaylists")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlists.Playlist).To(HaveLen(1))
|
|
|
|
|
Expect(resp.Playlists.Playlist[0].Id).To(Equal(playlistID))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("deletePlaylist removes the playlist", func() {
|
|
|
|
@@ -107,4 +227,294 @@ var _ = Describe("Playlist Endpoints", Ordered, func() {
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("getPlaylists returns empty after deletion", func() {
|
|
|
|
|
resp := doReq("getPlaylists")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlists.Playlist).To(BeEmpty())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
Describe("Playlist Permissions", Ordered, func() {
|
|
|
|
|
var songIDs []string
|
|
|
|
|
var adminPrivateID string
|
|
|
|
|
var adminPublicID string
|
|
|
|
|
var regularPlaylistID string
|
|
|
|
|
|
|
|
|
|
BeforeAll(func() {
|
|
|
|
|
setupTestDB()
|
|
|
|
|
|
|
|
|
|
songs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{Sort: "title", Max: 6})
|
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
|
Expect(len(songs)).To(BeNumerically(">=", 3))
|
|
|
|
|
for _, s := range songs {
|
|
|
|
|
songIDs = append(songIDs, s.ID)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("admin creates a private playlist", func() {
|
|
|
|
|
resp := doReqWithUser(adminUser, "createPlaylist", "name", "Admin Private",
|
|
|
|
|
"songId", songIDs[0], "songId", songIDs[1])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
adminPrivateID = resp.Playlist.Id
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("admin creates a public playlist", func() {
|
|
|
|
|
resp := doReqWithUser(adminUser, "createPlaylist", "name", "Admin Public",
|
|
|
|
|
"songId", songIDs[0], "songId", songIDs[1])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
adminPublicID = resp.Playlist.Id
|
|
|
|
|
|
|
|
|
|
// Make it public
|
|
|
|
|
resp = doReqWithUser(adminUser, "updatePlaylist",
|
|
|
|
|
"playlistId", adminPublicID, "public", "true")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user creates a playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "createPlaylist", "name", "Regular Playlist",
|
|
|
|
|
"songId", songIDs[0])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
regularPlaylistID = resp.Playlist.Id
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// --- Private playlist: regular user gets "not found" (repo hides it entirely) ---
|
|
|
|
|
|
|
|
|
|
It("regular user cannot see admin's private playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "getPlaylist", "id", adminPrivateID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot update admin's private playlist (not found)", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "updatePlaylist",
|
|
|
|
|
"playlistId", adminPrivateID, "name", "Hacked")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot delete admin's private playlist (not found)", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "deletePlaylist", "id", adminPrivateID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// --- Public playlist: regular user can see but cannot modify (authorization fail, code 50) ---
|
|
|
|
|
|
|
|
|
|
It("regular user can see admin's public playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "getPlaylist", "id", adminPublicID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Admin Public"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot update admin's public playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "updatePlaylist",
|
|
|
|
|
"playlistId", adminPublicID, "name", "Hacked")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot add songs to admin's public playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "updatePlaylist",
|
|
|
|
|
"playlistId", adminPublicID, "songIdToAdd", songIDs[2])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot remove songs from admin's public playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "updatePlaylist",
|
|
|
|
|
"playlistId", adminPublicID, "songIndexToRemove", "0")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot delete admin's public playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "deletePlaylist", "id", adminPublicID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user cannot replace tracks on admin's public playlist via createPlaylist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "createPlaylist",
|
|
|
|
|
"playlistId", adminPublicID, "songId", songIDs[2])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// --- Regular user can manage their own playlists ---
|
|
|
|
|
|
|
|
|
|
It("regular user can update their own playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "updatePlaylist",
|
|
|
|
|
"playlistId", regularPlaylistID, "name", "My Updated Playlist")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReqWithUser(regularUser, "getPlaylist", "id", regularPlaylistID)
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("My Updated Playlist"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user can add songs to their own playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "updatePlaylist",
|
|
|
|
|
"playlistId", regularPlaylistID, "songIdToAdd", songIDs[1])
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReqWithUser(regularUser, "getPlaylist", "id", regularPlaylistID)
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("regular user can delete their own playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "deletePlaylist", "id", regularPlaylistID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// --- Admin can manage any user's playlists ---
|
|
|
|
|
|
|
|
|
|
It("admin can update any user's playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "createPlaylist", "name", "To Be Admin-Edited",
|
|
|
|
|
"songId", songIDs[0])
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
plsID := resp.Playlist.Id
|
|
|
|
|
|
|
|
|
|
resp = doReqWithUser(adminUser, "updatePlaylist",
|
|
|
|
|
"playlistId", plsID, "name", "Admin Edited")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReqWithUser(adminUser, "getPlaylist", "id", plsID)
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Admin Edited"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("admin can delete any user's playlist", func() {
|
|
|
|
|
resp := doReqWithUser(regularUser, "createPlaylist", "name", "To Be Admin-Deleted",
|
|
|
|
|
"songId", songIDs[0])
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
plsID := resp.Playlist.Id
|
|
|
|
|
|
|
|
|
|
resp = doReqWithUser(adminUser, "deletePlaylist", "id", plsID)
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReqWithUser(adminUser, "getPlaylist", "id", plsID)
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// --- Verify admin's playlists are unchanged ---
|
|
|
|
|
|
|
|
|
|
It("admin's private playlist is unchanged after failed regular user operations", func() {
|
|
|
|
|
resp := doReqWithUser(adminUser, "getPlaylist", "id", adminPrivateID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Admin Private"))
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("admin's public playlist is unchanged after failed regular user operations", func() {
|
|
|
|
|
resp := doReqWithUser(adminUser, "getPlaylist", "id", adminPublicID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Admin Public"))
|
|
|
|
|
Expect(resp.Playlist.SongCount).To(Equal(int32(2)))
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
Describe("Smart Playlist Protection", Ordered, func() {
|
|
|
|
|
var smartPlaylistID string
|
|
|
|
|
var songID string
|
|
|
|
|
|
|
|
|
|
BeforeAll(func() {
|
|
|
|
|
setupTestDB()
|
|
|
|
|
|
|
|
|
|
// Look up a song ID for mutation tests
|
|
|
|
|
songs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{Sort: "title", Max: 1})
|
|
|
|
|
Expect(err).ToNot(HaveOccurred())
|
|
|
|
|
Expect(songs).ToNot(BeEmpty())
|
|
|
|
|
songID = songs[0].ID
|
|
|
|
|
|
|
|
|
|
// Insert a smart playlist directly into the DB
|
|
|
|
|
smartPls := &model.Playlist{
|
|
|
|
|
Name: "Smart Playlist",
|
|
|
|
|
OwnerID: adminUser.ID,
|
|
|
|
|
Public: false,
|
|
|
|
|
Rules: &criteria.Criteria{Expression: criteria.Contains{"title": ""}},
|
|
|
|
|
}
|
|
|
|
|
Expect(ds.Playlist(ctx).Put(smartPls)).To(Succeed())
|
|
|
|
|
smartPlaylistID = smartPls.ID
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("getPlaylist returns smart playlist with readonly flag and validUntil", func() {
|
|
|
|
|
resp := doReq("getPlaylist", "id", smartPlaylistID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Smart Playlist"))
|
|
|
|
|
Expect(resp.Playlist.OpenSubsonicPlaylist).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Playlist.OpenSubsonicPlaylist.Readonly).To(BeTrue())
|
|
|
|
|
expectedValidUntil := time.Now().Add(conf.Server.SmartPlaylistRefreshDelay)
|
|
|
|
|
Expect(*resp.Playlist.OpenSubsonicPlaylist.ValidUntil).To(BeTemporally("~", expectedValidUntil, time.Second))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("createPlaylist rejects replacing tracks on smart playlist", func() {
|
|
|
|
|
resp := doReq("createPlaylist", "playlistId", smartPlaylistID, "songId", songID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist rejects adding songs to smart playlist", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", smartPlaylistID,
|
|
|
|
|
"songIdToAdd", songID)
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist rejects removing songs from smart playlist", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", smartPlaylistID,
|
|
|
|
|
"songIndexToRemove", "0")
|
|
|
|
|
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
Expect(resp.Error).ToNot(BeNil())
|
|
|
|
|
Expect(resp.Error.Code).To(Equal(int32(50)))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist allows renaming smart playlist", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", smartPlaylistID,
|
|
|
|
|
"name", "Renamed Smart")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", smartPlaylistID)
|
|
|
|
|
Expect(resp.Playlist.Name).To(Equal("Renamed Smart"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("updatePlaylist allows setting comment on smart playlist", func() {
|
|
|
|
|
resp := doReq("updatePlaylist", "playlistId", smartPlaylistID,
|
|
|
|
|
"comment", "Auto-generated playlist")
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", smartPlaylistID)
|
|
|
|
|
Expect(resp.Playlist.Comment).To(Equal("Auto-generated playlist"))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
It("deletePlaylist can delete smart playlist", func() {
|
|
|
|
|
resp := doReq("deletePlaylist", "id", smartPlaylistID)
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusOK))
|
|
|
|
|
|
|
|
|
|
resp = doReq("getPlaylist", "id", smartPlaylistID)
|
|
|
|
|
Expect(resp.Status).To(Equal(responses.StatusFailed))
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|