morz-infoboard/player/agent/internal/playerserver/server_test.go
Jesko Anschütz 6bc4d3d2f8 Fix: Protokoll-relative URLs, PDF-Fragment-Merge, Startup-Token-Cache, Test-Nil-Deref
- URL-Normalisierung überspringt jetzt //protocol-relative URLs
- PDF-Viewer-Parameter werden mit bestehenden Fragments gemerged statt blind angehängt
- /api/startup-token setzt Cache-Control: no-store (Server + Client)
- Tote Goroutine mit ignoriertem net.Listen-Error aus TestAssetsServed entfernt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 15:21:26 +01:00

133 lines
3.8 KiB
Go

package playerserver
import (
"context"
"encoding/json"
"io/fs"
"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{"<!DOCTYPE html>", "/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) {
// 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)
}
}
}
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)
}
}