navidrome/server/jellyfin/e2e/system_test.go
Deluan Quintão aa0824e03b
feat(jellyfin): add System/Endpoint so Finamp's connection test passes (#5955)
Finamp's connection test GETs /System/Endpoint and treats anything other
than a 200 carrying an IsInNetwork key as "not a Jellyfin server", so the
test failed against Navidrome and dual-connection setups could never
switch to the local address.

IsInNetwork mirrors Jellyfin's default LAN set (NetworkManager with no
LocalNetworkSubnets configured): loopback, the RFC 1918 ranges, fc00::/7
and fe80::/10. Notably that set omits 169.254.0.0/16, so Go's
IsLinkLocalUnicast is deliberately restricted to its IPv6 half.

IsLocal mirrors HttpContext.IsLocal(): the caller shares the connection's
local address, not merely "is loopback". It falls back to the loopback
check when the local address is unavailable.
2026-08-14 09:17:00 -04:00

93 lines
2.8 KiB
Go

package e2e
import (
"net/http"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = Describe("System", func() {
BeforeEach(func() { setupTestDB() })
Describe("GET /System/Info/Public", func() {
It("returns public server info without authentication", func() {
w := rawReq("GET", "/System/Info/Public", "")
Expect(w.Code).To(Equal(http.StatusOK))
var info map[string]any
parseInto(w, &info)
Expect(info["ServerName"]).To(HavePrefix("Navidrome"))
Expect(info["ProductName"]).To(Equal("Jellyfin Server"))
Expect(info["StartupWizardCompleted"]).To(BeTrue())
Expect(info["Id"]).ToNot(BeEmpty())
Expect(info["Version"]).ToNot(BeEmpty())
})
It("routes case-insensitively (lowercase path)", func() {
w := rawReq("GET", "/system/info/public", "")
Expect(w.Code).To(Equal(http.StatusOK))
})
})
Describe("GET /System/Info", func() {
It("returns system info to any authenticated user, matching the public Version", func() {
w := getAs(regularUser, "/System/Info")
var info map[string]any
parseInto(w, &info)
Expect(info["Version"]).ToNot(BeEmpty())
Expect(info["SupportsLibraryMonitor"]).To(BeTrue())
pub := rawReq("GET", "/System/Info/Public", "")
var pubInfo map[string]any
parseInto(pub, &pubInfo)
Expect(info["Version"]).To(Equal(pubInfo["Version"]))
Expect(info["Id"]).To(Equal(pubInfo["Id"]))
})
It("rejects unauthenticated requests", func() {
w := rawReq("GET", "/System/Info", "")
Expect(w.Code).To(Equal(http.StatusUnauthorized))
})
})
Describe("GET /System/Endpoint", func() {
It("always reports IsInNetwork, which Finamp's connection test probes for", func() {
w := getAs(regularUser, "/System/Endpoint")
Expect(w.Code).To(Equal(http.StatusOK))
var info map[string]any
parseInto(w, &info)
Expect(info).To(HaveKey("IsInNetwork"))
Expect(info).To(HaveKey("IsLocal"))
})
It("rejects unauthenticated requests", func() {
w := rawReq("GET", "/System/Endpoint", "")
Expect(w.Code).To(Equal(http.StatusUnauthorized))
})
})
Describe("GET/POST /System/Ping", func() {
It("answers GET with a plain-text server name", func() {
w := rawReq("GET", "/System/Ping", "")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(w.Header().Get("Content-Type")).To(HavePrefix("text/plain"))
Expect(w.Body.String()).To(HavePrefix("Navidrome"))
})
It("answers POST identically", func() {
w := rawReq("POST", "/System/Ping", "")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(strings.TrimSpace(w.Body.String())).To(HavePrefix("Navidrome"))
})
})
Describe("GET /QuickConnect/Enabled", func() {
It("reports QuickConnect disabled", func() {
w := rawReq("GET", "/QuickConnect/Enabled", "")
Expect(w.Code).To(Equal(http.StatusOK))
Expect(strings.TrimSpace(w.Body.String())).To(Equal("false"))
})
})
})