75ce65c80e
- NotFound wrapper routes unknown /api/* and /rest/* to a JSON 404 - Everything else falls through to the embedded web handler, which resolves static assets or returns index.html for SPA deep links
113 lines
3.2 KiB
Go
113 lines
3.2 KiB
Go
package server
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"log/slog"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/subsonic"
|
|
)
|
|
|
|
func TestHealthz(t *testing.T) {
|
|
s := New(slog.New(slog.NewTextHandler(io.Discard, nil)), nil, nil, subsonic.Config{})
|
|
ts := httptest.NewServer(s.Router())
|
|
defer ts.Close()
|
|
|
|
resp, err := http.Get(ts.URL + "/healthz")
|
|
if err != nil {
|
|
t.Fatalf("GET /healthz: %v", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("status = %d, want 200", resp.StatusCode)
|
|
}
|
|
var body map[string]string
|
|
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
if body["status"] != "ok" {
|
|
t.Errorf("body = %v, want status=ok", body)
|
|
}
|
|
}
|
|
|
|
func TestRouter_ServesSPAAtRoot(t *testing.T) {
|
|
s := New(slog.New(slog.NewTextHandler(io.Discard, nil)), nil, nil, subsonic.Config{})
|
|
ts := httptest.NewServer(s.Router())
|
|
defer ts.Close()
|
|
|
|
resp, err := http.Get(ts.URL + "/")
|
|
if err != nil {
|
|
t.Fatalf("GET /: %v", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("status = %d, want 200", resp.StatusCode)
|
|
}
|
|
if ct := resp.Header.Get("Content-Type"); !strings.Contains(ct, "text/html") {
|
|
t.Errorf("Content-Type = %q, want text/html*", ct)
|
|
}
|
|
}
|
|
|
|
func TestRouter_DeepLinkFallbackReturnsSPA(t *testing.T) {
|
|
s := New(slog.New(slog.NewTextHandler(io.Discard, nil)), nil, nil, subsonic.Config{})
|
|
ts := httptest.NewServer(s.Router())
|
|
defer ts.Close()
|
|
|
|
resp, err := http.Get(ts.URL + "/artists/00000000-0000-0000-0000-000000000001")
|
|
if err != nil {
|
|
t.Fatalf("GET deep link: %v", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
t.Errorf("status = %d, want 200", resp.StatusCode)
|
|
}
|
|
if ct := resp.Header.Get("Content-Type"); !strings.Contains(ct, "text/html") {
|
|
t.Errorf("Content-Type = %q, want text/html*", ct)
|
|
}
|
|
}
|
|
|
|
func TestRouter_APIPathNotSwallowedBySPA(t *testing.T) {
|
|
s := New(slog.New(slog.NewTextHandler(io.Discard, nil)), nil, nil, subsonic.Config{})
|
|
ts := httptest.NewServer(s.Router())
|
|
defer ts.Close()
|
|
|
|
resp, err := http.Get(ts.URL + "/api/does-not-exist")
|
|
if err != nil {
|
|
t.Fatalf("GET /api/does-not-exist: %v", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
if resp.StatusCode != http.StatusNotFound {
|
|
t.Errorf("status = %d, want 404", resp.StatusCode)
|
|
}
|
|
if ct := resp.Header.Get("Content-Type"); strings.Contains(ct, "text/html") {
|
|
t.Errorf("Content-Type = %q; /api/* must never return text/html", ct)
|
|
}
|
|
}
|
|
|
|
func TestRouter_RestPathNotSwallowedBySPA(t *testing.T) {
|
|
s := New(slog.New(slog.NewTextHandler(io.Discard, nil)), nil, nil, subsonic.Config{})
|
|
ts := httptest.NewServer(s.Router())
|
|
defer ts.Close()
|
|
|
|
resp, err := http.Get(ts.URL + "/rest/does-not-exist")
|
|
if err != nil {
|
|
t.Fatalf("GET /rest/does-not-exist: %v", err)
|
|
}
|
|
defer func() { _ = resp.Body.Close() }()
|
|
|
|
if resp.StatusCode != http.StatusNotFound {
|
|
t.Errorf("status = %d, want 404", resp.StatusCode)
|
|
}
|
|
if ct := resp.Header.Get("Content-Type"); strings.Contains(ct, "text/html") {
|
|
t.Errorf("Content-Type = %q; /rest/* must never return text/html", ct)
|
|
}
|
|
}
|