package playerserver import ( "context" "encoding/json" "io/fs" "net" "net/http" "net/http/httptest" "strings" "testing" ) func newTestServer(np NowPlaying) *Server { return New("127.0.0.1:0", func() NowPlaying { return np }) } func TestHandlePlayerReturnsHTML(t *testing.T) { s := newTestServer(NowPlaying{Status: "running", Connectivity: "online"}) req := httptest.NewRequest(http.MethodGet, "/player", nil) w := httptest.NewRecorder() s.handlePlayer(w, req) if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "text/html") { t.Fatalf("Content-Type = %q, want text/html", ct) } body := w.Body.String() for _, want := range []string{"", "/api/now-playing", "/api/sysinfo", "splash-portrait.png", "splash-landscape.png"} { if !strings.Contains(body, want) { t.Fatalf("HTML missing %q", want) } } } func TestHandleNowPlayingWithURL(t *testing.T) { s := newTestServer(NowPlaying{ URL: "https://example.com", Status: "running", Connectivity: "online", }) req := httptest.NewRequest(http.MethodGet, "/api/now-playing", nil) w := httptest.NewRecorder() s.handleNowPlaying(w, req) var got NowPlaying if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatalf("Unmarshal() error = %v", err) } if got.URL != "https://example.com" { t.Fatalf("URL = %q, want https://example.com", got.URL) } if got.Connectivity != "online" { t.Fatalf("Connectivity = %q, want online", got.Connectivity) } } func TestHandleNowPlayingWithoutURL(t *testing.T) { s := newTestServer(NowPlaying{Status: "running", Connectivity: "degraded"}) req := httptest.NewRequest(http.MethodGet, "/api/now-playing", nil) w := httptest.NewRecorder() s.handleNowPlaying(w, req) var got NowPlaying if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatalf("Unmarshal() error = %v", err) } if got.URL != "" { t.Fatalf("URL = %q, want empty", got.URL) } } func TestHandleSysInfoReturnsItems(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/api/sysinfo", nil) w := httptest.NewRecorder() handleSysInfo(w, req) if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "application/json") { t.Fatalf("Content-Type = %q, want application/json", ct) } var got SysInfo if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatalf("Unmarshal() error = %v", err) } // At least hostname should always be present. if len(got.Items) == 0 { t.Fatal("expected at least one sysinfo item") } labels := make(map[string]bool) for _, item := range got.Items { labels[item.Label] = true if item.Value == "" { t.Errorf("item %q has empty value", item.Label) } } if !labels["Hostname"] { t.Error("expected Hostname item in sysinfo") } } func TestAssetsServed(t *testing.T) { s := New("127.0.0.1:0", func() NowPlaying { return NowPlaying{} }) ctx, cancel := context.WithCancel(context.Background()) defer cancel() ready := make(chan string, 1) go func() { ln, _ := net.Listen("tcp", "127.0.0.1:0") ready <- ln.Addr().String() ln.Close() }() // Use httptest recorder to test asset handler directly via the embed FS. sub, err := fs.Sub(assetsFS, "assets") if err != nil { t.Fatalf("fs.Sub error = %v", err) } handler := http.StripPrefix("/assets/", http.FileServer(http.FS(sub))) for _, name := range []string{"splash-landscape.png", "splash-portrait.png"} { req := httptest.NewRequest(http.MethodGet, "/assets/"+name, nil) w := httptest.NewRecorder() handler.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Errorf("GET /assets/%s = %d, want 200", name, w.Code) } if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "image/png") { t.Errorf("GET /assets/%s Content-Type = %q, want image/png", name, ct) } } _ = s _ = ctx } func TestServerRunAndStop(t *testing.T) { s := New("127.0.0.1:0", func() NowPlaying { return NowPlaying{Status: "running", Connectivity: "online"} }) ctx, cancel := context.WithCancel(context.Background()) errCh := make(chan error, 1) go func() { errCh <- s.Run(ctx) }() cancel() if err := <-errCh; err != nil { t.Fatalf("Run() error = %v", err) } }