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") } }