test and fix internal/reqdata #8
|
@ -4,8 +4,19 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"git.xdrm.io/go/aicra/internal/cerr"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// ErrUnknownType is returned when encountering an unknown type
|
||||||
|
const ErrUnknownType = cerr.Error("unknown type")
|
||||||
|
|
||||||
|
// ErrInvalidJSON is returned when json parse failed
|
||||||
|
const ErrInvalidJSON = cerr.Error("invalid json")
|
||||||
|
|
||||||
|
// ErrInvalidRootType is returned when json is a map
|
||||||
|
const ErrInvalidRootType = cerr.Error("invalid json root type")
|
||||||
|
|
||||||
// Parameter represents an http request parameter
|
// Parameter represents an http request parameter
|
||||||
// that can be of type URL, GET, or FORM (multipart, json, urlencoded)
|
// that can be of type URL, GET, or FORM (multipart, json, urlencoded)
|
||||||
type Parameter struct {
|
type Parameter struct {
|
||||||
|
@ -22,16 +33,23 @@ type Parameter struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse parameter (json-like) if not already done
|
// Parse parameter (json-like) if not already done
|
||||||
func (i *Parameter) Parse() {
|
func (i *Parameter) Parse() error {
|
||||||
|
|
||||||
/* (1) Stop if already parsed or nil*/
|
/* (1) Stop if already parsed or nil*/
|
||||||
if i.Parsed || i.Value == nil {
|
if i.Parsed || i.Value == nil {
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (2) Try to parse value */
|
/* (2) Try to parse value */
|
||||||
i.Value = parseParameter(i.Value)
|
parsed, err := parseParameter(i.Value)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
i.Parsed = true
|
||||||
|
i.Value = parsed
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseParameter parses http GET/POST data
|
// parseParameter parses http GET/POST data
|
||||||
|
@ -39,7 +57,7 @@ func (i *Parameter) Parse() {
|
||||||
// - size = 1 : return json of first element
|
// - size = 1 : return json of first element
|
||||||
// - size > 1 : return array of json elements
|
// - size > 1 : return array of json elements
|
||||||
// - string : return json if valid, else return raw string
|
// - string : return json if valid, else return raw string
|
||||||
func parseParameter(data interface{}) interface{} {
|
func parseParameter(data interface{}) (interface{}, error) {
|
||||||
dtype := reflect.TypeOf(data)
|
dtype := reflect.TypeOf(data)
|
||||||
dvalue := reflect.ValueOf(data)
|
dvalue := reflect.ValueOf(data)
|
||||||
|
|
||||||
|
@ -48,23 +66,12 @@ func parseParameter(data interface{}) interface{} {
|
||||||
/* (1) []string -> recursive */
|
/* (1) []string -> recursive */
|
||||||
case reflect.Slice:
|
case reflect.Slice:
|
||||||
|
|
||||||
// 1. Return nothing if empty
|
// 1. ignore empty
|
||||||
if dvalue.Len() == 0 {
|
if dvalue.Len() == 0 {
|
||||||
return nil
|
return data, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. only return first element if alone
|
// 2. parse each element recursively
|
||||||
if dvalue.Len() == 1 {
|
|
||||||
|
|
||||||
element := dvalue.Index(0)
|
|
||||||
if element.Kind() != reflect.String {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return parseParameter(element.String())
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Return all elements if more than 1
|
|
||||||
result := make([]interface{}, dvalue.Len())
|
result := make([]interface{}, dvalue.Len())
|
||||||
|
|
||||||
for i, l := 0, dvalue.Len(); i < l; i++ {
|
for i, l := 0, dvalue.Len(); i < l; i++ {
|
||||||
|
@ -72,12 +79,17 @@ func parseParameter(data interface{}) interface{} {
|
||||||
|
|
||||||
// ignore non-string
|
// ignore non-string
|
||||||
if element.Kind() != reflect.String {
|
if element.Kind() != reflect.String {
|
||||||
|
result[i] = element.Interface()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
result[i] = parseParameter(element.String())
|
parsed, err := parseParameter(element.String())
|
||||||
|
if err != nil {
|
||||||
|
return data, err
|
||||||
}
|
}
|
||||||
return result
|
result[i] = parsed
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
|
||||||
/* (2) string -> parse */
|
/* (2) string -> parse */
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
@ -94,23 +106,23 @@ func parseParameter(data interface{}) interface{} {
|
||||||
|
|
||||||
mapval, ok := result.(map[string]interface{})
|
mapval, ok := result.(map[string]interface{})
|
||||||
if !ok {
|
if !ok {
|
||||||
return dvalue.String()
|
return dvalue.String(), ErrInvalidRootType
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapped, ok := mapval["wrapped"]
|
wrapped, ok := mapval["wrapped"]
|
||||||
if !ok {
|
if !ok {
|
||||||
return dvalue.String()
|
return dvalue.String(), ErrInvalidJSON
|
||||||
}
|
}
|
||||||
|
|
||||||
return wrapped
|
return wrapped, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// else return as string
|
// else return as string
|
||||||
return dvalue.String()
|
return dvalue.String(), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* (3) NIL if unknown type */
|
/* (3) NIL if unknown type */
|
||||||
return dvalue
|
return dvalue.Interface(), nil
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,358 @@
|
||||||
|
package reqdata
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSimpleString(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: "some-string"}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
cast, canCast := p.Value.(string)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a string")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if cast != "some-string" {
|
||||||
|
t.Errorf("expected parameter to equal 'some-string', got '%s'", cast)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimpleFloat(t *testing.T) {
|
||||||
|
tcases := []float64{12.3456789, -12.3456789, 0.0000001, -0.0000001}
|
||||||
|
|
||||||
|
for i, tcase := range tcases {
|
||||||
|
t.Run("case "+string(i), func(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: tcase}
|
||||||
|
|
||||||
|
if err := p.Parse(); err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
cast, canCast := p.Value.(float64)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a float64")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.Abs(cast-tcase) > 0.00000001 {
|
||||||
|
t.Errorf("expected parameter to equal '%f', got '%f'", tcase, cast)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSimpleBool(t *testing.T) {
|
||||||
|
tcases := []bool{true, false}
|
||||||
|
|
||||||
|
for i, tcase := range tcases {
|
||||||
|
t.Run("case "+string(i), func(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: tcase}
|
||||||
|
|
||||||
|
if err := p.Parse(); err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
cast, canCast := p.Value.(bool)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a bool")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if cast != tcase {
|
||||||
|
t.Errorf("expected parameter to equal '%t', got '%t'", tcase, cast)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonStringSlice(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: `["str1", "str2"]`}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
slice, canCast := p.Value.([]interface{})
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a []interface{}")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(slice) != 2 {
|
||||||
|
t.Errorf("expected 2 values, got %d", len(slice))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []string{"str1", "str2"}
|
||||||
|
|
||||||
|
for i, res := range results {
|
||||||
|
|
||||||
|
cast, canCast := slice[i].(string)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter %d to be a []string", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cast != res {
|
||||||
|
t.Errorf("expected first value to be '%s', got '%s'", res, cast)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestStringSlice(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: []string{"str1", "str2"}}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
slice, canCast := p.Value.([]interface{})
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a []interface{}")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(slice) != 2 {
|
||||||
|
t.Errorf("expected 2 values, got %d", len(slice))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []string{"str1", "str2"}
|
||||||
|
|
||||||
|
for i, res := range results {
|
||||||
|
|
||||||
|
cast, canCast := slice[i].(string)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter %d to be a []string", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cast != res {
|
||||||
|
t.Errorf("expected first value to be '%s', got '%s'", res, cast)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonPrimitiveBool(t *testing.T) {
|
||||||
|
tcases := []struct {
|
||||||
|
Raw string
|
||||||
|
BoolValue bool
|
||||||
|
}{
|
||||||
|
{"true", true},
|
||||||
|
{"false", false},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tcase := range tcases {
|
||||||
|
t.Run("case "+string(i), func(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: tcase.Raw}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
cast, canCast := p.Value.(bool)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a bool")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if cast != tcase.BoolValue {
|
||||||
|
t.Errorf("expected a value of %t, got %t", tcase.BoolValue, cast)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonPrimitiveFloat(t *testing.T) {
|
||||||
|
tcases := []struct {
|
||||||
|
Raw string
|
||||||
|
FloatValue float64
|
||||||
|
}{
|
||||||
|
{"1", 1},
|
||||||
|
{"-1", -1},
|
||||||
|
|
||||||
|
{"0.001", 0.001},
|
||||||
|
{"-0.001", -0.001},
|
||||||
|
|
||||||
|
{"1.9992", 1.9992},
|
||||||
|
{"-1.9992", -1.9992},
|
||||||
|
|
||||||
|
{"19992", 19992},
|
||||||
|
{"-19992", -19992},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, tcase := range tcases {
|
||||||
|
t.Run("case "+string(i), func(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: tcase.Raw}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
cast, canCast := p.Value.(float64)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a float64")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if math.Abs(cast-tcase.FloatValue) > 0.00001 {
|
||||||
|
t.Errorf("expected a value of %f, got %f", tcase.FloatValue, cast)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJsonBoolSlice(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: []string{"true", "false"}}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
slice, canCast := p.Value.([]interface{})
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a []interface{}")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(slice) != 2 {
|
||||||
|
t.Errorf("expected 2 values, got %d", len(slice))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []bool{true, false}
|
||||||
|
|
||||||
|
for i, res := range results {
|
||||||
|
|
||||||
|
cast, canCast := slice[i].(bool)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter %d to be a []bool", i)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cast != res {
|
||||||
|
t.Errorf("expected first value to be '%t', got '%t'", res, cast)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBoolSlice(t *testing.T) {
|
||||||
|
p := Parameter{Parsed: false, File: false, Value: []bool{true, false}}
|
||||||
|
|
||||||
|
err := p.Parse()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: <%s>", err)
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if !p.Parsed {
|
||||||
|
t.Errorf("expected parameter to be parsed")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
slice, canCast := p.Value.([]interface{})
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter to be a []interface{}")
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(slice) != 2 {
|
||||||
|
t.Errorf("expected 2 values, got %d", len(slice))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
|
||||||
|
results := []bool{true, false}
|
||||||
|
|
||||||
|
for i, res := range results {
|
||||||
|
|
||||||
|
cast, canCast := slice[i].(bool)
|
||||||
|
if !canCast {
|
||||||
|
t.Errorf("expected parameter %d to be a bool, got %v", i, slice[i])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if cast != res {
|
||||||
|
t.Errorf("expected first value to be '%t', got '%t'", res, cast)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,11 +1,12 @@
|
||||||
package reqdata
|
package reqdata
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -222,7 +223,28 @@ func TestStoreWithGet(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
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()
|
||||||
|
store := New(nil, req)
|
||||||
|
if len(store.Form) > 0 {
|
||||||
|
t.Errorf("expected malformed urlencoded to have failed being parsed (got %d elements)", len(store.Form))
|
||||||
|
t.FailNow()
|
||||||
|
}
|
||||||
|
}
|
||||||
func TestStoreWithUrlEncodedForm(t *testing.T) {
|
func TestStoreWithUrlEncodedForm(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
URLEncoded string
|
URLEncoded string
|
||||||
|
@ -301,7 +323,7 @@ func TestStoreWithUrlEncodedForm(t *testing.T) {
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
||||||
body := bytes.NewBufferString(test.URLEncoded)
|
body := strings.NewReader(test.URLEncoded)
|
||||||
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
||||||
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
|
||||||
defer req.Body.Close()
|
defer req.Body.Close()
|
||||||
|
@ -474,7 +496,7 @@ func TestJsonParameters(t *testing.T) {
|
||||||
|
|
||||||
for i, test := range tests {
|
for i, test := range tests {
|
||||||
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
||||||
body := bytes.NewBufferString(test.RawJson)
|
body := strings.NewReader(test.RawJson)
|
||||||
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
||||||
req.Header.Add("Content-Type", "application/json")
|
req.Header.Add("Content-Type", "application/json")
|
||||||
defer req.Body.Close()
|
defer req.Body.Close()
|
||||||
|
@ -545,3 +567,238 @@ func TestJsonParameters(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMultipartParameters(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
RawMultipart string
|
||||||
|
|
||||||
|
InvalidNames []string
|
||||||
|
ParamNames []string
|
||||||
|
ParamValues []interface{}
|
||||||
|
}{
|
||||||
|
// no need to fully check json because it is parsed with the standard library
|
||||||
|
{
|
||||||
|
RawMultipart: ``,
|
||||||
|
InvalidNames: []string{},
|
||||||
|
ParamNames: []string{},
|
||||||
|
ParamValues: []interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
`,
|
||||||
|
InvalidNames: []string{},
|
||||||
|
ParamNames: []string{},
|
||||||
|
ParamValues: []interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{},
|
||||||
|
ParamNames: []string{},
|
||||||
|
ParamValues: []interface{}{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="a"
|
||||||
|
|
||||||
|
b
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{},
|
||||||
|
ParamNames: []string{"a"},
|
||||||
|
ParamValues: []interface{}{"b"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="a"
|
||||||
|
|
||||||
|
b
|
||||||
|
--xxx
|
||||||
|
Content-Disposition: form-data; name="c"
|
||||||
|
|
||||||
|
d
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{},
|
||||||
|
ParamNames: []string{"a", "c"},
|
||||||
|
ParamValues: []interface{}{"b", "d"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="_invalid"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"_invalid"},
|
||||||
|
ParamNames: []string{"_invalid"},
|
||||||
|
ParamValues: []interface{}{nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="a"
|
||||||
|
|
||||||
|
b
|
||||||
|
--xxx
|
||||||
|
Content-Disposition: form-data; name="_invalid"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"_invalid"},
|
||||||
|
ParamNames: []string{"a", "_invalid"},
|
||||||
|
ParamValues: []interface{}{"b", nil},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="invalid_"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"invalid_"},
|
||||||
|
ParamNames: []string{"invalid_"},
|
||||||
|
ParamValues: []interface{}{nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="a"
|
||||||
|
|
||||||
|
b
|
||||||
|
--xxx
|
||||||
|
Content-Disposition: form-data; name="invalid_"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"invalid_"},
|
||||||
|
ParamNames: []string{"a", "invalid_"},
|
||||||
|
ParamValues: []interface{}{"b", nil},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="GET@injection"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"GET@injection"},
|
||||||
|
ParamNames: []string{"GET@injection"},
|
||||||
|
ParamValues: []interface{}{nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="a"
|
||||||
|
|
||||||
|
b
|
||||||
|
--xxx
|
||||||
|
Content-Disposition: form-data; name="GET@injection"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"GET@injection"},
|
||||||
|
ParamNames: []string{"a", "GET@injection"},
|
||||||
|
ParamValues: []interface{}{"b", nil},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="URL#injection"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"URL#injection"},
|
||||||
|
ParamNames: []string{"URL#injection"},
|
||||||
|
ParamValues: []interface{}{nil},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RawMultipart: `--xxx
|
||||||
|
Content-Disposition: form-data; name="a"
|
||||||
|
|
||||||
|
b
|
||||||
|
--xxx
|
||||||
|
Content-Disposition: form-data; name="URL#injection"
|
||||||
|
|
||||||
|
x
|
||||||
|
--xxx--`,
|
||||||
|
InvalidNames: []string{"URL#injection"},
|
||||||
|
ParamNames: []string{"a", "URL#injection"},
|
||||||
|
ParamValues: []interface{}{"b", nil},
|
||||||
|
},
|
||||||
|
// json parse error
|
||||||
|
{
|
||||||
|
RawMultipart: "{ \"a\": \"b\", }",
|
||||||
|
InvalidNames: []string{},
|
||||||
|
ParamNames: []string{},
|
||||||
|
ParamValues: []interface{}{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, test := range tests {
|
||||||
|
t.Run(fmt.Sprintf("request.%d", i), func(t *testing.T) {
|
||||||
|
body := strings.NewReader(test.RawMultipart)
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "http://host.com", body)
|
||||||
|
req.Header.Add("Content-Type", "multipart/form-data; boundary=xxx")
|
||||||
|
defer req.Body.Close()
|
||||||
|
store := New(nil, req)
|
||||||
|
|
||||||
|
if test.ParamNames == nil || test.ParamValues == nil {
|
||||||
|
if len(store.Set) != 0 {
|
||||||
|
t.Errorf("expected no JSON parameters and got %d", len(store.Get))
|
||||||
|
t.Failed()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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))
|
||||||
|
t.Failed()
|
||||||
|
}
|
||||||
|
|
||||||
|
for pi, pName := range test.ParamNames {
|
||||||
|
key := pName
|
||||||
|
value := test.ParamValues[pi]
|
||||||
|
|
||||||
|
isNameValid := true
|
||||||
|
for _, invalid := range test.InvalidNames {
|
||||||
|
if pName == invalid {
|
||||||
|
isNameValid = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run(key, func(t *testing.T) {
|
||||||
|
|
||||||
|
param, isset := store.Set[key]
|
||||||
|
if !isset {
|
||||||
|
if isNameValid {
|
||||||
|
t.Errorf("store should contain element with key '%s'", key)
|
||||||
|
t.Failed()
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// if should be invalid
|
||||||
|
if isset && !isNameValid {
|
||||||
|
t.Errorf("store should NOT contain element with key '%s' (invalid name)", key)
|
||||||
|
t.Failed()
|
||||||
|
}
|
||||||
|
|
||||||
|
valueType := reflect.TypeOf(value)
|
||||||
|
|
||||||
|
paramValue := param.Value
|
||||||
|
paramValueType := reflect.TypeOf(param.Value)
|
||||||
|
|
||||||
|
if valueType != paramValueType {
|
||||||
|
t.Errorf("should be of type %v (got '%v')", valueType, paramValueType)
|
||||||
|
t.Failed()
|
||||||
|
}
|
||||||
|
|
||||||
|
if paramValue != value {
|
||||||
|
t.Errorf("should return %v (got '%v')", value, paramValue)
|
||||||
|
t.Failed()
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -41,6 +41,18 @@ func TestString_AvailableTypes(t *testing.T) {
|
||||||
{"string(1 )", false},
|
{"string(1 )", false},
|
||||||
{"string( 1 )", false},
|
{"string( 1 )", false},
|
||||||
|
|
||||||
|
{"string()", false},
|
||||||
|
{"string(a)", false},
|
||||||
|
{"string(-1)", false},
|
||||||
|
|
||||||
|
{"string(,)", false},
|
||||||
|
{"string(1,b)", false},
|
||||||
|
{"string(a,b)", false},
|
||||||
|
{"string(a,1)", false},
|
||||||
|
{"string(-1,1)", false},
|
||||||
|
{"string(1,-1)", false},
|
||||||
|
{"string(-1,-1)", false},
|
||||||
|
|
||||||
{"string(1,2)", true},
|
{"string(1,2)", true},
|
||||||
{"string(1, 2)", true},
|
{"string(1, 2)", true},
|
||||||
{"string(1, 2)", false},
|
{"string(1, 2)", false},
|
||||||
|
|
|
@ -96,6 +96,11 @@ func TestUint_Values(t *testing.T) {
|
||||||
// strane offset because of how precision works
|
// strane offset because of how precision works
|
||||||
{fmt.Sprintf("%f", float64(math.MaxUint64+1024*3)), false},
|
{fmt.Sprintf("%f", float64(math.MaxUint64+1024*3)), false},
|
||||||
|
|
||||||
|
{[]byte(fmt.Sprintf("%d", math.MaxInt64)), true},
|
||||||
|
{[]byte(fmt.Sprintf("%d", uint(math.MaxUint64))), true},
|
||||||
|
// strane offset because of how precision works
|
||||||
|
{[]byte(fmt.Sprintf("%f", float64(math.MaxUint64+1024*3))), false},
|
||||||
|
|
||||||
{"string", false},
|
{"string", false},
|
||||||
{[]byte("bytes"), false},
|
{[]byte("bytes"), false},
|
||||||
{-0.1, false},
|
{-0.1, false},
|
||||||
|
|
Loading…
Reference in New Issue