mirror of
https://github.com/navidrome/navidrome.git
synced 2026-11-08 11:18:41 +01:00
Replace the single hardcoded "music" UserView with one CollectionFolder view per library the user can access, and scope every /Items browse query (albums, songs, artists, /Latest) to those libraries via server/filter's ApplyLibraryFilter/ApplyArtistLibraryFilter. ParentId is now disambiguated: a numeric value the user has access to is treated as a library scope (browsing a UserView), otherwise it falls through as an entity id (artist/album), which safely matches nothing rather than leaking another library's content. getItem now 404s when fetching an album or song outside the user's accessible libraries; artists are skipped (they can span multiple libraries) with a TODO. Genres remain unscoped since they're global tags, not per-library entities.
75 lines
2.6 KiB
Go
75 lines
2.6 KiB
Go
package jellyfin
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
|
|
"github.com/navidrome/navidrome/model"
|
|
"github.com/navidrome/navidrome/model/request"
|
|
"github.com/navidrome/navidrome/server/jellyfin/dto"
|
|
"github.com/navidrome/navidrome/tests"
|
|
. "github.com/onsi/ginkgo/v2"
|
|
. "github.com/onsi/gomega"
|
|
)
|
|
|
|
var _ = Describe("Users", func() {
|
|
var api *Router
|
|
authedWithLibraries := func(r *http.Request, libs model.Libraries) *http.Request {
|
|
ctx := request.WithUser(context.Background(), model.User{ID: "u1", UserName: "alice", Libraries: libs})
|
|
return r.WithContext(ctx)
|
|
}
|
|
BeforeEach(func() { api = &Router{ds: &tests.MockDataStore{}} })
|
|
|
|
Describe("getUserViews", func() {
|
|
It("returns one view per accessible library", func() {
|
|
libs := model.Libraries{{ID: 1, Name: "Music"}, {ID: 2, Name: "Podcasts"}}
|
|
w := httptest.NewRecorder()
|
|
api.getUserViews(w, authedWithLibraries(httptest.NewRequest("GET", "/UserViews", nil), libs))
|
|
Expect(w.Code).To(Equal(http.StatusOK))
|
|
var res dto.QueryResult
|
|
Expect(json.Unmarshal(w.Body.Bytes(), &res)).To(Succeed())
|
|
Expect(res.Items).To(HaveLen(2))
|
|
Expect(res.TotalRecordCount).To(Equal(2))
|
|
|
|
Expect(res.Items[0].Id).To(Equal("1"))
|
|
Expect(res.Items[0].Name).To(Equal("Music"))
|
|
Expect(res.Items[0].Type).To(Equal("CollectionFolder"))
|
|
Expect(res.Items[0].CollectionType).To(Equal("music"))
|
|
Expect(res.Items[0].IsFolder).To(BeTrue())
|
|
|
|
Expect(res.Items[1].Id).To(Equal("2"))
|
|
Expect(res.Items[1].Name).To(Equal("Podcasts"))
|
|
})
|
|
|
|
It("returns a single view for a user with one library", func() {
|
|
libs := model.Libraries{{ID: 1, Name: "Music"}}
|
|
w := httptest.NewRecorder()
|
|
api.getUserViews(w, authedWithLibraries(httptest.NewRequest("GET", "/UserViews", nil), libs))
|
|
Expect(w.Code).To(Equal(http.StatusOK))
|
|
var res dto.QueryResult
|
|
Expect(json.Unmarshal(w.Body.Bytes(), &res)).To(Succeed())
|
|
Expect(res.Items).To(HaveLen(1))
|
|
Expect(res.Items[0].Id).To(Equal("1"))
|
|
})
|
|
|
|
It("returns no views for a user with no library access", func() {
|
|
w := httptest.NewRecorder()
|
|
api.getUserViews(w, authedWithLibraries(httptest.NewRequest("GET", "/UserViews", nil), nil))
|
|
Expect(w.Code).To(Equal(http.StatusOK))
|
|
var res dto.QueryResult
|
|
Expect(json.Unmarshal(w.Body.Bytes(), &res)).To(Succeed())
|
|
Expect(res.Items).To(HaveLen(0))
|
|
Expect(res.TotalRecordCount).To(Equal(0))
|
|
})
|
|
})
|
|
|
|
It("returns the current user", func() {
|
|
w := httptest.NewRecorder()
|
|
api.getCurrentUser(w, authedWithLibraries(httptest.NewRequest("GET", "/Users/Me", nil), nil))
|
|
var u dto.UserDto
|
|
Expect(json.Unmarshal(w.Body.Bytes(), &u)).To(Succeed())
|
|
Expect(u.Name).To(Equal("alice"))
|
|
})
|
|
})
|