Skip to content

Commit f7e005a

Browse files
committed
fix(server): ensure single record per user by reusing existing playqueue ID
Signed-off-by: Deluan <deluan@navidrome.org>
1 parent 410e457 commit f7e005a

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

persistence/playqueue_repository.go

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package persistence
22

33
import (
44
"context"
5+
"errors"
56
"strings"
67
"time"
78

@@ -38,6 +39,18 @@ type playQueue struct {
3839
func (r *playQueueRepository) Store(q *model.PlayQueue, colNames ...string) error {
3940
u := loggedUser(r.ctx)
4041

42+
// Always find existing playqueue for this user
43+
existingQueue, err := r.Retrieve(q.UserID)
44+
if err != nil && !errors.Is(err, model.ErrNotFound) {
45+
log.Error(r.ctx, "Error retrieving existing playqueue", "user", u.UserName, err)
46+
return err
47+
}
48+
49+
// Use existing ID if found, otherwise keep the provided ID (which may be empty for new records)
50+
if !errors.Is(err, model.ErrNotFound) && existingQueue.ID != "" {
51+
q.ID = existingQueue.ID
52+
}
53+
4154
// When no specific columns are provided, we replace the whole queue
4255
if len(colNames) == 0 {
4356
err := r.clearPlayQueue(q.UserID)
@@ -55,7 +68,7 @@ func (r *playQueueRepository) Store(q *model.PlayQueue, colNames ...string) erro
5568
pq.CreatedAt = time.Now()
5669
}
5770
pq.UpdatedAt = time.Now()
58-
_, err := r.put(pq.ID, pq, colNames...)
71+
_, err = r.put(pq.ID, pq, colNames...)
5972
if err != nil {
6073
log.Error(r.ctx, "Error saving playqueue", "user", u.UserName, err)
6174
return err

persistence/playqueue_repository_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,59 @@ var _ = Describe("PlayQueueRepository", func() {
169169
Expect(actual.Position).To(Equal(int64(200)))
170170
Expect(actual.Items).To(HaveLen(2)) // Should remain unchanged
171171
})
172+
173+
It("ensures only one record per user by reusing existing record ID", func() {
174+
By("Storing initial playqueue")
175+
initial := aPlayQueue("userid", 0, 100, songComeTogether)
176+
Expect(repo.Store(initial)).To(Succeed())
177+
initialCount := countPlayQueues(repo, "userid")
178+
Expect(initialCount).To(Equal(1))
179+
180+
By("Storing another playqueue with different ID but same user")
181+
different := aPlayQueue("userid", 1, 200, songDayInALife)
182+
different.ID = "different-id" // Force a different ID
183+
Expect(repo.Store(different)).To(Succeed())
184+
185+
By("Verifying only one record exists for the user")
186+
finalCount := countPlayQueues(repo, "userid")
187+
Expect(finalCount).To(Equal(1))
188+
189+
By("Verifying the record was updated, not duplicated")
190+
actual, err := repo.Retrieve("userid")
191+
Expect(err).ToNot(HaveOccurred())
192+
Expect(actual.Current).To(Equal(1)) // Should be updated value
193+
Expect(actual.Position).To(Equal(int64(200))) // Should be updated value
194+
Expect(actual.Items).To(HaveLen(1)) // Should be new items
195+
Expect(actual.Items[0].ID).To(Equal(songDayInALife.ID))
196+
})
197+
198+
It("ensures only one record per user even with partial updates", func() {
199+
By("Storing initial playqueue")
200+
initial := aPlayQueue("userid", 0, 100, songComeTogether, songDayInALife)
201+
Expect(repo.Store(initial)).To(Succeed())
202+
initialCount := countPlayQueues(repo, "userid")
203+
Expect(initialCount).To(Equal(1))
204+
205+
By("Storing partial update with different ID but same user")
206+
partialUpdate := &model.PlayQueue{
207+
ID: "completely-different-id", // Use a completely different ID
208+
UserID: "userid",
209+
Current: 1,
210+
ChangedBy: "test-partial",
211+
}
212+
Expect(repo.Store(partialUpdate, "current")).To(Succeed())
213+
214+
By("Verifying only one record still exists for the user")
215+
finalCount := countPlayQueues(repo, "userid")
216+
Expect(finalCount).To(Equal(1))
217+
218+
By("Verifying the existing record was updated with new current value")
219+
actual, err := repo.Retrieve("userid")
220+
Expect(err).ToNot(HaveOccurred())
221+
Expect(actual.Current).To(Equal(1)) // Should be updated value
222+
Expect(actual.Position).To(Equal(int64(100))) // Should remain unchanged
223+
Expect(actual.Items).To(HaveLen(2)) // Should remain unchanged
224+
})
172225
})
173226

174227
Describe("Retrieve", func() {

0 commit comments

Comments
 (0)