package navidrome
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/delucks/go-subsonic"
"naviwatcher/internal/config"
)
func TestNewClient_ValidConfig(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
w.Write([]byte(`
`))
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
client, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
if client == nil {
t.Fatal("NewClient() returned nil client")
}
if client.client == nil {
t.Fatal("NewClient() returned client with nil subsonic client")
}
}
func TestNewClient_InvalidCredentials(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
w.Write([]byte(`
`))
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "baduser",
Password: "badpass",
}
_, err := NewClient(cfg)
if err == nil {
t.Fatal("NewClient() expected error for invalid credentials, got nil")
}
}
func TestPing_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
w.Write([]byte(`
`))
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
client, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
if err := client.Ping(); err != nil {
t.Errorf("Ping() error = %v", err)
}
}
func TestPing_ServerUnreachable(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
serverURL := server.URL
server.Close()
client := &NavidromeClient{
client: &subsonic.Client{
Client: &http.Client{},
BaseUrl: serverURL,
User: "testuser",
ClientName: "naviwatcher",
},
}
err := client.Ping()
if err == nil {
t.Error("Ping() expected error for unreachable server, got nil")
}
}
func TestPing_Non200Status(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusInternalServerError)
w.Write([]byte("Internal Server Error"))
}))
defer server.Close()
client := &NavidromeClient{
client: &subsonic.Client{
Client: &http.Client{},
BaseUrl: server.URL,
User: "testuser",
ClientName: "naviwatcher",
},
}
err := client.Ping()
if err == nil {
t.Error("Ping() expected error for non-200 status, got nil")
}
}
func TestGetArtists_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
if r.URL.Path == "/rest/getArtists" {
w.Write([]byte(`
`))
} else {
w.Write([]byte(`
`))
}
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
nc, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
artists, err := nc.GetArtists()
if err != nil {
t.Fatalf("GetArtists() error = %v", err)
}
if len(artists) != 3 {
t.Fatalf("GetArtists() returned %d artists, want 3", len(artists))
}
expected := []ArtistInfo{
{ID: "1", Name: "Artist One"},
{ID: "2", Name: "Artist Two"},
{ID: "3", Name: "Band Three"},
}
for i, a := range artists {
if a.ID != expected[i].ID || a.Name != expected[i].Name {
t.Errorf("GetArtists()[%d] = {ID: %q, Name: %q}, want {ID: %q, Name: %q}",
i, a.ID, a.Name, expected[i].ID, expected[i].Name)
}
}
}
func TestGetArtists_EmptyLibrary(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
w.Write([]byte(`
`))
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
nc, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
artists, err := nc.GetArtists()
if err != nil {
t.Fatalf("GetArtists() error = %v", err)
}
if len(artists) != 0 {
t.Errorf("GetArtists() returned %d artists, want 0", len(artists))
}
}
func TestGetArtists_APIError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
if r.URL.Path == "/rest/getArtists" {
w.Write([]byte(`
`))
return
}
w.Write([]byte(`
`))
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
nc, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
_, err = nc.GetArtists()
if err == nil {
t.Fatal("GetArtists() expected error for API failure, got nil")
}
}
func TestGetArtistAlbums_Success(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
if r.URL.Path == "/rest/getArtist" {
w.Write([]byte(`
`))
} else {
w.Write([]byte(`
`))
}
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
nc, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
albums, err := nc.GetArtistAlbums("1")
if err != nil {
t.Fatalf("GetArtistAlbums() error = %v", err)
}
if len(albums) != 2 {
t.Fatalf("GetArtistAlbums() returned %d albums, want 2", len(albums))
}
expected := []AlbumInfo{
{ID: "101", Name: "First Album", ArtistID: "1"},
{ID: "102", Name: "Second Album", ArtistID: "1"},
}
for i, a := range albums {
if a.ID != expected[i].ID || a.Name != expected[i].Name || a.ArtistID != expected[i].ArtistID {
t.Errorf("GetArtistAlbums()[%d] = {ID: %q, Name: %q, ArtistID: %q}, want {ID: %q, Name: %q, ArtistID: %q}",
i, a.ID, a.Name, a.ArtistID, expected[i].ID, expected[i].Name, expected[i].ArtistID)
}
}
}
func TestGetArtistAlbums_NoAlbums(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
if r.URL.Path == "/rest/getArtist" {
w.Write([]byte(`
`))
} else {
w.Write([]byte(`
`))
}
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
nc, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
albums, err := nc.GetArtistAlbums("5")
if err != nil {
t.Fatalf("GetArtistAlbums() error = %v", err)
}
if len(albums) != 0 {
t.Errorf("GetArtistAlbums() returned %d albums, want 0", len(albums))
}
}
func TestGetArtistAlbums_APIError(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/xml")
if r.URL.Path == "/rest/getArtist" {
w.Write([]byte(`
`))
return
}
w.Write([]byte(`
`))
}))
defer server.Close()
cfg := config.NavidromeConfig{
URL: server.URL,
User: "testuser",
Password: "testpass",
}
nc, err := NewClient(cfg)
if err != nil {
t.Fatalf("NewClient() error = %v", err)
}
_, err = nc.GetArtistAlbums("999")
if err == nil {
t.Fatal("GetArtistAlbums() expected error for API failure, got nil")
}
}