package subsonic import ( "net/http/httptest" "os/exec" "testing" "time" "git.noroutes.net/NoRoutes/vibetunes/internal/database" ) func requireMPVForTest(t *testing.T) { t.Helper() if _, err := exec.LookPath("mpv"); err != nil { t.Skip("mpv not installed") } } func TestJukeboxControlLifecycle(t *testing.T) { requireMPVForTest(t) s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", true) seedLibrary(t, s) songs := []string{} q0 := baseParams("alice", "password123") q0.Set("f", "json") resp := getPath(t, srv.URL, "/rest/getRandomSongs.view", q0) j := decodeJSON(t, resp) for _, s := range j["randomSongs"].(map[string]any)["song"].([]any) { songs = append(songs, s.(map[string]any)["id"].(string)) } if len(songs) == 0 { t.Fatal("no songs available to jukebox") } q := baseParams("alice", "password123") q.Set("action", "set") for _, id := range songs { q.Add("id", id) } q.Set("f", "json") resp = getPath(t, srv.URL, "/rest/jukeboxControl.view", q) j = decodeJSON(t, resp) status, ok := j["jukeboxStatus"].(map[string]any) if !ok { t.Fatalf("expected jukeboxStatus in response: %v", j) } _ = status time.Sleep(200 * time.Millisecond) q2 := baseParams("alice", "password123") q2.Set("action", "get") q2.Set("f", "json") resp = getPath(t, srv.URL, "/rest/jukeboxControl.view", q2) j = decodeJSON(t, resp) playlist, ok := j["jukeboxPlaylist"].(map[string]any) if !ok { t.Fatalf("expected jukeboxPlaylist for action=get: %v", j) } entries, ok := playlist["entry"].([]any) if !ok || len(entries) != len(songs) { t.Fatalf("jukeboxPlaylist entries = %v, want %d", playlist["entry"], len(songs)) } q3 := baseParams("alice", "password123") q3.Set("action", "stop") resp = getPath(t, srv.URL, "/rest/jukeboxControl.view", q3) x := decodeXML(t, resp) if x.Status != "ok" { t.Fatalf("stop failed: %+v", x) } q4 := baseParams("alice", "password123") q4.Set("action", "setGain") q4.Set("gain", "0.3") q4.Set("f", "json") resp = getPath(t, srv.URL, "/rest/jukeboxControl.view", q4) j = decodeJSON(t, resp) status = j["jukeboxStatus"].(map[string]any) if gain, _ := status["gain"].(float64); gain < 0.29 || gain > 0.31 { t.Fatalf("gain = %v, want ~0.3", status["gain"]) } q5 := baseParams("alice", "password123") q5.Set("action", "clear") resp = getPath(t, srv.URL, "/rest/jukeboxControl.view", q5) x = decodeXML(t, resp) if x.Status != "ok" { t.Fatalf("clear failed: %+v", x) } } func TestJukeboxControlRequiresPermission(t *testing.T) { requireMPVForTest(t) s, db := newTestServer(t) u := createTestUser(t, s, "alice", "password123", false) srv := httptest.NewServer(s.Router()) defer srv.Close() perms := u.Permissions perms.Jukebox = false if err := database.SetUserPermissions(db, u.ID, perms); err != nil { t.Fatal(err) } q := baseParams("alice", "password123") q.Set("action", "status") resp := getPath(t, srv.URL, "/rest/jukeboxControl.view", q) x := decodeXML(t, resp) if x.Status != "failed" || x.Error == nil || x.Error.Code != ErrUnauthorized { t.Fatalf("expected error 50 without jukebox permission, got %+v", x) } } func TestJukeboxControlMissingAction(t *testing.T) { requireMPVForTest(t) s, srv := newTestRecorderServer(t) createTestUser(t, s, "alice", "password123", true) resp := getPath(t, srv.URL, "/rest/jukeboxControl.view", baseParams("alice", "password123")) x := decodeXML(t, resp) if x.Status != "failed" || x.Error == nil || x.Error.Code != ErrMissingParameter { t.Fatalf("expected error 10, got %+v", x) } }