goupnp/v2alpha/soap/client/client_test.go
John Beisley 59a727e571 Rename v2 to v2alpha.
Any incremental development on this should be clearly labeled as
unstable.
2022-03-24 19:46:39 +00:00

116 lines
2.8 KiB
Go

package client
import (
"context"
"encoding/xml"
"fmt"
"net/http"
"net/http/httptest"
"testing"
"time"
"github.com/huin/goupnp/v2alpha/soap/envelope"
)
type ActionArgs struct {
Name string
}
type ActionReply struct {
Greeting string
}
type actionKey struct {
endpointURL string
action string
}
var _ http.Handler = &fakeSoapServer{}
type fakeSoapServer struct {
responses map[actionKey]*envelope.Action
errors []error
}
func (fss *fakeSoapServer) badRequest(w http.ResponseWriter, err error) {
fss.errors = append(fss.errors, err)
w.WriteHeader(http.StatusBadRequest)
_, _ = w.Write([]byte(err.Error()))
}
func (fss *fakeSoapServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
fss.badRequest(w, fmt.Errorf("want POST, got %q", r.Method))
return
}
actions := r.Header.Values("SOAPACTION")
if len(actions) != 1 {
fss.badRequest(w, fmt.Errorf("want exactly 1 SOAPACTION, got %d: %q", len(actions), actions))
return
}
headerAction := actions[0]
key := actionKey{
endpointURL: r.URL.Path,
action: headerAction,
}
response, ok := fss.responses[key]
if !ok {
fss.badRequest(w, fmt.Errorf("no response known for %#v", key))
return
}
reqArgs := &ActionArgs{}
reqAction := envelope.Action{Args: reqArgs}
if err := envelope.Read(r.Body, &reqAction); err != nil {
fss.badRequest(w, fmt.Errorf("reading envelope from request: %w", err))
return
}
envelopeAction := fmt.Sprintf("\"%s#%s\"", reqAction.XMLName.Space, reqAction.XMLName.Local)
if envelopeAction != headerAction {
fss.badRequest(w, fmt.Errorf("mismatch in header/envelope action: %q/%q", headerAction, envelopeAction))
return
}
w.Header().Add("CONTENT-TYPE", `text/xml; charset="utf-8"`)
if err := envelope.Write(w, response); err != nil {
fss.errors = append(fss.errors, fmt.Errorf("writing envelope: %w", err))
}
}
func TestPerformAction(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
t.Cleanup(cancel)
service := &fakeSoapServer{
responses: map[actionKey]*envelope.Action{
{"/endpointpath", "\"http://example.com/endpointns#Foo\""}: {
Args: &ActionReply{Greeting: "Hello, World!"},
},
},
}
ts := httptest.NewServer(service)
t.Cleanup(ts.Close)
c := New(ts.URL + "/endpointpath")
reqAction := &envelope.Action{
XMLName: xml.Name{Space: "http://example.com/endpointns", Local: "Foo"},
Args: &ActionArgs{
Name: "World",
},
}
reply := &ActionReply{}
replyAction := &envelope.Action{Args: reply}
if err := c.PerformAction(ctx, reqAction, replyAction); err != nil {
t.Errorf("got error: %v, want success", err)
} else {
if got, want := reply.Greeting, "Hello, World!"; got != want {
t.Errorf("got %q, want %q", got, want)
}
}
for _, err := range service.errors {
t.Errorf("Service error: %v", err)
}
}