...

Source file src/github.com/go-openapi/runtime/middleware/parameter_test.go

Documentation: github.com/go-openapi/runtime/middleware

     1  // Copyright 2015 go-swagger maintainers
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //    http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package middleware
    16  
    17  import (
    18  	"math"
    19  	"net/url"
    20  	"reflect"
    21  	"strconv"
    22  	"testing"
    23  
    24  	"github.com/go-openapi/errors"
    25  	"github.com/go-openapi/spec"
    26  	"github.com/go-openapi/strfmt"
    27  	"github.com/stretchr/testify/assert"
    28  	"github.com/stretchr/testify/require"
    29  
    30  	"github.com/go-openapi/runtime"
    31  )
    32  
    33  type paramFactory func(string) *spec.Parameter
    34  
    35  var paramFactories = []paramFactory{
    36  	spec.QueryParam,
    37  	spec.HeaderParam,
    38  	spec.PathParam,
    39  	spec.FormDataParam,
    40  }
    41  
    42  func np(param *spec.Parameter) *untypedParamBinder {
    43  	return newUntypedParamBinder(*param, new(spec.Swagger), strfmt.Default)
    44  }
    45  
    46  var stringItems = new(spec.Items)
    47  
    48  func init() {
    49  	stringItems.Type = typeString
    50  }
    51  
    52  func testCollectionFormat(t *testing.T, param *spec.Parameter, valid bool) {
    53  	binder := &untypedParamBinder{
    54  		parameter: param,
    55  	}
    56  	_, _, _, err := binder.readValue(runtime.Values(nil), reflect.ValueOf(nil)) //nolint:dogsled // we just want to test the error
    57  	if valid {
    58  		require.NoError(t, err)
    59  	} else {
    60  		require.Error(t, err)
    61  		require.EqualError(t, err, errors.InvalidCollectionFormat(param.Name, param.In, param.CollectionFormat).Error())
    62  	}
    63  }
    64  
    65  func requiredError(param *spec.Parameter, data interface{}) *errors.Validation {
    66  	return errors.Required(param.Name, param.In, data)
    67  }
    68  
    69  func validateRequiredTest(t *testing.T, param *spec.Parameter, value reflect.Value) {
    70  	binder := np(param)
    71  
    72  	err := binder.bindValue([]string{}, true, value)
    73  	require.Error(t, err)
    74  	assert.NotNil(t, param)
    75  	require.EqualError(t, requiredError(param, value.Interface()), err.Error())
    76  
    77  	err = binder.bindValue([]string{""}, true, value)
    78  	require.Error(t, err)
    79  	require.EqualError(t, requiredError(param, value.Interface()), err.Error())
    80  
    81  	// should be impossible data, but let's go with it
    82  	err = binder.bindValue([]string{"a"}, false, value)
    83  	require.Error(t, err)
    84  	require.EqualError(t, err, requiredError(param, value.Interface()).Error())
    85  
    86  	err = binder.bindValue([]string{""}, false, value)
    87  	require.Error(t, err)
    88  	require.EqualError(t, requiredError(param, value.Interface()), err.Error())
    89  }
    90  
    91  func validateRequiredAllowEmptyTest(t *testing.T, param *spec.Parameter, value reflect.Value) {
    92  	param.AllowEmptyValue = true
    93  	binder := np(param)
    94  	err := binder.bindValue([]string{}, true, value)
    95  	require.NoError(t, err)
    96  	require.NotNil(t, param)
    97  
    98  	err = binder.bindValue([]string{""}, true, value)
    99  	require.NoError(t, err)
   100  
   101  	err = binder.bindValue([]string{"1"}, false, value)
   102  	require.Error(t, err)
   103  	require.EqualError(t, requiredError(param, value.Interface()), err.Error())
   104  
   105  	err = binder.bindValue([]string{""}, false, value)
   106  	require.Error(t, err)
   107  	require.EqualError(t, requiredError(param, value.Interface()), err.Error())
   108  }
   109  
   110  func TestRequiredValidation(t *testing.T) {
   111  	strParam := spec.QueryParam("name").Typed(typeString, "").AsRequired()
   112  	validateRequiredTest(t, strParam, reflect.ValueOf(""))
   113  	validateRequiredAllowEmptyTest(t, strParam, reflect.ValueOf(""))
   114  
   115  	intParam := spec.QueryParam("id").Typed("integer", "int32").AsRequired()
   116  	validateRequiredTest(t, intParam, reflect.ValueOf(int32(0)))
   117  	validateRequiredAllowEmptyTest(t, intParam, reflect.ValueOf(int32(0)))
   118  	longParam := spec.QueryParam("id").Typed("integer", "int64").AsRequired()
   119  	validateRequiredTest(t, longParam, reflect.ValueOf(int64(0)))
   120  	validateRequiredAllowEmptyTest(t, longParam, reflect.ValueOf(int64(0)))
   121  
   122  	floatParam := spec.QueryParam("score").Typed("number", "float").AsRequired()
   123  	validateRequiredTest(t, floatParam, reflect.ValueOf(float32(0)))
   124  	validateRequiredAllowEmptyTest(t, floatParam, reflect.ValueOf(float32(0)))
   125  	doubleParam := spec.QueryParam("score").Typed("number", "double").AsRequired()
   126  	validateRequiredTest(t, doubleParam, reflect.ValueOf(float64(0)))
   127  	validateRequiredAllowEmptyTest(t, doubleParam, reflect.ValueOf(float64(0)))
   128  
   129  	dateTimeParam := spec.QueryParam("registered").Typed(typeString, "date-time").AsRequired()
   130  	validateRequiredTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{}))
   131  	// validateRequiredAllowEmptyTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{}))
   132  
   133  	dateParam := spec.QueryParam("registered").Typed(typeString, "date").AsRequired()
   134  	validateRequiredTest(t, dateParam, reflect.ValueOf(strfmt.Date{}))
   135  	// validateRequiredAllowEmptyTest(t, dateParam, reflect.ValueOf(strfmt.DateTime{}))
   136  
   137  	sliceParam := spec.QueryParam("tags").CollectionOf(stringItems, "").AsRequired()
   138  	validateRequiredTest(t, sliceParam, reflect.MakeSlice(reflect.TypeOf([]string{}), 0, 0))
   139  	validateRequiredAllowEmptyTest(t, sliceParam, reflect.MakeSlice(reflect.TypeOf([]string{}), 0, 0))
   140  }
   141  
   142  func TestInvalidCollectionFormat(t *testing.T) {
   143  	validCf1 := spec.QueryParam("validFmt").CollectionOf(stringItems, "multi")
   144  	validCf2 := spec.FormDataParam("validFmt2").CollectionOf(stringItems, "multi")
   145  	invalidCf1 := spec.HeaderParam("invalidHdr").CollectionOf(stringItems, "multi")
   146  	invalidCf2 := spec.PathParam("invalidPath").CollectionOf(stringItems, "multi")
   147  
   148  	testCollectionFormat(t, validCf1, true)
   149  	testCollectionFormat(t, validCf2, true)
   150  	testCollectionFormat(t, invalidCf1, false)
   151  	testCollectionFormat(t, invalidCf2, false)
   152  }
   153  
   154  func invalidTypeError(param *spec.Parameter, data interface{}) *errors.Validation {
   155  	tpe := param.Type
   156  	if param.Format != "" {
   157  		tpe = param.Format
   158  	}
   159  	return errors.InvalidType(param.Name, param.In, tpe, data)
   160  }
   161  
   162  func TestTypeValidation(t *testing.T) {
   163  	for _, newParam := range paramFactories {
   164  		intParam := newParam("badInt").Typed("integer", "int32")
   165  		value := reflect.ValueOf(int32(0))
   166  		binder := np(intParam)
   167  		err := binder.bindValue([]string{"yada"}, true, value)
   168  		// fails for invalid string
   169  		require.Error(t, err)
   170  		require.EqualError(t, err, invalidTypeError(intParam, "yada").Error())
   171  
   172  		// fails for overflow
   173  		val := int64(math.MaxInt32)
   174  		str := strconv.FormatInt(val, 10) + "0"
   175  		v := int32(0)
   176  		value = reflect.ValueOf(&v).Elem()
   177  
   178  		binder = np(intParam)
   179  		err = binder.bindValue([]string{str}, true, value)
   180  		require.Error(t, err)
   181  		require.EqualError(t, err, invalidTypeError(intParam, str).Error())
   182  
   183  		// fails for invalid string
   184  		longParam := newParam("badLong").Typed("integer", "int64")
   185  		value = reflect.ValueOf(int64(0))
   186  
   187  		binder = np(longParam)
   188  		err = binder.bindValue([]string{"yada"}, true, value)
   189  		require.Error(t, err)
   190  		require.EqualError(t, err, invalidTypeError(longParam, "yada").Error())
   191  
   192  		// fails for overflow
   193  		str2 := strconv.FormatInt(math.MaxInt64, 10) + "0"
   194  		v2 := int64(0)
   195  		vv2 := reflect.ValueOf(&v2).Elem()
   196  		binder = np(longParam)
   197  		err = binder.bindValue([]string{str2}, true, vv2)
   198  		require.Error(t, err)
   199  		require.EqualError(t, err, invalidTypeError(longParam, str2).Error())
   200  
   201  		// fails for invalid string
   202  		floatParam := newParam("badFloat").Typed("number", "float")
   203  		value = reflect.ValueOf(float64(0))
   204  		binder = np(floatParam)
   205  		err = binder.bindValue([]string{"yada"}, true, value)
   206  		require.Error(t, err)
   207  		require.EqualError(t, err, invalidTypeError(floatParam, "yada").Error())
   208  
   209  		// fails for overflow
   210  		str3 := strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
   211  		v3 := reflect.TypeOf(float32(0))
   212  		value = reflect.New(v3).Elem()
   213  		binder = np(floatParam)
   214  		err = binder.bindValue([]string{str3}, true, value)
   215  		require.Error(t, err)
   216  		require.EqualError(t, err, invalidTypeError(floatParam, str3).Error())
   217  
   218  		// fails for invalid string
   219  		doubleParam := newParam("badDouble").Typed("number", "double")
   220  		value = reflect.ValueOf(float64(0))
   221  		binder = np(doubleParam)
   222  		err = binder.bindValue([]string{"yada"}, true, value)
   223  		require.Error(t, err)
   224  		require.EqualError(t, err, invalidTypeError(doubleParam, "yada").Error())
   225  
   226  		// fails for overflow
   227  		str4 := "9" + strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
   228  		v4 := reflect.TypeOf(float64(0))
   229  		value = reflect.New(v4).Elem()
   230  		binder = np(doubleParam)
   231  		err = binder.bindValue([]string{str4}, true, value)
   232  		require.Error(t, err)
   233  		require.EqualError(t, err, invalidTypeError(doubleParam, str4).Error())
   234  
   235  		// fails for invalid string
   236  		dateParam := newParam("badDate").Typed(typeString, "date")
   237  		value = reflect.ValueOf(strfmt.Date{})
   238  		binder = np(dateParam)
   239  		err = binder.bindValue([]string{"yada"}, true, value)
   240  		require.Error(t, err)
   241  		require.EqualError(t, err, invalidTypeError(dateParam, "yada").Error())
   242  
   243  		// fails for invalid string
   244  		dateTimeParam := newParam("badDateTime").Typed(typeString, "date-time")
   245  		value = reflect.ValueOf(strfmt.DateTime{})
   246  		binder = np(dateTimeParam)
   247  		err = binder.bindValue([]string{"yada"}, true, value)
   248  		require.Error(t, err)
   249  		require.EqualError(t, err, invalidTypeError(dateTimeParam, "yada").Error())
   250  
   251  		// fails for invalid string
   252  		byteParam := newParam("badByte").Typed(typeString, "byte")
   253  		values := url.Values(map[string][]string{})
   254  		values.Add("badByte", "yaüda")
   255  		v5 := []byte{}
   256  		value = reflect.ValueOf(&v5).Elem()
   257  		binder = np(byteParam)
   258  		err = binder.bindValue([]string{"yaüda"}, true, value)
   259  		require.Error(t, err)
   260  		require.EqualError(t, err, invalidTypeError(byteParam, "yaüda").Error())
   261  	}
   262  }
   263  
   264  func TestTypeDetectionInvalidItems(t *testing.T) {
   265  	withoutItems := spec.QueryParam("without").CollectionOf(nil, "")
   266  	binder := &untypedParamBinder{
   267  		Name:      "without",
   268  		parameter: withoutItems,
   269  	}
   270  	assert.Nil(t, binder.Type())
   271  
   272  	items := new(spec.Items)
   273  	items.Type = typeArray
   274  	withInvalidItems := spec.QueryParam("invalidItems").CollectionOf(items, "")
   275  	binder = &untypedParamBinder{
   276  		Name:      "invalidItems",
   277  		parameter: withInvalidItems,
   278  	}
   279  	assert.Nil(t, binder.Type())
   280  
   281  	noType := spec.QueryParam("invalidType")
   282  	noType.Type = "invalid"
   283  	binder = &untypedParamBinder{
   284  		Name:      "invalidType",
   285  		parameter: noType,
   286  	}
   287  	assert.Nil(t, binder.Type())
   288  }
   289  
   290  // type emailStrFmt struct {
   291  // 	name      string
   292  // 	tpe       reflect.Type
   293  // 	validator FormatValidator
   294  // }
   295  //
   296  // func (e *emailStrFmt) Name() string {
   297  // 	return e.name
   298  // }
   299  //
   300  // func (e *emailStrFmt) Type() reflect.Type {
   301  // 	return e.tpe
   302  // }
   303  //
   304  // func (e *emailStrFmt) Matches(str string) bool {
   305  // 	return e.validator(str)
   306  // }
   307  //
   308  // func TestTypeDetectionValid(t *testing.T) {
   309  // 	// emlFmt := &emailStrFmt{
   310  // 	// 	name: "email",
   311  // 	// 	tpe:  reflect.TypeOf(email{}),
   312  // 	// }
   313  // 	// formats := []StringFormat{emlFmt}
   314  //
   315  // 	expected := map[string]reflect.Type{
   316  // 		"name":         reflect.TypeOf(""),
   317  // 		"id":           reflect.TypeOf(int64(0)),
   318  // 		"age":          reflect.TypeOf(int32(0)),
   319  // 		"score":        reflect.TypeOf(float32(0)),
   320  // 		"factor":       reflect.TypeOf(float64(0)),
   321  // 		"friend":       reflect.TypeOf(map[string]interface{}{}),
   322  // 		"X-Request-Id": reflect.TypeOf(int64(0)),
   323  // 		"tags":         reflect.TypeOf([]string{}),
   324  // 		"confirmed":    reflect.TypeOf(true),
   325  // 		"planned":      reflect.TypeOf(swagger.Date{}),
   326  // 		"delivered":    reflect.TypeOf(swagger.DateTime{}),
   327  // 		"email":        reflect.TypeOf(email{}),
   328  // 		"picture":      reflect.TypeOf([]byte{}),
   329  // 		"file":         reflect.TypeOf(&swagger.File{}).Elem(),
   330  // 	}
   331  //
   332  // 	params := parametersForAllTypes("")
   333  // 	emailParam := spec.QueryParam("email").Typed(typeString, "email")
   334  // 	params["email"] = *emailParam
   335  //
   336  // 	fileParam := spec.FileParam("file")
   337  // 	params["file"] = *fileParam
   338  //
   339  // 	for _, v := range params {
   340  // 		binder := &paramBinder{
   341  // 			formats:   formats,
   342  // 			name:      v.Name,
   343  // 			parameter: &v,
   344  // 		}
   345  // 		assert.Equal(t, expected[v.Name], binder.Type(), "name: %s", v.Name)
   346  // 	}
   347  // }
   348  

View as plain text