package subsonic import ( "testing" "git.noroutes.net/NoRoutes/vibetunes/internal/database" ) func TestPlayQueueByIndexRoundTrip(t *testing.T) { s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", false) seedLibrary(t, s) songs, err := allSongIDs(t, s) if err != nil || len(songs) < 2 { t.Fatalf("need >=2 songs, got %v %v", songs, err) } q := baseParams("alice", "password123") q.Add("id", songs[0]) q.Add("id", songs[1]) q.Set("currentIndex", "1") q.Set("position", "5000") resp := getPath(t, srv.URL, "/rest/savePlayQueueByIndex.view", q) x := decodeXML(t, resp) if x.Status != "ok" { t.Fatalf("savePlayQueueByIndex failed: %+v", x) } q2 := baseParams("alice", "password123") q2.Set("f", "json") resp = getPath(t, srv.URL, "/rest/getPlayQueueByIndex.view", q2) j := decodeJSON(t, resp) pq, ok := j["playQueueByIndex"].(map[string]any) if !ok { t.Fatalf("playQueueByIndex missing: %v", j) } if int(pq["currentIndex"].(float64)) != 1 { t.Fatalf("currentIndex = %v, want 1", pq["currentIndex"]) } entries, ok := pq["entry"].([]any) if !ok || len(entries) != 2 { t.Fatalf("entries = %v, want 2", pq["entry"]) } } // TestPlayQueueByIndexHandlesDuplicateSongs is the whole point of this // extension: the same song ID can appear twice, and currentIndex // unambiguously identifies which occurrence is playing — unlike the // classic getPlayQueue's "current" song-ID field, which can't // distinguish between two occurrences of the same song. func TestPlayQueueByIndexHandlesDuplicateSongs(t *testing.T) { s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", false) seedLibrary(t, s) songs, err := allSongIDs(t, s) if err != nil || len(songs) == 0 { t.Fatalf("need >=1 song, got %v %v", songs, err) } same := songs[0] q := baseParams("alice", "password123") q.Add("id", same) q.Add("id", same) q.Add("id", same) q.Set("currentIndex", "2") // the third occurrence, not the first resp := getPath(t, srv.URL, "/rest/savePlayQueueByIndex.view", q) x := decodeXML(t, resp) if x.Status != "ok" { t.Fatalf("save failed: %+v", x) } q2 := baseParams("alice", "password123") q2.Set("f", "json") resp = getPath(t, srv.URL, "/rest/getPlayQueueByIndex.view", q2) j := decodeJSON(t, resp) pq := j["playQueueByIndex"].(map[string]any) if int(pq["currentIndex"].(float64)) != 2 { t.Fatalf("currentIndex = %v, want 2 (third occurrence)", pq["currentIndex"]) } entries := pq["entry"].([]any) if len(entries) != 3 { t.Fatalf("entries = %v, want 3 (duplicates preserved)", entries) } } func TestSavePlayQueueByIndexClears(t *testing.T) { s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", false) seedLibrary(t, s) songs, _ := allSongIDs(t, s) q := baseParams("alice", "password123") q.Add("id", songs[0]) q.Set("currentIndex", "0") getPath(t, srv.URL, "/rest/savePlayQueueByIndex.view", q) clear := baseParams("alice", "password123") resp := getPath(t, srv.URL, "/rest/savePlayQueueByIndex.view", clear) x := decodeXML(t, resp) if x.Status != "ok" { t.Fatalf("clear failed: %+v", x) } q2 := baseParams("alice", "password123") q2.Set("f", "json") resp = getPath(t, srv.URL, "/rest/getPlayQueueByIndex.view", q2) j := decodeJSON(t, resp) pq := j["playQueueByIndex"].(map[string]any) if _, ok := pq["entry"]; ok { t.Fatalf("expected empty queue after clearing, got %v", pq) } } func TestSavePlayQueueByIndexMissingCurrentIndex(t *testing.T) { s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", false) seedLibrary(t, s) songs, _ := allSongIDs(t, s) q := baseParams("alice", "password123") q.Add("id", songs[0]) resp := getPath(t, srv.URL, "/rest/savePlayQueueByIndex.view", q) x := decodeXML(t, resp) if x.Status != "failed" || x.Error == nil || x.Error.Code != ErrMissingParameter { t.Fatalf("expected error 10 when ids given without currentIndex, got %+v", x) } } func TestGetPlayQueueByIndexEmpty(t *testing.T) { s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", false) q := baseParams("alice", "password123") q.Set("f", "json") resp := getPath(t, srv.URL, "/rest/getPlayQueueByIndex.view", q) j := decodeJSON(t, resp) if j["status"] != "ok" { t.Fatalf("status = %v", j["status"]) } } func allSongIDs(t *testing.T, s *Server) ([]string, error) { t.Helper() songs, err := database.ListSongs(s.DB, 10, 0) if err != nil { return nil, err } ids := make([]string, len(songs)) for i, song := range songs { ids[i] = song.ID } return ids, nil }