File: //opt/go/pkg/mod/github.com/go-openapi/
[email protected]/middleware/parameter_test.go
// Copyright 2015 go-swagger maintainers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package middleware
import (
"math"
"net/url"
"reflect"
"strconv"
"testing"
"github.com/go-openapi/errors"
"github.com/go-openapi/spec"
"github.com/go-openapi/strfmt"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/go-openapi/runtime"
)
type paramFactory func(string) *spec.Parameter
var paramFactories = []paramFactory{
spec.QueryParam,
spec.HeaderParam,
spec.PathParam,
spec.FormDataParam,
}
func np(param *spec.Parameter) *untypedParamBinder {
return newUntypedParamBinder(*param, new(spec.Swagger), strfmt.Default)
}
var stringItems = new(spec.Items)
func init() {
stringItems.Type = typeString
}
func testCollectionFormat(t *testing.T, param *spec.Parameter, valid bool) {
binder := &untypedParamBinder{
parameter: param,
}
_, _, _, err := binder.readValue(runtime.Values(nil), reflect.ValueOf(nil)) //nolint:dogsled // we just want to test the error
if valid {
require.NoError(t, err)
} else {
require.Error(t, err)
require.EqualError(t, err, errors.InvalidCollectionFormat(param.Name, param.In, param.CollectionFormat).Error())
}
}
func requiredError(param *spec.Parameter, data interface{}) *errors.Validation {
return errors.Required(param.Name, param.In, data)
}
func validateRequiredTest(t *testing.T, param *spec.Parameter, value reflect.Value) {
binder := np(param)
err := binder.bindValue([]string{}, true, value)
require.Error(t, err)
assert.NotNil(t, param)
require.EqualError(t, requiredError(param, value.Interface()), err.Error())
err = binder.bindValue([]string{""}, true, value)
require.Error(t, err)
require.EqualError(t, requiredError(param, value.Interface()), err.Error())
// should be impossible data, but let's go with it
err = binder.bindValue([]string{"a"}, false, value)
require.Error(t, err)
require.EqualError(t, err, requiredError(param, value.Interface()).Error())
err = binder.bindValue([]string{""}, false, value)
require.Error(t, err)
require.EqualError(t, requiredError(param, value.Interface()), err.Error())
}
func validateRequiredAllowEmptyTest(t *testing.T, param *spec.Parameter, value reflect.Value) {
param.AllowEmptyValue = true
binder := np(param)
err := binder.bindValue([]string{}, true, value)
require.NoError(t, err)
require.NotNil(t, param)
err = binder.bindValue([]string{""}, true, value)
require.NoError(t, err)
err = binder.bindValue([]string{"1"}, false, value)
require.Error(t, err)
require.EqualError(t, requiredError(param, value.Interface()), err.Error())
err = binder.bindValue([]string{""}, false, value)
require.Error(t, err)
require.EqualError(t, requiredError(param, value.Interface()), err.Error())
}
func TestRequiredValidation(t *testing.T) {
strParam := spec.QueryParam("name").Typed(typeString, "").AsRequired()
validateRequiredTest(t, strParam, reflect.ValueOf(""))
validateRequiredAllowEmptyTest(t, strParam, reflect.ValueOf(""))
intParam := spec.QueryParam("id").Typed("integer", "int32").AsRequired()
validateRequiredTest(t, intParam, reflect.ValueOf(int32(0)))
validateRequiredAllowEmptyTest(t, intParam, reflect.ValueOf(int32(0)))
longParam := spec.QueryParam("id").Typed("integer", "int64").AsRequired()
validateRequiredTest(t, longParam, reflect.ValueOf(int64(0)))
validateRequiredAllowEmptyTest(t, longParam, reflect.ValueOf(int64(0)))
floatParam := spec.QueryParam("score").Typed("number", "float").AsRequired()
validateRequiredTest(t, floatParam, reflect.ValueOf(float32(0)))
validateRequiredAllowEmptyTest(t, floatParam, reflect.ValueOf(float32(0)))
doubleParam := spec.QueryParam("score").Typed("number", "double").AsRequired()
validateRequiredTest(t, doubleParam, reflect.ValueOf(float64(0)))
validateRequiredAllowEmptyTest(t, doubleParam, reflect.ValueOf(float64(0)))
dateTimeParam := spec.QueryParam("registered").Typed(typeString, "date-time").AsRequired()
validateRequiredTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{}))
// validateRequiredAllowEmptyTest(t, dateTimeParam, reflect.ValueOf(strfmt.DateTime{}))
dateParam := spec.QueryParam("registered").Typed(typeString, "date").AsRequired()
validateRequiredTest(t, dateParam, reflect.ValueOf(strfmt.Date{}))
// validateRequiredAllowEmptyTest(t, dateParam, reflect.ValueOf(strfmt.DateTime{}))
sliceParam := spec.QueryParam("tags").CollectionOf(stringItems, "").AsRequired()
validateRequiredTest(t, sliceParam, reflect.MakeSlice(reflect.TypeOf([]string{}), 0, 0))
validateRequiredAllowEmptyTest(t, sliceParam, reflect.MakeSlice(reflect.TypeOf([]string{}), 0, 0))
}
func TestInvalidCollectionFormat(t *testing.T) {
validCf1 := spec.QueryParam("validFmt").CollectionOf(stringItems, "multi")
validCf2 := spec.FormDataParam("validFmt2").CollectionOf(stringItems, "multi")
invalidCf1 := spec.HeaderParam("invalidHdr").CollectionOf(stringItems, "multi")
invalidCf2 := spec.PathParam("invalidPath").CollectionOf(stringItems, "multi")
testCollectionFormat(t, validCf1, true)
testCollectionFormat(t, validCf2, true)
testCollectionFormat(t, invalidCf1, false)
testCollectionFormat(t, invalidCf2, false)
}
func invalidTypeError(param *spec.Parameter, data interface{}) *errors.Validation {
tpe := param.Type
if param.Format != "" {
tpe = param.Format
}
return errors.InvalidType(param.Name, param.In, tpe, data)
}
func TestTypeValidation(t *testing.T) {
for _, newParam := range paramFactories {
intParam := newParam("badInt").Typed("integer", "int32")
value := reflect.ValueOf(int32(0))
binder := np(intParam)
err := binder.bindValue([]string{"yada"}, true, value)
// fails for invalid string
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(intParam, "yada").Error())
// fails for overflow
val := int64(math.MaxInt32)
str := strconv.FormatInt(val, 10) + "0"
v := int32(0)
value = reflect.ValueOf(&v).Elem()
binder = np(intParam)
err = binder.bindValue([]string{str}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(intParam, str).Error())
// fails for invalid string
longParam := newParam("badLong").Typed("integer", "int64")
value = reflect.ValueOf(int64(0))
binder = np(longParam)
err = binder.bindValue([]string{"yada"}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(longParam, "yada").Error())
// fails for overflow
str2 := strconv.FormatInt(math.MaxInt64, 10) + "0"
v2 := int64(0)
vv2 := reflect.ValueOf(&v2).Elem()
binder = np(longParam)
err = binder.bindValue([]string{str2}, true, vv2)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(longParam, str2).Error())
// fails for invalid string
floatParam := newParam("badFloat").Typed("number", "float")
value = reflect.ValueOf(float64(0))
binder = np(floatParam)
err = binder.bindValue([]string{"yada"}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(floatParam, "yada").Error())
// fails for overflow
str3 := strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
v3 := reflect.TypeOf(float32(0))
value = reflect.New(v3).Elem()
binder = np(floatParam)
err = binder.bindValue([]string{str3}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(floatParam, str3).Error())
// fails for invalid string
doubleParam := newParam("badDouble").Typed("number", "double")
value = reflect.ValueOf(float64(0))
binder = np(doubleParam)
err = binder.bindValue([]string{"yada"}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(doubleParam, "yada").Error())
// fails for overflow
str4 := "9" + strconv.FormatFloat(math.MaxFloat64, 'f', 5, 64)
v4 := reflect.TypeOf(float64(0))
value = reflect.New(v4).Elem()
binder = np(doubleParam)
err = binder.bindValue([]string{str4}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(doubleParam, str4).Error())
// fails for invalid string
dateParam := newParam("badDate").Typed(typeString, "date")
value = reflect.ValueOf(strfmt.Date{})
binder = np(dateParam)
err = binder.bindValue([]string{"yada"}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(dateParam, "yada").Error())
// fails for invalid string
dateTimeParam := newParam("badDateTime").Typed(typeString, "date-time")
value = reflect.ValueOf(strfmt.DateTime{})
binder = np(dateTimeParam)
err = binder.bindValue([]string{"yada"}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(dateTimeParam, "yada").Error())
// fails for invalid string
byteParam := newParam("badByte").Typed(typeString, "byte")
values := url.Values(map[string][]string{})
values.Add("badByte", "yaüda")
v5 := []byte{}
value = reflect.ValueOf(&v5).Elem()
binder = np(byteParam)
err = binder.bindValue([]string{"yaüda"}, true, value)
require.Error(t, err)
require.EqualError(t, err, invalidTypeError(byteParam, "yaüda").Error())
}
}
func TestTypeDetectionInvalidItems(t *testing.T) {
withoutItems := spec.QueryParam("without").CollectionOf(nil, "")
binder := &untypedParamBinder{
Name: "without",
parameter: withoutItems,
}
assert.Nil(t, binder.Type())
items := new(spec.Items)
items.Type = typeArray
withInvalidItems := spec.QueryParam("invalidItems").CollectionOf(items, "")
binder = &untypedParamBinder{
Name: "invalidItems",
parameter: withInvalidItems,
}
assert.Nil(t, binder.Type())
noType := spec.QueryParam("invalidType")
noType.Type = "invalid"
binder = &untypedParamBinder{
Name: "invalidType",
parameter: noType,
}
assert.Nil(t, binder.Type())
}
// type emailStrFmt struct {
// name string
// tpe reflect.Type
// validator FormatValidator
// }
//
// func (e *emailStrFmt) Name() string {
// return e.name
// }
//
// func (e *emailStrFmt) Type() reflect.Type {
// return e.tpe
// }
//
// func (e *emailStrFmt) Matches(str string) bool {
// return e.validator(str)
// }
//
// func TestTypeDetectionValid(t *testing.T) {
// // emlFmt := &emailStrFmt{
// // name: "email",
// // tpe: reflect.TypeOf(email{}),
// // }
// // formats := []StringFormat{emlFmt}
//
// expected := map[string]reflect.Type{
// "name": reflect.TypeOf(""),
// "id": reflect.TypeOf(int64(0)),
// "age": reflect.TypeOf(int32(0)),
// "score": reflect.TypeOf(float32(0)),
// "factor": reflect.TypeOf(float64(0)),
// "friend": reflect.TypeOf(map[string]interface{}{}),
// "X-Request-Id": reflect.TypeOf(int64(0)),
// "tags": reflect.TypeOf([]string{}),
// "confirmed": reflect.TypeOf(true),
// "planned": reflect.TypeOf(swagger.Date{}),
// "delivered": reflect.TypeOf(swagger.DateTime{}),
// "email": reflect.TypeOf(email{}),
// "picture": reflect.TypeOf([]byte{}),
// "file": reflect.TypeOf(&swagger.File{}).Elem(),
// }
//
// params := parametersForAllTypes("")
// emailParam := spec.QueryParam("email").Typed(typeString, "email")
// params["email"] = *emailParam
//
// fileParam := spec.FileParam("file")
// params["file"] = *fileParam
//
// for _, v := range params {
// binder := ¶mBinder{
// formats: formats,
// name: v.Name,
// parameter: &v,
// }
// assert.Equal(t, expected[v.Name], binder.Type(), "name: %s", v.Name)
// }
// }