2019-11-19 15:15:26 +00:00
|
|
|
package reqdata
|
|
|
|
|
|
|
|
import (
|
2020-03-20 21:36:15 +00:00
|
|
|
"errors"
|
2019-11-19 15:15:26 +00:00
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2019-11-21 22:21:59 +00:00
|
|
|
"net/url"
|
2019-11-19 15:57:23 +00:00
|
|
|
"reflect"
|
2019-11-21 22:00:42 +00:00
|
|
|
"strings"
|
2019-11-19 15:15:26 +00:00
|
|
|
"testing"
|
2020-03-20 21:36:15 +00:00
|
|
|
|
|
|
|
"git.xdrm.io/go/aicra/internal/config"
|
2019-11-19 15:15:26 +00:00
|
|
|
)
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
func getEmptyService() *config.Service {
|
|
|
|
return &config.Service{}
|
|
|
|
}
|
2019-11-19 15:15:26 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
func getServiceWithURI(capturingBraces ...string) *config.Service {
|
|
|
|
service := &config.Service{
|
|
|
|
Input: make(map[string]*config.Parameter),
|
2019-11-19 15:15:26 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
index := 0
|
|
|
|
|
|
|
|
for _, capture := range capturingBraces {
|
|
|
|
if len(capture) == 0 {
|
|
|
|
index++
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
id := fmt.Sprintf("{%s}", capture)
|
|
|
|
service.Input[id] = &config.Parameter{
|
|
|
|
Rename: capture,
|
|
|
|
Validator: func(value interface{}) (interface{}, bool) { return value, true },
|
|
|
|
}
|
|
|
|
|
|
|
|
service.Captures = append(service.Captures, &config.BraceCapture{
|
|
|
|
Name: capture,
|
|
|
|
Index: index,
|
|
|
|
Ref: service.Input[id],
|
|
|
|
})
|
|
|
|
index++
|
2019-11-19 15:15:26 +00:00
|
|
|
}
|
2020-03-20 21:36:15 +00:00
|
|
|
|
|
|
|
return service
|
|
|
|
}
|
|
|
|
func getServiceWithQuery(params ...string) *config.Service {
|
|
|
|
service := &config.Service{
|
|
|
|
Input: make(map[string]*config.Parameter),
|
|
|
|
Query: make(map[string]*config.Parameter),
|
2019-11-19 15:15:26 +00:00
|
|
|
}
|
2020-03-20 21:36:15 +00:00
|
|
|
|
|
|
|
for _, name := range params {
|
|
|
|
id := fmt.Sprintf("GET@%s", name)
|
|
|
|
service.Input[id] = &config.Parameter{
|
|
|
|
Rename: name,
|
|
|
|
Validator: func(value interface{}) (interface{}, bool) { return value, true },
|
|
|
|
}
|
|
|
|
|
|
|
|
service.Query[name] = service.Input[id]
|
2019-11-19 15:15:26 +00:00
|
|
|
}
|
2020-03-20 21:36:15 +00:00
|
|
|
|
|
|
|
return service
|
2019-11-19 15:15:26 +00:00
|
|
|
}
|
2020-03-20 21:36:15 +00:00
|
|
|
func getServiceWithForm(params ...string) *config.Service {
|
|
|
|
service := &config.Service{
|
|
|
|
Input: make(map[string]*config.Parameter),
|
|
|
|
Form: make(map[string]*config.Parameter),
|
|
|
|
}
|
2019-11-19 15:15:44 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
for _, name := range params {
|
|
|
|
service.Input[name] = &config.Parameter{
|
|
|
|
Rename: name,
|
|
|
|
Validator: func(value interface{}) (interface{}, bool) { return value, true },
|
|
|
|
}
|
2019-11-19 15:15:44 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
service.Form[name] = service.Input[name]
|
2019-11-19 15:15:44 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
return service
|
|
|
|
}
|
2019-11-19 15:15:44 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
func TestStoreWithUri(t *testing.T) {
|
|
|
|
tests := []struct {
|
|
|
|
ServiceParams []string
|
|
|
|
URI string
|
|
|
|
Err error
|
|
|
|
}{
|
|
|
|
{
|
|
|
|
[]string{},
|
|
|
|
"/non-captured/uri",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"missing"},
|
|
|
|
"/",
|
|
|
|
ErrMissingURIParameter,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"gotit", "missing"},
|
|
|
|
"/gotme",
|
|
|
|
ErrMissingURIParameter,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"gotit", "gotittoo"},
|
|
|
|
"/gotme/andme",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"gotit", "gotittoo"},
|
|
|
|
"/gotme/andme/ignored",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"first", "", "second"},
|
|
|
|
"/gotme/ignored/gotmetoo",
|
|
|
|
nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
[]string{"first", "", "second"},
|
|
|
|
"/gotme/ignored",
|
|
|
|
ErrMissingURIParameter,
|
|
|
|
},
|
|
|
|
}
|
2019-11-19 15:15:44 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
for i, test := range tests {
|
|
|
|
t.Run(fmt.Sprintf("test.%d", i), func(t *testing.T) {
|
|
|
|
service := getServiceWithURI(test.ServiceParams...)
|
|
|
|
store := New(service)
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "http://host.com"+test.URI, nil)
|
2020-04-04 12:56:15 +00:00
|
|
|
err := store.GetURI(*req)
|
2020-03-20 21:36:15 +00:00
|
|
|
if err != nil {
|
|
|
|
if test.Err != nil {
|
|
|
|
if !errors.Is(err, test.Err) {
|
|
|
|
t.Errorf("expected error <%s>, got <%s>", test.Err, err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.Errorf("unexpected error <%s>", err)
|
|
|
|
t.FailNow()
|
2019-11-19 15:15:44 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
if len(store.Data) != len(service.Input) {
|
|
|
|
t.Errorf("store should contain %d elements, got %d", len(service.Input), len(store.Data))
|
|
|
|
t.Fail()
|
2019-11-19 15:15:44 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
})
|
2019-11-19 15:15:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-11-19 15:15:53 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
func TestExtractQuery(t *testing.T) {
|
|
|
|
|
2019-11-19 15:15:53 +00:00
|
|
|
tests := []struct {
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam []string
|
|
|
|
Query string
|
|
|
|
Err error
|
2019-11-19 15:15:53 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
ParamNames []string
|
|
|
|
ParamValues [][]string
|
2019-11-19 15:15:53 +00:00
|
|
|
}{
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{},
|
|
|
|
Query: "",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: nil,
|
|
|
|
ParamValues: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ServiceParam: []string{"missing"},
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "",
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: ErrMissingRequiredParam,
|
|
|
|
ParamNames: nil,
|
|
|
|
ParamValues: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{"a"},
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "a",
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: [][]string{[]string{""}},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ServiceParam: []string{"a"},
|
|
|
|
Query: "a&b",
|
|
|
|
Err: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: [][]string{[]string{""}},
|
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{"a", "missing"},
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "a&b",
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: ErrMissingRequiredParam,
|
|
|
|
ParamNames: nil,
|
|
|
|
ParamValues: nil,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
ServiceParam: []string{"a", "b"},
|
|
|
|
Query: "a&b",
|
|
|
|
Err: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
ParamNames: []string{"a", "b"},
|
|
|
|
ParamValues: [][]string{[]string{""}, []string{""}},
|
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{"a"},
|
|
|
|
Err: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "a=",
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: [][]string{[]string{""}},
|
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{"a", "b"},
|
|
|
|
Err: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "a=&b=x",
|
|
|
|
ParamNames: []string{"a", "b"},
|
|
|
|
ParamValues: [][]string{[]string{""}, []string{"x"}},
|
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{"a", "c"},
|
|
|
|
Err: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "a=b&c=d",
|
|
|
|
ParamNames: []string{"a", "c"},
|
|
|
|
ParamValues: [][]string{[]string{"b"}, []string{"d"}},
|
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParam: []string{"a", "c"},
|
|
|
|
Err: nil,
|
2019-11-19 15:15:53 +00:00
|
|
|
Query: "a=b&c=d&a=x",
|
|
|
|
ParamNames: []string{"a", "c"},
|
|
|
|
ParamValues: [][]string{[]string{"b", "x"}, []string{"d"}},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
store := New(getServiceWithQuery(test.ServiceParam...))
|
|
|
|
|
2019-11-19 15:15:53 +00:00
|
|
|
req := httptest.NewRequest(http.MethodGet, fmt.Sprintf("http://host.com?%s", test.Query), nil)
|
2020-04-04 12:56:15 +00:00
|
|
|
err := store.GetQuery(*req)
|
2020-03-20 21:36:15 +00:00
|
|
|
if err != nil {
|
|
|
|
if test.Err != nil {
|
|
|
|
if !errors.Is(err, test.Err) {
|
|
|
|
t.Errorf("expected error <%s>, got <%s>", test.Err, err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.Errorf("unexpected error <%s>", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2019-11-19 15:15:53 +00:00
|
|
|
|
|
|
|
if test.ParamNames == nil || test.ParamValues == nil {
|
2020-03-20 21:36:15 +00:00
|
|
|
if len(store.Data) != 0 {
|
|
|
|
t.Errorf("expected no GET parameters and got %d", len(store.Data))
|
|
|
|
t.FailNow()
|
2019-11-19 15:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// no param to check
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(test.ParamNames) != len(test.ParamValues) {
|
|
|
|
t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues))
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for pi, pName := range test.ParamNames {
|
|
|
|
values := test.ParamValues[pi]
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
t.Run(pName, func(t *testing.T) {
|
|
|
|
param, isset := store.Data[pName]
|
2019-11-19 15:53:02 +00:00
|
|
|
if !isset {
|
2020-03-20 21:36:15 +00:00
|
|
|
t.Errorf("param does not exist")
|
|
|
|
t.FailNow()
|
2019-11-19 15:53:02 +00:00
|
|
|
}
|
|
|
|
|
2020-03-21 13:45:39 +00:00
|
|
|
cast, canCast := param.([]interface{})
|
2019-11-19 15:53:02 +00:00
|
|
|
if !canCast {
|
|
|
|
t.Errorf("should return a []string (got '%v')", cast)
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:53:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(cast) != len(values) {
|
|
|
|
t.Errorf("should return %d string(s) (got '%d')", len(values), len(cast))
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:53:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for vi, value := range values {
|
|
|
|
|
|
|
|
t.Run(fmt.Sprintf("value.%d", vi), func(t *testing.T) {
|
|
|
|
if value != cast[vi] {
|
|
|
|
t.Errorf("should return '%s' (got '%s')", value, cast[vi])
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:53:02 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-11-21 22:21:59 +00:00
|
|
|
func TestStoreWithUrlEncodedFormParseError(t *testing.T) {
|
|
|
|
// http.Request.ParseForm() fails when:
|
|
|
|
// - http.Request.Method is one of [POST,PUT,PATCH]
|
|
|
|
// - http.Request.Form is not nil (created manually)
|
|
|
|
// - http.Request.PostForm is nil (deleted manually)
|
|
|
|
// - http.Request.Body is nil (deleted manually)
|
|
|
|
|
|
|
|
req := httptest.NewRequest(http.MethodPost, "http://host.com/", nil)
|
|
|
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
|
|
|
// break everything
|
|
|
|
req.Body = nil
|
|
|
|
req.Form = make(url.Values)
|
|
|
|
req.PostForm = nil
|
|
|
|
|
|
|
|
// defer req.Body.Close()
|
2020-03-20 21:36:15 +00:00
|
|
|
store := New(nil)
|
2020-04-04 12:56:15 +00:00
|
|
|
err := store.GetForm(*req)
|
2020-03-20 21:36:15 +00:00
|
|
|
if err == nil {
|
|
|
|
t.Errorf("expected malformed urlencoded to have FailNow being parsed (got %d elements)", len(store.Data))
|
2019-11-21 22:21:59 +00:00
|
|
|
t.FailNow()
|
2020-03-20 21:36:15 +00:00
|
|
|
|
2019-11-21 22:21:59 +00:00
|
|
|
}
|
|
|
|
}
|
2020-03-20 21:36:15 +00:00
|
|
|
func TestExtractFormUrlEncoded(t *testing.T) {
|
2019-11-19 15:53:02 +00:00
|
|
|
tests := []struct {
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams []string
|
|
|
|
URLEncoded string
|
|
|
|
Err error
|
2019-11-19 15:53:02 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
ParamNames []string
|
|
|
|
ParamValues [][]string
|
2019-11-19 15:53:02 +00:00
|
|
|
}{
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
|
|
|
URLEncoded: "",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: nil,
|
|
|
|
ParamValues: nil,
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"missing"},
|
|
|
|
URLEncoded: "",
|
|
|
|
Err: ErrMissingRequiredParam,
|
|
|
|
ParamNames: nil,
|
|
|
|
ParamValues: nil,
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
|
|
|
URLEncoded: "a",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: [][]string{[]string{""}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
|
|
|
URLEncoded: "a&b",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: [][]string{[]string{""}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "missing"},
|
|
|
|
URLEncoded: "a&b",
|
|
|
|
Err: ErrMissingRequiredParam,
|
|
|
|
ParamNames: nil,
|
|
|
|
ParamValues: nil,
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "b"},
|
|
|
|
URLEncoded: "a&b",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a", "b"},
|
|
|
|
ParamValues: [][]string{[]string{""}, []string{""}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
|
|
|
Err: nil,
|
|
|
|
URLEncoded: "a=",
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: [][]string{[]string{""}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "b"},
|
|
|
|
Err: nil,
|
|
|
|
URLEncoded: "a=&b=x",
|
|
|
|
ParamNames: []string{"a", "b"},
|
|
|
|
ParamValues: [][]string{[]string{""}, []string{"x"}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "c"},
|
|
|
|
Err: nil,
|
|
|
|
URLEncoded: "a=b&c=d",
|
|
|
|
ParamNames: []string{"a", "c"},
|
|
|
|
ParamValues: [][]string{[]string{"b"}, []string{"d"}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "c"},
|
|
|
|
Err: nil,
|
|
|
|
URLEncoded: "a=b&c=d&a=x",
|
|
|
|
ParamNames: []string{"a", "c"},
|
|
|
|
ParamValues: [][]string{[]string{"b", "x"}, []string{"d"}},
|
2019-11-19 15:53:02 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
2019-11-21 22:00:42 +00:00
|
|
|
body := strings.NewReader(test.URLEncoded)
|
2019-11-19 15:53:02 +00:00
|
|
|
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
|
|
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
defer req.Body.Close()
|
2020-03-20 21:36:15 +00:00
|
|
|
|
|
|
|
store := New(getServiceWithForm(test.ServiceParams...))
|
2020-04-04 12:56:15 +00:00
|
|
|
err := store.GetForm(*req)
|
2020-03-20 21:36:15 +00:00
|
|
|
if err != nil {
|
|
|
|
if test.Err != nil {
|
|
|
|
if !errors.Is(err, test.Err) {
|
|
|
|
t.Errorf("expected error <%s>, got <%s>", test.Err, err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.Errorf("unexpected error <%s>", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2019-11-19 15:53:02 +00:00
|
|
|
|
|
|
|
if test.ParamNames == nil || test.ParamValues == nil {
|
2020-03-20 21:36:15 +00:00
|
|
|
if len(store.Data) != 0 {
|
|
|
|
t.Errorf("expected no GET parameters and got %d", len(store.Data))
|
|
|
|
t.FailNow()
|
2019-11-19 15:53:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// no param to check
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(test.ParamNames) != len(test.ParamValues) {
|
|
|
|
t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues))
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:53:02 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
for pi, key := range test.ParamNames {
|
2019-11-19 15:53:02 +00:00
|
|
|
values := test.ParamValues[pi]
|
|
|
|
|
2019-11-19 15:15:53 +00:00
|
|
|
t.Run(key, func(t *testing.T) {
|
2020-03-20 21:36:15 +00:00
|
|
|
param, isset := store.Data[key]
|
2019-11-19 15:15:53 +00:00
|
|
|
if !isset {
|
2020-03-20 21:36:15 +00:00
|
|
|
t.Errorf("param does not exist")
|
|
|
|
t.FailNow()
|
2019-11-19 15:15:53 +00:00
|
|
|
}
|
|
|
|
|
2020-03-21 13:45:39 +00:00
|
|
|
cast, canCast := param.([]interface{})
|
2019-11-19 15:15:53 +00:00
|
|
|
if !canCast {
|
2020-03-21 13:45:39 +00:00
|
|
|
t.Errorf("should return a []interface{} (got '%v')", cast)
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if len(cast) != len(values) {
|
|
|
|
t.Errorf("should return %d string(s) (got '%d')", len(values), len(cast))
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:15:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for vi, value := range values {
|
|
|
|
|
|
|
|
t.Run(fmt.Sprintf("value.%d", vi), func(t *testing.T) {
|
|
|
|
if value != cast[vi] {
|
|
|
|
t.Errorf("should return '%s' (got '%s')", value, cast[vi])
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:15:53 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2019-11-19 15:57:23 +00:00
|
|
|
|
|
|
|
func TestJsonParameters(t *testing.T) {
|
|
|
|
tests := []struct {
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams []string
|
|
|
|
Raw string
|
|
|
|
Err error
|
2019-11-19 15:57:23 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
ParamNames []string
|
|
|
|
ParamValues []interface{}
|
2019-11-19 15:57:23 +00:00
|
|
|
}{
|
|
|
|
// no need to fully check json because it is parsed with the standard library
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
|
|
|
Raw: "",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
|
|
|
Raw: "{}",
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
|
|
|
Raw: `{ "a": "b" }`,
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
|
|
|
Raw: `{ "a": "b" }`,
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: []interface{}{"b"},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
|
|
|
Raw: `{ "a": "b", "ignored": "d" }`,
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: []interface{}{"b"},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "c"},
|
|
|
|
Raw: `{ "a": "b", "c": "d" }`,
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a", "c"},
|
|
|
|
ParamValues: []interface{}{"b", "d"},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
|
|
|
Raw: `{ "a": null }`,
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: []interface{}{nil},
|
2019-11-19 15:57:23 +00:00
|
|
|
},
|
2019-11-19 16:00:11 +00:00
|
|
|
// json parse error
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
|
|
|
Raw: `{ "a": "b", }`,
|
|
|
|
Err: ErrInvalidJSON,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-19 16:00:11 +00:00
|
|
|
},
|
2019-11-19 15:57:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
2020-03-20 21:36:15 +00:00
|
|
|
body := strings.NewReader(test.Raw)
|
2019-11-19 15:57:23 +00:00
|
|
|
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
|
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
defer req.Body.Close()
|
2020-03-20 21:36:15 +00:00
|
|
|
store := New(getServiceWithForm(test.ServiceParams...))
|
|
|
|
|
2020-04-04 12:56:15 +00:00
|
|
|
err := store.GetForm(*req)
|
2020-03-20 21:36:15 +00:00
|
|
|
if err != nil {
|
|
|
|
if test.Err != nil {
|
|
|
|
if !errors.Is(err, test.Err) {
|
|
|
|
t.Errorf("expected error <%s>, got <%s>", test.Err, err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.Errorf("unexpected error <%s>", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2019-11-19 15:57:23 +00:00
|
|
|
|
|
|
|
if test.ParamNames == nil || test.ParamValues == nil {
|
2020-03-20 21:36:15 +00:00
|
|
|
if len(store.Data) != 0 {
|
|
|
|
t.Errorf("expected no JSON parameters and got %d", len(store.Data))
|
|
|
|
t.FailNow()
|
2019-11-19 15:57:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// no param to check
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(test.ParamNames) != len(test.ParamValues) {
|
|
|
|
t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues))
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:57:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for pi, pName := range test.ParamNames {
|
2019-11-21 21:58:03 +00:00
|
|
|
key := pName
|
|
|
|
value := test.ParamValues[pi]
|
|
|
|
|
|
|
|
t.Run(key, func(t *testing.T) {
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
param, isset := store.Data[key]
|
2019-11-21 21:58:03 +00:00
|
|
|
if !isset {
|
2020-03-20 21:36:15 +00:00
|
|
|
t.Errorf("store should contain element with key '%s'", key)
|
|
|
|
t.FailNow()
|
2019-11-21 21:58:03 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
valueType := reflect.TypeOf(value)
|
|
|
|
|
2020-03-21 13:45:39 +00:00
|
|
|
paramValue := param
|
|
|
|
paramValueType := reflect.TypeOf(param)
|
2019-11-21 21:58:03 +00:00
|
|
|
|
|
|
|
if valueType != paramValueType {
|
|
|
|
t.Errorf("should be of type %v (got '%v')", valueType, paramValueType)
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-21 21:58:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if paramValue != value {
|
|
|
|
t.Errorf("should return %v (got '%v')", value, paramValue)
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-21 21:58:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestMultipartParameters(t *testing.T) {
|
|
|
|
tests := []struct {
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams []string
|
|
|
|
RawMultipart string
|
|
|
|
Err error
|
2019-11-21 21:58:03 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
ParamNames []string
|
|
|
|
ParamValues []interface{}
|
2019-11-21 21:58:03 +00:00
|
|
|
}{
|
|
|
|
// no need to fully check json because it is parsed with the standard library
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
|
|
|
RawMultipart: ``,
|
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
2019-11-21 21:58:03 +00:00
|
|
|
RawMultipart: `--xxx
|
|
|
|
`,
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: ErrInvalidMultipart,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
2019-11-21 21:58:03 +00:00
|
|
|
RawMultipart: `--xxx
|
|
|
|
--xxx--`,
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: ErrInvalidMultipart,
|
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{},
|
2019-11-21 21:58:03 +00:00
|
|
|
RawMultipart: `--xxx
|
|
|
|
Content-Disposition: form-data; name="a"
|
|
|
|
|
|
|
|
b
|
|
|
|
--xxx--`,
|
2020-03-20 21:36:15 +00:00
|
|
|
ParamNames: []string{},
|
|
|
|
ParamValues: []interface{}{},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
2019-11-21 21:58:03 +00:00
|
|
|
RawMultipart: `--xxx
|
|
|
|
Content-Disposition: form-data; name="a"
|
|
|
|
|
|
|
|
b
|
|
|
|
--xxx--`,
|
2020-03-20 21:36:15 +00:00
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: []interface{}{"b"},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a", "c"},
|
2019-11-21 21:58:03 +00:00
|
|
|
RawMultipart: `--xxx
|
|
|
|
Content-Disposition: form-data; name="a"
|
|
|
|
|
|
|
|
b
|
|
|
|
--xxx
|
2020-03-20 21:36:15 +00:00
|
|
|
Content-Disposition: form-data; name="c"
|
2019-11-21 21:58:03 +00:00
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
d
|
2019-11-21 21:58:03 +00:00
|
|
|
--xxx--`,
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a", "c"},
|
|
|
|
ParamValues: []interface{}{"b", "d"},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
{
|
2020-03-20 21:36:15 +00:00
|
|
|
ServiceParams: []string{"a"},
|
2019-11-21 21:58:03 +00:00
|
|
|
RawMultipart: `--xxx
|
|
|
|
Content-Disposition: form-data; name="a"
|
|
|
|
|
|
|
|
b
|
|
|
|
--xxx
|
2020-03-20 21:36:15 +00:00
|
|
|
Content-Disposition: form-data; name="ignored"
|
2019-11-21 21:58:03 +00:00
|
|
|
|
|
|
|
x
|
|
|
|
--xxx--`,
|
2020-03-20 21:36:15 +00:00
|
|
|
Err: nil,
|
|
|
|
ParamNames: []string{"a"},
|
|
|
|
ParamValues: []interface{}{"b"},
|
2019-11-21 21:58:03 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
for i, test := range tests {
|
|
|
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
2019-11-21 22:00:42 +00:00
|
|
|
body := strings.NewReader(test.RawMultipart)
|
2019-11-21 21:58:03 +00:00
|
|
|
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
|
|
|
req.Header.Add("Content-Type", "multipart/form-data; boundary=xxx")
|
|
|
|
defer req.Body.Close()
|
2020-03-20 21:36:15 +00:00
|
|
|
store := New(getServiceWithForm(test.ServiceParams...))
|
|
|
|
|
2020-04-04 12:56:15 +00:00
|
|
|
err := store.GetForm(*req)
|
2020-03-20 21:36:15 +00:00
|
|
|
if err != nil {
|
|
|
|
if test.Err != nil {
|
|
|
|
if !errors.Is(err, test.Err) {
|
|
|
|
t.Errorf("expected error <%s>, got <%s>", test.Err, err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
t.Errorf("unexpected error <%s>", err)
|
|
|
|
t.FailNow()
|
|
|
|
}
|
2019-11-21 21:58:03 +00:00
|
|
|
|
|
|
|
if test.ParamNames == nil || test.ParamValues == nil {
|
2020-03-20 21:36:15 +00:00
|
|
|
if len(store.Data) != 0 {
|
|
|
|
t.Errorf("expected no JSON parameters and got %d", len(store.Data))
|
|
|
|
t.FailNow()
|
2019-11-21 21:58:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// no param to check
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(test.ParamNames) != len(test.ParamValues) {
|
|
|
|
t.Errorf("invalid test: names and values differ in size (%d vs %d)", len(test.ParamNames), len(test.ParamValues))
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-21 21:58:03 +00:00
|
|
|
}
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
for pi, key := range test.ParamNames {
|
2019-11-19 15:57:23 +00:00
|
|
|
value := test.ParamValues[pi]
|
|
|
|
|
|
|
|
t.Run(key, func(t *testing.T) {
|
|
|
|
|
2020-03-20 21:36:15 +00:00
|
|
|
param, isset := store.Data[key]
|
2019-11-19 15:57:23 +00:00
|
|
|
if !isset {
|
2020-03-20 21:36:15 +00:00
|
|
|
t.Errorf("store should contain element with key '%s'", key)
|
|
|
|
t.FailNow()
|
2019-11-19 15:57:23 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
valueType := reflect.TypeOf(value)
|
|
|
|
|
2020-03-21 13:45:39 +00:00
|
|
|
paramValue := param
|
|
|
|
paramValueType := reflect.TypeOf(param)
|
2019-11-19 15:57:23 +00:00
|
|
|
|
|
|
|
if valueType != paramValueType {
|
|
|
|
t.Errorf("should be of type %v (got '%v')", valueType, paramValueType)
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:57:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if paramValue != value {
|
|
|
|
t.Errorf("should return %v (got '%v')", value, paramValue)
|
2020-03-20 21:36:15 +00:00
|
|
|
t.FailNow()
|
2019-11-19 15:57:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|