* Initial commit of internet radio support. * Added first test for internet radio. * Refactor to prepare for more test cases. * Added a few more tests. Realized that I was not calling as admin so added ability to mock admin. * Added more internet radio tests. Added proper JSON unmarshaling for ID. * More test cases. Fixed some accidental tabs in files. * Fixed some more tabs. * lint fixes * Changed placeholder for homepage URL to fit into box. * Finished out internet radio test cases. Found a few bad error codes in internet radio AND podcasts (mea culpa). * Realized that delete via website was not checking properly if id existed. Fixed. gofmt
381 lines
15 KiB
Go
381 lines
15 KiB
Go
package ctrlsubsonic
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"testing"
|
|
|
|
"go.senan.xyz/gonic/server/ctrlsubsonic/spec"
|
|
"go.senan.xyz/gonic/server/ctrlsubsonic/specid"
|
|
)
|
|
|
|
const station1ID = "ir-1"
|
|
|
|
var station1IDT = specid.ID{Type: specid.InternetRadioStation, Value: 1}
|
|
|
|
const (
|
|
station1StreamURL = "http://lyd.nrk.no/nrk_radio_p1_ostlandssendingen_mp3_m"
|
|
station1Name = "NRK P1"
|
|
station1HomepageURL = "http://www.nrk.no/p1"
|
|
)
|
|
|
|
const station2ID = "ir-2"
|
|
|
|
var station2IDT = specid.ID{Type: specid.InternetRadioStation, Value: 2}
|
|
|
|
const (
|
|
station2StreamURL = "http://lyd.nrk.no/nrk_radio_p2_mp3_m"
|
|
station2Name = "NRK P2"
|
|
station2HomepageURL = "http://p3.no"
|
|
)
|
|
|
|
const (
|
|
newstation1StreamURL = "http://media.kcrw.com/pls/kcrwmusic.pls"
|
|
newstation1Name = "KCRW Eclectic"
|
|
newstation1HomepageURL = "https://www.kcrw.com/music/shows/eclectic24"
|
|
)
|
|
|
|
const newstation2StreamURL = "http://media.kcrw.com/pls/kcrwsantabarbara.pls"
|
|
const newstation2Name = "KCRW Santa Barbara"
|
|
|
|
const station3ID = "ir-3"
|
|
|
|
const notAURL = "not_a_url"
|
|
|
|
func TestInternetRadio(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
contr := makeController(t)
|
|
t.Run("TestInternetRadioInitialEmpty", func(t *testing.T) { testInternetRadioInitialEmpty(t, contr) })
|
|
t.Run("TestInternetRadioBadCreates", func(t *testing.T) { testInternetRadioBadCreates(t, contr) })
|
|
t.Run("TestInternetRadioInitialAdds", func(t *testing.T) { testInternetRadioInitialAdds(t, contr) })
|
|
t.Run("TestInternetRadioUpdateHomepage", func(t *testing.T) { testInternetRadioUpdateHomepage(t, contr) })
|
|
t.Run("TestInternetRadioNotAdmin", func(t *testing.T) { testInternetRadioNotAdmin(t, contr) })
|
|
t.Run("TestInternetRadioUpdates", func(t *testing.T) { testInternetRadioUpdates(t, contr) })
|
|
t.Run("TestInternetRadioDeletes", func(t *testing.T) { testInternetRadioDeletes(t, contr) })
|
|
}
|
|
|
|
func runTestCase(t *testing.T, contr *Controller, h handlerSubsonic, q url.Values, admin bool) *spec.SubsonicResponse {
|
|
var rr *httptest.ResponseRecorder
|
|
var req *http.Request
|
|
|
|
if admin {
|
|
rr, req = makeHTTPMockWithAdmin(q)
|
|
} else {
|
|
rr, req = makeHTTPMock(q)
|
|
}
|
|
contr.H(h).ServeHTTP(rr, req)
|
|
body := rr.Body.String()
|
|
if status := rr.Code; status != http.StatusOK {
|
|
t.Fatalf("didn't give a 200\n%s", body)
|
|
}
|
|
|
|
var response spec.SubsonicResponse
|
|
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
|
switch ty := err.(type) {
|
|
case *json.SyntaxError:
|
|
jsn := body[0:ty.Offset]
|
|
jsn += "<--(Invalid Character)"
|
|
t.Fatalf("invalid character at offset %v\n %s", ty.Offset, jsn)
|
|
case *json.UnmarshalTypeError:
|
|
jsn := body[0:ty.Offset]
|
|
jsn += "<--(Invalid Type)"
|
|
t.Fatalf("invalid type at offset %v\n %s", ty.Offset, jsn)
|
|
default:
|
|
t.Fatalf("json unmarshal failed: %s", err.Error())
|
|
}
|
|
}
|
|
|
|
return &response
|
|
}
|
|
|
|
func checkSuccess(t *testing.T, response *spec.SubsonicResponse) {
|
|
t.Helper()
|
|
|
|
if response.Response.Status != "ok" {
|
|
t.Fatal("didn't return ok status")
|
|
}
|
|
}
|
|
|
|
func checkError(t *testing.T, response *spec.SubsonicResponse, code int) {
|
|
t.Helper()
|
|
|
|
if response.Response.Status != "failed" {
|
|
t.Fatal("didn't return failed status")
|
|
}
|
|
if response.Response.Error.Code != code {
|
|
t.Fatal("returned wrong error code")
|
|
}
|
|
}
|
|
|
|
func checkMissingParameter(t *testing.T, response *spec.SubsonicResponse) {
|
|
t.Helper()
|
|
checkError(t, response, 10)
|
|
}
|
|
|
|
func checkBadParameter(t *testing.T, response *spec.SubsonicResponse) {
|
|
t.Helper()
|
|
checkError(t, response, 70)
|
|
}
|
|
|
|
func checkNotAdmin(t *testing.T, response *spec.SubsonicResponse) {
|
|
t.Helper()
|
|
checkError(t, response, 50)
|
|
}
|
|
|
|
func testInternetRadioBadCreates(t *testing.T, contr *Controller) {
|
|
var response *spec.SubsonicResponse
|
|
|
|
// no parameters
|
|
response = runTestCase(t, contr, contr.ServeCreateInternetRadioStation, url.Values{}, true)
|
|
checkMissingParameter(t, response)
|
|
|
|
// just one required parameter
|
|
response = runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"streamUrl": {station1StreamURL}}, true)
|
|
checkMissingParameter(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"name": {station1Name}}, true)
|
|
checkMissingParameter(t, response)
|
|
|
|
// bad URLs
|
|
response = runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"streamUrl": {station1StreamURL}, "name": {station1Name}, "homepageUrl": {notAURL}}, true)
|
|
checkBadParameter(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"streamUrl": {notAURL}, "name": {station1Name}, "homepageUrl": {station1HomepageURL}}, true)
|
|
checkBadParameter(t, response)
|
|
|
|
// check for empty get after
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if (response.Response.InternetRadioStations == nil) || (len(response.Response.InternetRadioStations.List) != 0) {
|
|
t.Fatal("didn't return empty stations")
|
|
}
|
|
}
|
|
|
|
func testInternetRadioInitialEmpty(t *testing.T, contr *Controller) {
|
|
// check for empty get on new DB
|
|
response := runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if (response.Response.InternetRadioStations == nil) || (len(response.Response.InternetRadioStations.List) != 0) {
|
|
t.Fatal("didn't return empty stations")
|
|
}
|
|
}
|
|
|
|
func testInternetRadioInitialAdds(t *testing.T, contr *Controller) {
|
|
// successful adds and read back
|
|
response := runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"streamUrl": {station1StreamURL}, "name": {station1Name}, "homepageUrl": {station1HomepageURL}}, true)
|
|
checkSuccess(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"streamUrl": {station2StreamURL}, "name": {station2Name}}, true) // NOTE: no homepage Url
|
|
checkSuccess(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 2 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station1IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != station1StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != station1Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != station1HomepageURL) ||
|
|
(*response.Response.InternetRadioStations.List[1].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[1].StreamURL != station2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[1].Name != station2Name) ||
|
|
(response.Response.InternetRadioStations.List[1].HomepageURL != "") {
|
|
t.Fatal("bad data")
|
|
}
|
|
}
|
|
|
|
func testInternetRadioUpdateHomepage(t *testing.T, contr *Controller) {
|
|
// update empty homepage URL without other parameters (fails)
|
|
response := runTestCase(t, contr, contr.ServeUpdateInternetRadioStation,
|
|
url.Values{"id": {station2ID}, "homepageUrl": {station2HomepageURL}}, true)
|
|
checkMissingParameter(t, response)
|
|
|
|
// update empty homepage URL properly and read back
|
|
response = runTestCase(t, contr, contr.ServeUpdateInternetRadioStation,
|
|
url.Values{"id": {station2ID}, "streamUrl": {station2StreamURL}, "name": {station2Name}, "homepageUrl": {station2HomepageURL}}, true)
|
|
checkSuccess(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 2 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station1IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != station1StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != station1Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != station1HomepageURL) ||
|
|
(*response.Response.InternetRadioStations.List[1].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[1].StreamURL != station2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[1].Name != station2Name) ||
|
|
(response.Response.InternetRadioStations.List[1].HomepageURL != station2HomepageURL) {
|
|
t.Fatal("bad data")
|
|
}
|
|
}
|
|
|
|
func testInternetRadioNotAdmin(t *testing.T, contr *Controller) {
|
|
// create, update, delete w/o admin privileges (fails and does not modify data)
|
|
response := runTestCase(t, contr, contr.ServeCreateInternetRadioStation,
|
|
url.Values{"streamUrl": {station1StreamURL}, "name": {station1Name}, "homepageUrl": {station1HomepageURL}}, false)
|
|
checkNotAdmin(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeUpdateInternetRadioStation,
|
|
url.Values{"id": {station1ID}, "streamUrl": {newstation1StreamURL}, "name": {newstation1Name}, "homepageUrl": {newstation1HomepageURL}}, false)
|
|
checkNotAdmin(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeDeleteInternetRadioStation,
|
|
url.Values{"id": {station1ID}}, false)
|
|
checkNotAdmin(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 2 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station1IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != station1StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != station1Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != station1HomepageURL) ||
|
|
(*response.Response.InternetRadioStations.List[1].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[1].StreamURL != station2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[1].Name != station2Name) ||
|
|
(response.Response.InternetRadioStations.List[1].HomepageURL != station2HomepageURL) {
|
|
t.Fatal("bad data")
|
|
}
|
|
}
|
|
|
|
func testInternetRadioUpdates(t *testing.T, contr *Controller) {
|
|
// replace station 1 and read back
|
|
response := runTestCase(t, contr, contr.ServeUpdateInternetRadioStation,
|
|
url.Values{"id": {station1ID}, "streamUrl": {newstation1StreamURL}, "name": {newstation1Name}, "homepageUrl": {newstation1HomepageURL}}, true)
|
|
|
|
checkSuccess(t, response)
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 2 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station1IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != newstation1StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != newstation1Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != newstation1HomepageURL) ||
|
|
(*response.Response.InternetRadioStations.List[1].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[1].StreamURL != station2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[1].Name != station2Name) ||
|
|
(response.Response.InternetRadioStations.List[1].HomepageURL != station2HomepageURL) {
|
|
t.Fatal("bad data")
|
|
}
|
|
|
|
// update station 2 but without homepage URL and read back
|
|
response = runTestCase(t, contr, contr.ServeUpdateInternetRadioStation,
|
|
url.Values{"id": {station2ID}, "streamUrl": {newstation2StreamURL}, "name": {newstation2Name}}, true)
|
|
checkSuccess(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 2 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station1IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != newstation1StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != newstation1Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != newstation1HomepageURL) ||
|
|
(*response.Response.InternetRadioStations.List[1].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[1].StreamURL != newstation2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[1].Name != newstation2Name) ||
|
|
(response.Response.InternetRadioStations.List[1].HomepageURL != "") {
|
|
t.Fatal("bad data")
|
|
}
|
|
}
|
|
|
|
func testInternetRadioDeletes(t *testing.T, contr *Controller) {
|
|
// delete non-existent station 3 (fails and does not modify data)
|
|
response := runTestCase(t, contr, contr.ServeDeleteInternetRadioStation,
|
|
url.Values{"id": {station3ID}}, true)
|
|
checkBadParameter(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 2 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station1IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != newstation1StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != newstation1Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != newstation1HomepageURL) ||
|
|
(*response.Response.InternetRadioStations.List[1].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[1].StreamURL != newstation2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[1].Name != newstation2Name) ||
|
|
(response.Response.InternetRadioStations.List[1].HomepageURL != "") {
|
|
t.Fatal("bad data")
|
|
}
|
|
|
|
// delete station 1 and recheck
|
|
response = runTestCase(t, contr, contr.ServeDeleteInternetRadioStation,
|
|
url.Values{"id": {station1ID}}, true)
|
|
checkSuccess(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if response.Response.InternetRadioStations == nil {
|
|
t.Fatal("didn't return stations")
|
|
}
|
|
if len(response.Response.InternetRadioStations.List) != 1 {
|
|
t.Fatal("wrong number of stations")
|
|
}
|
|
if (*response.Response.InternetRadioStations.List[0].ID != station2IDT) ||
|
|
(response.Response.InternetRadioStations.List[0].StreamURL != newstation2StreamURL) ||
|
|
(response.Response.InternetRadioStations.List[0].Name != newstation2Name) ||
|
|
(response.Response.InternetRadioStations.List[0].HomepageURL != "") {
|
|
t.Fatal("bad data")
|
|
}
|
|
|
|
// delete station 2 and check that they're all gone
|
|
response = runTestCase(t, contr, contr.ServeDeleteInternetRadioStation,
|
|
url.Values{"id": {station2ID}}, true)
|
|
checkSuccess(t, response)
|
|
|
|
response = runTestCase(t, contr, contr.ServeGetInternetRadioStations, url.Values{}, false) // no need to be admin
|
|
checkSuccess(t, response)
|
|
|
|
if (response.Response.InternetRadioStations == nil) || (len(response.Response.InternetRadioStations.List) != 0) {
|
|
t.Fatal("didn't return empty stations")
|
|
}
|
|
}
|