package persistenceimport ("context""github.com/navidrome/navidrome/log""github.com/navidrome/navidrome/model""github.com/navidrome/navidrome/model/request". "github.com/onsi/ginkgo/v2". "github.com/onsi/gomega")var _ = Describe("sqlBookmarks", func() {var mr model.MediaFileRepositoryvar ctx context.ContextBeforeEach(func() {ctx = request.WithUser(log.NewContext(GinkgoT().Context()), model.User{ID: "userid"})mr = NewMediaFileRepository(GetDBXBuilder())})Describe("Bookmarks", func() {It("returns an empty collection if there are no bookmarks", func() {Expect(mr.GetBookmarks(ctx)).To(BeEmpty())})It("saves and overrides bookmarks", func() {By("Saving the bookmark")Expect(mr.AddBookmark(ctx, songAntenna.ID, "this is a comment", 123)).To(BeNil())bms, err := mr.GetBookmarks(ctx)Expect(err).ToNot(HaveOccurred())Expect(bms).To(HaveLen(1))Expect(bms[0].Item.ID).To(Equal(songAntenna.ID))Expect(bms[0].Item.Title).To(Equal(songAntenna.Title))Expect(bms[0].Comment).To(Equal("this is a comment"))Expect(bms[0].Position).To(Equal(int64(123)))created := bms[0].CreatedAtupdated := bms[0].UpdatedAtExpect(created.IsZero()).To(BeFalse())Expect(updated).To(BeTemporally(">=", created))By("Overriding the bookmark")Expect(mr.AddBookmark(ctx, songAntenna.ID, "another comment", 333)).To(BeNil())bms, err = mr.GetBookmarks(ctx)Expect(err).ToNot(HaveOccurred())Expect(bms[0].Item.ID).To(Equal(songAntenna.ID))Expect(bms[0].Comment).To(Equal("another comment"))Expect(bms[0].Position).To(Equal(int64(333)))Expect(bms[0].CreatedAt).To(Equal(created))Expect(bms[0].UpdatedAt).To(BeTemporally(">=", updated))By("Saving another bookmark")Expect(mr.AddBookmark(ctx, songComeTogether.ID, "one more comment", 444)).To(BeNil())bms, err = mr.GetBookmarks(ctx)Expect(err).ToNot(HaveOccurred())Expect(bms).To(HaveLen(2))By("Delete bookmark")Expect(mr.DeleteBookmark(ctx, songAntenna.ID)).To(Succeed())bms, err = mr.GetBookmarks(ctx)Expect(err).ToNot(HaveOccurred())Expect(bms).To(HaveLen(1))Expect(bms[0].Item.ID).To(Equal(songComeTogether.ID))Expect(bms[0].Item.Title).To(Equal(songComeTogether.Title))Expect(mr.DeleteBookmark(ctx, songComeTogether.ID)).To(Succeed())Expect(mr.GetBookmarks(ctx)).To(BeEmpty())})})Describe("library access", func() {var otherLib model.Libraryvar restrictedUser model.Uservar adminCtx, userCtx context.Contextvar userMr model.MediaFileRepositoryBeforeEach(func() {adminCtx, otherLib, restrictedUser = restrictedFixture("bmk")adminMr := NewMediaFileRepository(GetDBXBuilder())Expect(adminMr.Put(adminCtx, &model.MediaFile{ID: "bmk-otherlib-track", LibraryID: otherLib.ID,Path: "hidden/bookmarked.mp3", Title: "Hidden Bookmarked",})).To(Succeed())DeferCleanup(func() { _ = adminMr.Delete(adminCtx, "bmk-otherlib-track") })userCtx = request.WithUser(ctx, restrictedUser)userMr = NewMediaFileRepository(GetDBXBuilder())})It("does not return bookmarks for tracks outside the user's libraries", func() {Expect(userMr.AddBookmark(userCtx, "bmk-otherlib-track", "sneaky", 1)).To(Succeed())Expect(userMr.GetBookmarks(userCtx)).To(BeEmpty())})It("still returns the bookmark for an admin", func() {adminMr := NewMediaFileRepository(GetDBXBuilder())Expect(adminMr.AddBookmark(adminCtx, "bmk-otherlib-track", "mine", 1)).To(Succeed())DeferCleanup(func() { _ = adminMr.DeleteBookmark(adminCtx, "bmk-otherlib-track") })bms, err := adminMr.GetBookmarks(adminCtx)Expect(err).ToNot(HaveOccurred())Expect(bms).To(HaveLen(1))Expect(bms[0].Item.ID).To(Equal("bmk-otherlib-track"))})It("keeps returning bookmarks for tracks inside the user's libraries", func() {Expect(userMr.AddBookmark(userCtx, songAntenna.ID, "allowed", 5)).To(Succeed())DeferCleanup(func() { _ = userMr.DeleteBookmark(userCtx, songAntenna.ID) })bms, err := userMr.GetBookmarks(userCtx)Expect(err).ToNot(HaveOccurred())Expect(bms).To(HaveLen(1))Expect(bms[0].Item.ID).To(Equal(songAntenna.ID))})})})