Files
wehub-resource-sync ead81af521
Deploy to GitHub Pages / deploy (push) Failing after 0s
CI / go (push) Has been cancelled
CI / build (darwin, macos-14) (push) Has been cancelled
CI / build (windows, windows-2025) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:31:13 +08:00

161 lines
4.5 KiB
Go

package radio
import (
"net/http"
"net/http/httptest"
"net/url"
"strings"
"testing"
"time"
)
type hostRewriter struct {
target *url.URL
rt http.RoundTripper
}
func (h hostRewriter) RoundTrip(req *http.Request) (*http.Response, error) {
clone := req.Clone(req.Context())
clone.URL.Scheme = h.target.Scheme
clone.URL.Host = h.target.Host
clone.Host = h.target.Host
return h.rt.RoundTrip(clone)
}
func installCatalogClient(t *testing.T, serverURL string) {
t.Helper()
u, err := url.Parse(serverURL)
if err != nil {
t.Fatalf("parse server URL: %v", err)
}
old := catalogClient
catalogClient = &http.Client{
Timeout: 5 * time.Second,
Transport: hostRewriter{target: u, rt: http.DefaultTransport},
}
t.Cleanup(func() { catalogClient = old })
}
func TestSearchStationsSuccess(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if !strings.Contains(r.URL.Path, "/stations/byname/") {
t.Errorf("unexpected path %q", r.URL.Path)
}
if !strings.Contains(r.URL.Path, "jazz") {
t.Errorf("path should contain query 'jazz': %q", r.URL.Path)
}
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[{"name":"Jazz FM","url_resolved":"https://jazz.example/stream","country":"UK","bitrate":128}]`))
}))
defer srv.Close()
installCatalogClient(t, srv.URL)
stations, err := SearchStations("jazz", 10)
if err != nil {
t.Fatalf("SearchStations: %v", err)
}
if len(stations) != 1 {
t.Fatalf("got %d stations, want 1", len(stations))
}
if stations[0].Name != "Jazz FM" {
t.Errorf("Name = %q, want Jazz FM", stations[0].Name)
}
if stations[0].URL != "https://jazz.example/stream" {
t.Errorf("URL = %q", stations[0].URL)
}
if stations[0].Bitrate != 128 {
t.Errorf("Bitrate = %d, want 128", stations[0].Bitrate)
}
}
func TestSearchStationsDefaultLimit(t *testing.T) {
var gotURL string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotURL = r.URL.String()
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[]`))
}))
defer srv.Close()
installCatalogClient(t, srv.URL)
if _, err := SearchStations("x", 0); err != nil {
t.Fatalf("SearchStations: %v", err)
}
if !strings.Contains(gotURL, "limit=50") {
t.Errorf("default limit should be 50, got URL %q", gotURL)
}
}
func TestSearchStationsHTTPError(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
http.Error(w, "down", http.StatusInternalServerError)
}))
defer srv.Close()
installCatalogClient(t, srv.URL)
_, err := SearchStations("jazz", 10)
if err == nil {
t.Error("SearchStations should return error on 500")
}
}
func TestTopStationsOffset(t *testing.T) {
var gotURL string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotURL = r.URL.String()
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[{"name":"Top1","url_resolved":"http://t1/","bitrate":64}]`))
}))
defer srv.Close()
installCatalogClient(t, srv.URL)
stations, err := TopStationsOffset(50, 25)
if err != nil {
t.Fatalf("TopStationsOffset: %v", err)
}
if len(stations) != 1 || stations[0].Name != "Top1" {
t.Fatalf("stations = %+v", stations)
}
if !strings.Contains(gotURL, "topvote/25") {
t.Errorf("URL %q should use limit 25", gotURL)
}
if !strings.Contains(gotURL, "offset=50") {
t.Errorf("URL %q should contain offset=50", gotURL)
}
}
func TestTopStationsOffsetClampsNegatives(t *testing.T) {
var gotURL string
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotURL = r.URL.String()
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`[]`))
}))
defer srv.Close()
installCatalogClient(t, srv.URL)
if _, err := TopStationsOffset(-10, 0); err != nil {
t.Fatalf("TopStationsOffset: %v", err)
}
if !strings.Contains(gotURL, "offset=0") {
t.Errorf("URL %q should clamp negative offset to 0", gotURL)
}
if !strings.Contains(gotURL, "topvote/50") {
t.Errorf("URL %q should use default limit 50", gotURL)
}
}
func TestFetchStationsInvalidJSON(t *testing.T) {
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(`{not valid`))
}))
defer srv.Close()
installCatalogClient(t, srv.URL)
_, err := SearchStations("x", 10)
if err == nil {
t.Error("SearchStations should error on invalid JSON")
}
}