package subsonic import ( "encoding/json" "encoding/xml" "io" "net/http/httptest" "strings" "testing" ) func TestWriteJSONEnvelope(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/rest/ping.view", nil) Write(rec, req, PingResponse{Envelope: NewEnvelope("ok")}) if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "application/json") { t.Errorf("content-type = %q", ct) } var payload map[string]map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("unmarshal: %v — body=%s", err, rec.Body) } inner, ok := payload["subsonic-response"] if !ok { t.Fatalf("missing subsonic-response key: %s", rec.Body) } if inner["status"] != "ok" || inner["version"] != APIVersion || inner["type"] != ServerType { t.Errorf("envelope fields = %+v", inner) } if inner["openSubsonic"] != true { t.Errorf("openSubsonic flag missing: %+v", inner) } } func TestWriteXMLEnvelope(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/rest/getLicense.view?f=xml", nil) Write(rec, req, LicenseResponse{ Envelope: NewEnvelope("ok"), License: License{Valid: true}, }) if ct := rec.Header().Get("Content-Type"); !strings.HasPrefix(ct, "application/xml") { t.Errorf("content-type = %q", ct) } body, _ := io.ReadAll(rec.Body) var decoded struct { XMLName xml.Name `xml:"subsonic-response"` Status string `xml:"status,attr"` Version string `xml:"version,attr"` License struct { Valid bool `xml:"valid,attr"` } `xml:"license"` } if err := xml.Unmarshal(body, &decoded); err != nil { t.Fatalf("xml decode: %v — body=%s", err, body) } if decoded.Status != "ok" || decoded.Version != APIVersion { t.Errorf("envelope attrs = %+v", decoded) } if !decoded.License.Valid { t.Errorf("license.valid attr not parsed: %+v", decoded) } } func TestWriteFailEnvelopeJSON(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/rest/ping.view", nil) WriteFail(rec, req, ErrWrongCredentials, "nope") var payload map[string]map[string]any if err := json.Unmarshal(rec.Body.Bytes(), &payload); err != nil { t.Fatalf("unmarshal: %v", err) } inner := payload["subsonic-response"] if inner["status"] != "failed" { t.Errorf("status = %v, want failed", inner["status"]) } errMap, ok := inner["error"].(map[string]any) if !ok { t.Fatalf("error field missing/not object: %+v", inner) } if int(errMap["code"].(float64)) != ErrWrongCredentials { t.Errorf("error.code = %v, want %d", errMap["code"], ErrWrongCredentials) } if errMap["message"] != "nope" { t.Errorf("error.message = %v", errMap["message"]) } } func TestWriteJSONPWrapsCallback(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/rest/ping.view?f=jsonp&callback=cb", nil) Write(rec, req, PingResponse{Envelope: NewEnvelope("ok")}) body := rec.Body.String() if !strings.HasPrefix(body, "cb(") || !strings.HasSuffix(strings.TrimSpace(body), ")") { t.Errorf("jsonp wrapper missing: %q", body) } }