Files
navidrome/server/e2e/subsonic_media_retrieval_test.go
T
Deluan 75e5bc4e81 refactor: rename spy to streamerSpy in e2e tests for clarity
Renamed the spy variable to streamerSpy across all e2e test files so
that its purpose is immediately clear without needing to look up the
declaration.
2026-03-10 17:19:25 -04:00

200 lines
5.9 KiB
Go

package e2e
import (
"net/http"
"github.com/navidrome/navidrome/conf"
"github.com/navidrome/navidrome/conf/configtest"
"github.com/navidrome/navidrome/model"
"github.com/navidrome/navidrome/server/subsonic/responses"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("Media Retrieval Endpoints", Ordered, func() {
BeforeAll(func() {
setupTestDB()
})
Describe("Stream", func() {
var trackID string
BeforeAll(func() {
// All test tracks are mp3 at 320kbps
songs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{Max: 1, Sort: "title"})
Expect(err).ToNot(HaveOccurred())
Expect(songs).ToNot(BeEmpty())
trackID = songs[0].ID
})
It("returns error when id parameter is missing", func() {
resp := doReq("stream")
Expect(resp.Status).To(Equal(responses.StatusFailed))
Expect(resp.Error).ToNot(BeNil())
})
It("streams raw when no format or bitrate specified", func() {
w := doRawReq("stream", "id", trackID)
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("raw"))
})
It("streams raw when format=raw", func() {
w := doRawReq("stream", "id", trackID, "format", "raw")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("raw"))
})
It("transcodes to different format with bitrate", func() {
w := doRawReq("stream", "id", trackID, "format", "opus", "maxBitRate", "128")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("opus"))
Expect(streamerSpy.LastRequest.BitRate).To(Equal(128))
})
It("downsamples when only maxBitRate is specified (lower than source)", func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.DefaultDownsamplingFormat = "opus"
w := doRawReq("stream", "id", trackID, "maxBitRate", "128")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("opus"))
Expect(streamerSpy.LastRequest.BitRate).To(Equal(128))
})
It("streams raw when maxBitRate is higher than source", func() {
w := doRawReq("stream", "id", trackID, "maxBitRate", "999")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("raw"))
})
It("streams raw when format matches source and no bitrate reduction", func() {
w := doRawReq("stream", "id", trackID, "format", "mp3", "maxBitRate", "320")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("raw"))
})
It("transcodes when same format but lower bitrate", func() {
w := doRawReq("stream", "id", trackID, "format", "mp3", "maxBitRate", "128")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("mp3"))
Expect(streamerSpy.LastRequest.BitRate).To(Equal(128))
})
It("falls back to raw for unknown format", func() {
w := doRawReq("stream", "id", trackID, "format", "xyz")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("raw"))
})
It("passes timeOffset through", func() {
w := doRawReq("stream", "id", trackID, "format", "opus", "maxBitRate", "128", "timeOffset", "30")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("opus"))
Expect(streamerSpy.LastRequest.Offset).To(Equal(30))
})
})
Describe("Download", func() {
var trackID string
BeforeAll(func() {
// All test tracks are mp3 at 320kbps
songs, err := ds.MediaFile(ctx).GetAll(model.QueryOptions{Max: 1, Sort: "title"})
Expect(err).ToNot(HaveOccurred())
Expect(songs).ToNot(BeEmpty())
trackID = songs[0].ID
})
It("returns error when id parameter is missing", func() {
resp := doReq("download")
Expect(resp.Status).To(Equal(responses.StatusFailed))
Expect(resp.Error).ToNot(BeNil())
})
It("downloads raw when no format specified and AutoTranscodeDownload is false", func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.EnableDownloads = true
conf.Server.AutoTranscodeDownload = false
w := doRawReq("download", "id", trackID)
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("raw"))
})
It("downloads with explicit format and bitrate", func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.EnableDownloads = true
w := doRawReq("download", "id", trackID, "format", "opus", "bitrate", "128")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(streamerSpy.LastRequest.Format).To(Equal("opus"))
Expect(streamerSpy.LastRequest.BitRate).To(Equal(128))
})
It("returns error when downloads are disabled", func() {
DeferCleanup(configtest.SetupConfig())
conf.Server.EnableDownloads = false
resp := doReq("download", "id", trackID)
Expect(resp.Status).To(Equal(responses.StatusFailed))
})
})
Describe("GetCoverArt", func() {
It("handles request without error", func() {
w := doRawReq("getCoverArt")
Expect(w.Code).To(Equal(http.StatusOK))
})
})
Describe("GetAvatar", func() {
It("returns placeholder avatar when gravatar disabled", func() {
w := doRawReq("getAvatar", "username", "admin")
Expect(w.Code).To(Equal(http.StatusOK))
})
})
Describe("GetLyrics", func() {
It("returns empty lyrics when no match found", func() {
resp := doReq("getLyrics", "artist", "NonExistentArtist", "title", "NonExistentTitle")
Expect(resp.Status).To(Equal(responses.StatusOK))
Expect(resp.Lyrics).ToNot(BeNil())
Expect(resp.Lyrics.Value).To(BeEmpty())
})
})
Describe("GetLyricsBySongId", func() {
It("returns error when id parameter is missing", func() {
resp := doReq("getLyricsBySongId")
Expect(resp.Status).To(Equal(responses.StatusFailed))
Expect(resp.Error).ToNot(BeNil())
})
It("returns error for non-existent song id", func() {
resp := doReq("getLyricsBySongId", "id", "non-existent-id")
Expect(resp.Status).To(Equal(responses.StatusFailed))
Expect(resp.Error).ToNot(BeNil())
})
})
})