2022-05-28 05:42:28 +03:00
|
|
|
// Copyright 2014 Manu Martinez-Almeida. All rights reserved.
|
2015-04-09 13:15:02 +03:00
|
|
|
// Use of this source code is governed by a MIT style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
|
|
|
package binding
|
|
|
|
|
|
|
|
import (
|
2016-01-26 21:28:26 +03:00
|
|
|
"bytes"
|
2015-04-09 13:15:02 +03:00
|
|
|
"testing"
|
2016-01-26 21:28:26 +03:00
|
|
|
"time"
|
2015-04-09 13:15:02 +03:00
|
|
|
|
2019-11-25 09:49:45 +03:00
|
|
|
"github.com/go-playground/validator/v10"
|
2015-04-09 13:15:02 +03:00
|
|
|
"github.com/stretchr/testify/assert"
|
2024-07-14 15:33:08 +03:00
|
|
|
"github.com/stretchr/testify/require"
|
2015-04-09 13:15:02 +03:00
|
|
|
)
|
|
|
|
|
2016-01-26 21:28:26 +03:00
|
|
|
type testInterface interface {
|
|
|
|
String() string
|
2015-04-09 13:15:02 +03:00
|
|
|
}
|
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
type substructNoValidation struct {
|
|
|
|
IString string
|
|
|
|
IInt int
|
2015-04-09 13:15:02 +03:00
|
|
|
}
|
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
type mapNoValidationSub map[string]substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
type structNoValidationValues struct {
|
|
|
|
substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
Boolean bool
|
|
|
|
|
|
|
|
Uinteger uint
|
2015-04-09 13:15:02 +03:00
|
|
|
Integer int
|
2016-01-26 21:28:26 +03:00
|
|
|
Integer8 int8
|
|
|
|
Integer16 int16
|
|
|
|
Integer32 int32
|
|
|
|
Integer64 int64
|
|
|
|
Uinteger8 uint8
|
|
|
|
Uinteger16 uint16
|
|
|
|
Uinteger32 uint32
|
|
|
|
Uinteger64 uint64
|
|
|
|
|
|
|
|
Float32 float32
|
|
|
|
Float64 float64
|
|
|
|
|
|
|
|
String string
|
|
|
|
|
|
|
|
Date time.Time
|
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
Struct substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
InlinedStruct struct {
|
|
|
|
String []string
|
|
|
|
Integer int
|
|
|
|
}
|
|
|
|
|
|
|
|
IntSlice []int
|
|
|
|
IntPointerSlice []*int
|
2016-04-15 02:16:46 +03:00
|
|
|
StructPointerSlice []*substructNoValidation
|
|
|
|
StructSlice []substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
InterfaceSlice []testInterface
|
|
|
|
|
2022-03-21 04:43:17 +03:00
|
|
|
UniversalInterface any
|
2016-01-26 21:28:26 +03:00
|
|
|
CustomInterface testInterface
|
|
|
|
|
|
|
|
FloatMap map[string]float32
|
|
|
|
StructMap mapNoValidationSub
|
2015-04-09 13:15:02 +03:00
|
|
|
}
|
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
func createNoValidationValues() structNoValidationValues {
|
2016-01-26 21:28:26 +03:00
|
|
|
integer := 1
|
2016-04-15 02:16:46 +03:00
|
|
|
s := structNoValidationValues{
|
2016-01-26 21:28:26 +03:00
|
|
|
Boolean: true,
|
|
|
|
Uinteger: 1 << 29,
|
|
|
|
Integer: -10000,
|
|
|
|
Integer8: 120,
|
|
|
|
Integer16: -20000,
|
|
|
|
Integer32: 1 << 29,
|
|
|
|
Integer64: 1 << 61,
|
|
|
|
Uinteger8: 250,
|
|
|
|
Uinteger16: 50000,
|
|
|
|
Uinteger32: 1 << 31,
|
|
|
|
Uinteger64: 1 << 62,
|
|
|
|
Float32: 123.456,
|
|
|
|
Float64: 123.456789,
|
|
|
|
String: "text",
|
|
|
|
Date: time.Time{},
|
|
|
|
CustomInterface: &bytes.Buffer{},
|
2016-04-15 02:16:46 +03:00
|
|
|
Struct: substructNoValidation{},
|
2016-01-26 21:28:26 +03:00
|
|
|
IntSlice: []int{-3, -2, 1, 0, 1, 2, 3},
|
|
|
|
IntPointerSlice: []*int{&integer},
|
2016-04-15 02:16:46 +03:00
|
|
|
StructSlice: []substructNoValidation{},
|
2016-01-26 21:28:26 +03:00
|
|
|
UniversalInterface: 1.2,
|
|
|
|
FloatMap: map[string]float32{
|
|
|
|
"foo": 1.23,
|
|
|
|
"bar": 232.323,
|
|
|
|
},
|
|
|
|
StructMap: mapNoValidationSub{
|
2016-04-15 02:16:46 +03:00
|
|
|
"foo": substructNoValidation{},
|
|
|
|
"bar": substructNoValidation{},
|
2015-04-09 13:15:02 +03:00
|
|
|
},
|
2016-01-26 21:28:26 +03:00
|
|
|
// StructPointerSlice []noValidationSub
|
|
|
|
// InterfaceSlice []testInterface
|
2015-04-09 13:15:02 +03:00
|
|
|
}
|
2016-01-26 21:28:26 +03:00
|
|
|
s.InlinedStruct.Integer = 1000
|
|
|
|
s.InlinedStruct.String = []string{"first", "second"}
|
2016-04-15 02:16:46 +03:00
|
|
|
s.IString = "substring"
|
|
|
|
s.IInt = 987654
|
2016-01-26 21:28:26 +03:00
|
|
|
return s
|
2015-04-09 13:15:02 +03:00
|
|
|
}
|
|
|
|
|
2016-01-26 21:28:26 +03:00
|
|
|
func TestValidateNoValidationValues(t *testing.T) {
|
2016-04-15 02:16:46 +03:00
|
|
|
origin := createNoValidationValues()
|
|
|
|
test := createNoValidationValues()
|
|
|
|
empty := structNoValidationValues{}
|
2016-01-26 21:28:26 +03:00
|
|
|
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, validate(test))
|
|
|
|
require.NoError(t, validate(&test))
|
|
|
|
require.NoError(t, validate(empty))
|
|
|
|
require.NoError(t, validate(&empty))
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
assert.Equal(t, origin, test)
|
2015-04-09 13:15:02 +03:00
|
|
|
}
|
2015-05-29 21:34:41 +03:00
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
type structNoValidationPointer struct {
|
|
|
|
substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
Boolean bool
|
2015-05-29 21:34:41 +03:00
|
|
|
|
2016-01-26 21:28:26 +03:00
|
|
|
Uinteger *uint
|
|
|
|
Integer *int
|
|
|
|
Integer8 *int8
|
|
|
|
Integer16 *int16
|
|
|
|
Integer32 *int32
|
|
|
|
Integer64 *int64
|
|
|
|
Uinteger8 *uint8
|
|
|
|
Uinteger16 *uint16
|
|
|
|
Uinteger32 *uint32
|
|
|
|
Uinteger64 *uint64
|
2015-05-29 21:34:41 +03:00
|
|
|
|
2016-01-26 21:28:26 +03:00
|
|
|
Float32 *float32
|
|
|
|
Float64 *float64
|
|
|
|
|
|
|
|
String *string
|
|
|
|
|
|
|
|
Date *time.Time
|
|
|
|
|
2016-04-15 02:16:46 +03:00
|
|
|
Struct *substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
IntSlice *[]int
|
|
|
|
IntPointerSlice *[]*int
|
2016-04-15 02:16:46 +03:00
|
|
|
StructPointerSlice *[]*substructNoValidation
|
|
|
|
StructSlice *[]substructNoValidation
|
2016-01-26 21:28:26 +03:00
|
|
|
InterfaceSlice *[]testInterface
|
|
|
|
|
|
|
|
FloatMap *map[string]float32
|
|
|
|
StructMap *mapNoValidationSub
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestValidateNoValidationPointers(t *testing.T) {
|
|
|
|
//origin := createNoValidation_values()
|
|
|
|
//test := createNoValidation_values()
|
2016-04-15 02:16:46 +03:00
|
|
|
empty := structNoValidationPointer{}
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
//assert.Nil(t, validate(test))
|
|
|
|
//assert.Nil(t, validate(&test))
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, validate(empty))
|
|
|
|
require.NoError(t, validate(&empty))
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
//assert.Equal(t, origin, test)
|
|
|
|
}
|
|
|
|
|
2022-03-21 04:43:17 +03:00
|
|
|
type Object map[string]any
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
func TestValidatePrimitives(t *testing.T) {
|
|
|
|
obj := Object{"foo": "bar", "bar": 1}
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, validate(obj))
|
|
|
|
require.NoError(t, validate(&obj))
|
2018-04-20 05:27:44 +03:00
|
|
|
assert.Equal(t, Object{"foo": "bar", "bar": 1}, obj)
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
obj2 := []Object{{"foo": "bar", "bar": 1}, {"foo": "bar", "bar": 1}}
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, validate(obj2))
|
|
|
|
require.NoError(t, validate(&obj2))
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
nu := 10
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, validate(nu))
|
|
|
|
require.NoError(t, validate(&nu))
|
2018-04-20 05:27:44 +03:00
|
|
|
assert.Equal(t, 10, nu)
|
2016-01-26 21:28:26 +03:00
|
|
|
|
|
|
|
str := "value"
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, validate(str))
|
|
|
|
require.NoError(t, validate(&str))
|
2018-04-20 05:27:44 +03:00
|
|
|
assert.Equal(t, "value", str)
|
2015-05-29 21:34:41 +03:00
|
|
|
}
|
2017-08-27 10:37:39 +03:00
|
|
|
|
2024-02-07 14:44:11 +03:00
|
|
|
type structModifyValidation struct {
|
|
|
|
Integer int
|
|
|
|
}
|
|
|
|
|
|
|
|
func toZero(sl validator.StructLevel) {
|
|
|
|
var s *structModifyValidation = sl.Top().Interface().(*structModifyValidation)
|
|
|
|
s.Integer = 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestValidateAndModifyStruct(t *testing.T) {
|
|
|
|
// This validates that pointers to structs are passed to the validator
|
|
|
|
// giving us the ability to modify the struct being validated.
|
|
|
|
engine, ok := Validator.Engine().(*validator.Validate)
|
|
|
|
assert.True(t, ok)
|
|
|
|
|
|
|
|
engine.RegisterStructValidation(toZero, structModifyValidation{})
|
|
|
|
|
|
|
|
s := structModifyValidation{Integer: 1}
|
|
|
|
errs := validate(&s)
|
|
|
|
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, errs)
|
|
|
|
assert.Equal(t, structModifyValidation{Integer: 0}, s)
|
2024-02-07 14:44:11 +03:00
|
|
|
}
|
|
|
|
|
2017-08-27 10:37:39 +03:00
|
|
|
// structCustomValidation is a helper struct we use to check that
|
|
|
|
// custom validation can be registered on it.
|
|
|
|
// The `notone` binding directive is for custom validation and registered later.
|
|
|
|
type structCustomValidation struct {
|
|
|
|
Integer int `binding:"notone"`
|
|
|
|
}
|
|
|
|
|
2019-09-05 16:39:56 +03:00
|
|
|
func notOne(f1 validator.FieldLevel) bool {
|
|
|
|
if val, ok := f1.Field().Interface().(int); ok {
|
2017-08-27 10:37:39 +03:00
|
|
|
return val != 1
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2018-03-29 09:33:07 +03:00
|
|
|
func TestValidatorEngine(t *testing.T) {
|
2017-08-27 10:37:39 +03:00
|
|
|
// This validates that the function `notOne` matches
|
|
|
|
// the expected function signature by `defaultValidator`
|
|
|
|
// and by extension the validator library.
|
2018-03-29 09:33:07 +03:00
|
|
|
engine, ok := Validator.Engine().(*validator.Validate)
|
|
|
|
assert.True(t, ok)
|
|
|
|
|
|
|
|
err := engine.RegisterValidation("notone", notOne)
|
2017-08-27 10:37:39 +03:00
|
|
|
// Check that we can register custom validation without error
|
2024-07-14 15:33:08 +03:00
|
|
|
require.NoError(t, err)
|
2017-08-27 10:37:39 +03:00
|
|
|
|
|
|
|
// Create an instance which will fail validation
|
|
|
|
withOne := structCustomValidation{Integer: 1}
|
|
|
|
errs := validate(withOne)
|
|
|
|
|
|
|
|
// Check that we got back non-nil errs
|
2024-07-14 15:33:08 +03:00
|
|
|
require.Error(t, errs)
|
2018-03-29 09:33:07 +03:00
|
|
|
// Check that the error matches expectation
|
2024-07-14 15:33:08 +03:00
|
|
|
require.Error(t, errs, "", "", "notone")
|
2017-08-27 10:37:39 +03:00
|
|
|
}
|