chore: add more tests (#3324)
This commit is contained in:
@@ -176,7 +176,12 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
|
|||||||
switch dereffedBaseKind {
|
switch dereffedBaseKind {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
target := reflect.New(dereffedBaseType)
|
target := reflect.New(dereffedBaseType)
|
||||||
if err := u.Unmarshal(ithValue.(map[string]any), target.Interface()); err != nil {
|
val, ok := ithValue.(map[string]any)
|
||||||
|
if !ok {
|
||||||
|
return errTypeMismatch
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := u.Unmarshal(val, target.Interface()); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -557,6 +557,15 @@ func TestUnmarshalIntWithString(t *testing.T) {
|
|||||||
var in inner
|
var in inner
|
||||||
assert.Error(t, UnmarshalKey(m, &in))
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
t.Run("invalid options", func(t *testing.T) {
|
||||||
|
type Value struct {
|
||||||
|
Name string `key:"name,options="`
|
||||||
|
}
|
||||||
|
|
||||||
|
var v Value
|
||||||
|
assert.Error(t, UnmarshalKey(emptyMap, &v))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalBoolSliceRequired(t *testing.T) {
|
func TestUnmarshalBoolSliceRequired(t *testing.T) {
|
||||||
@@ -675,28 +684,54 @@ func TestUnmarshalFloatSliceWithDefault(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalStringSliceWithDefault(t *testing.T) {
|
func TestUnmarshalStringSliceWithDefault(t *testing.T) {
|
||||||
type inner struct {
|
t.Run("slice with default", func(t *testing.T) {
|
||||||
Strs []string `key:"strs,default=[foo,bar,woo]"`
|
type inner struct {
|
||||||
Strps []*string `key:"strs,default=[foo,bar,woo]"`
|
Strs []string `key:"strs,default=[foo,bar,woo]"`
|
||||||
Strpps []**string `key:"strs,default=[foo,bar,woo]"`
|
Strps []*string `key:"strs,default=[foo,bar,woo]"`
|
||||||
}
|
Strpps []**string `key:"strs,default=[foo,bar,woo]"`
|
||||||
|
|
||||||
var in inner
|
|
||||||
if assert.NoError(t, UnmarshalKey(nil, &in)) {
|
|
||||||
assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, in.Strs)
|
|
||||||
|
|
||||||
var ss []string
|
|
||||||
for _, s := range in.Strps {
|
|
||||||
ss = append(ss, *s)
|
|
||||||
}
|
}
|
||||||
assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, ss)
|
|
||||||
|
|
||||||
var sss []string
|
var in inner
|
||||||
for _, s := range in.Strpps {
|
if assert.NoError(t, UnmarshalKey(nil, &in)) {
|
||||||
sss = append(sss, **s)
|
assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, in.Strs)
|
||||||
|
|
||||||
|
var ss []string
|
||||||
|
for _, s := range in.Strps {
|
||||||
|
ss = append(ss, *s)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, ss)
|
||||||
|
|
||||||
|
var sss []string
|
||||||
|
for _, s := range in.Strpps {
|
||||||
|
sss = append(sss, **s)
|
||||||
|
}
|
||||||
|
assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, sss)
|
||||||
}
|
}
|
||||||
assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, sss)
|
})
|
||||||
}
|
|
||||||
|
t.Run("slice with default on errors", func(t *testing.T) {
|
||||||
|
type (
|
||||||
|
holder struct {
|
||||||
|
Chan []chan int
|
||||||
|
}
|
||||||
|
|
||||||
|
inner struct {
|
||||||
|
Strs []holder `key:"strs,default=[foo,bar,woo]"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(nil, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("slice with default on errors", func(t *testing.T) {
|
||||||
|
type inner struct {
|
||||||
|
Strs []complex64 `key:"strs,default=[foo,bar,woo]"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(nil, &in))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalStringSliceWithDefaultHasSpaces(t *testing.T) {
|
func TestUnmarshalStringSliceWithDefaultHasSpaces(t *testing.T) {
|
||||||
@@ -1327,25 +1362,71 @@ func TestUnmarshalStructOptionalDependsNotErrorDetails(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalStructOptionalDependsNotNested(t *testing.T) {
|
func TestUnmarshalStructOptionalDependsNotNested(t *testing.T) {
|
||||||
type address struct {
|
t.Run("mutal optionals", func(t *testing.T) {
|
||||||
Optional string `key:",optional"`
|
type address struct {
|
||||||
OptionalDepends string `key:",optional=!Optional"`
|
Optional string `key:",optional"`
|
||||||
}
|
OptionalDepends string `key:",optional=!Optional"`
|
||||||
type combo struct {
|
}
|
||||||
Name string `key:"name,optional"`
|
type combo struct {
|
||||||
Address address `key:"address"`
|
Name string `key:"name,optional"`
|
||||||
}
|
Address address `key:"address"`
|
||||||
type inner struct {
|
}
|
||||||
Name string `key:"name"`
|
type inner struct {
|
||||||
Combo combo `key:"combo"`
|
Name string `key:"name"`
|
||||||
}
|
Combo combo `key:"combo"`
|
||||||
|
}
|
||||||
|
|
||||||
m := map[string]any{
|
m := map[string]any{
|
||||||
"name": "kevin",
|
"name": "kevin",
|
||||||
}
|
}
|
||||||
|
|
||||||
var in inner
|
var in inner
|
||||||
assert.Error(t, UnmarshalKey(m, &in))
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad format", func(t *testing.T) {
|
||||||
|
type address struct {
|
||||||
|
Optional string `key:",optional"`
|
||||||
|
OptionalDepends string `key:",optional=!Optional=abcd"`
|
||||||
|
}
|
||||||
|
type combo struct {
|
||||||
|
Name string `key:"name,optional"`
|
||||||
|
Address address `key:"address"`
|
||||||
|
}
|
||||||
|
type inner struct {
|
||||||
|
Name string `key:"name"`
|
||||||
|
Combo combo `key:"combo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"name": "kevin",
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid option", func(t *testing.T) {
|
||||||
|
type address struct {
|
||||||
|
Optional string `key:",optional"`
|
||||||
|
OptionalDepends string `key:",opt=abcd"`
|
||||||
|
}
|
||||||
|
type combo struct {
|
||||||
|
Name string `key:"name,optional"`
|
||||||
|
Address address `key:"address"`
|
||||||
|
}
|
||||||
|
type inner struct {
|
||||||
|
Name string `key:"name"`
|
||||||
|
Combo combo `key:"combo"`
|
||||||
|
}
|
||||||
|
|
||||||
|
m := map[string]any{
|
||||||
|
"name": "kevin",
|
||||||
|
}
|
||||||
|
|
||||||
|
var in inner
|
||||||
|
assert.Error(t, UnmarshalKey(m, &in))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshalStructOptionalNestedDifferentKey(t *testing.T) {
|
func TestUnmarshalStructOptionalNestedDifferentKey(t *testing.T) {
|
||||||
@@ -3855,20 +3936,37 @@ func TestUnmarshalValuer(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshal_EnvString(t *testing.T) {
|
func TestUnmarshal_EnvString(t *testing.T) {
|
||||||
type Value struct {
|
t.Run("valid env", func(t *testing.T) {
|
||||||
Name string `key:"name,env=TEST_NAME_STRING"`
|
type Value struct {
|
||||||
}
|
Name string `key:"name,env=TEST_NAME_STRING"`
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
envName = "TEST_NAME_STRING"
|
envName = "TEST_NAME_STRING"
|
||||||
envVal = "this is a name"
|
envVal = "this is a name"
|
||||||
)
|
)
|
||||||
t.Setenv(envName, envVal)
|
t.Setenv(envName, envVal)
|
||||||
|
|
||||||
var v Value
|
var v Value
|
||||||
if assert.NoError(t, UnmarshalKey(emptyMap, &v)) {
|
if assert.NoError(t, UnmarshalKey(emptyMap, &v)) {
|
||||||
assert.Equal(t, envVal, v.Name)
|
assert.Equal(t, envVal, v.Name)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("invalid env", func(t *testing.T) {
|
||||||
|
type Value struct {
|
||||||
|
Name string `key:"name,env=TEST_NAME_STRING=invalid"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
envName = "TEST_NAME_STRING"
|
||||||
|
envVal = "this is a name"
|
||||||
|
)
|
||||||
|
t.Setenv(envName, envVal)
|
||||||
|
|
||||||
|
var v Value
|
||||||
|
assert.Error(t, UnmarshalKey(emptyMap, &v))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshal_EnvStringOverwrite(t *testing.T) {
|
func TestUnmarshal_EnvStringOverwrite(t *testing.T) {
|
||||||
@@ -4044,20 +4142,22 @@ func TestUnmarshal_EnvDurationBadValue(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshal_EnvWithOptions(t *testing.T) {
|
func TestUnmarshal_EnvWithOptions(t *testing.T) {
|
||||||
type Value struct {
|
t.Run("valid options", func(t *testing.T) {
|
||||||
Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_MATCH,options=[abc,123,xyz]"`
|
type Value struct {
|
||||||
}
|
Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_MATCH,options=[abc,123,xyz]"`
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
envName = "TEST_NAME_ENV_OPTIONS_MATCH"
|
envName = "TEST_NAME_ENV_OPTIONS_MATCH"
|
||||||
envVal = "123"
|
envVal = "123"
|
||||||
)
|
)
|
||||||
t.Setenv(envName, envVal)
|
t.Setenv(envName, envVal)
|
||||||
|
|
||||||
var v Value
|
var v Value
|
||||||
if assert.NoError(t, UnmarshalKey(emptyMap, &v)) {
|
if assert.NoError(t, UnmarshalKey(emptyMap, &v)) {
|
||||||
assert.Equal(t, envVal, v.Name)
|
assert.Equal(t, envVal, v.Name)
|
||||||
}
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestUnmarshal_EnvWithOptionsWrongValueBool(t *testing.T) {
|
func TestUnmarshal_EnvWithOptionsWrongValueBool(t *testing.T) {
|
||||||
|
|||||||
@@ -372,8 +372,6 @@ func parseOption(fieldOpts *fieldOptions, fieldName, option string) error {
|
|||||||
default:
|
default:
|
||||||
return fmt.Errorf("field %q has wrong optional", fieldName)
|
return fmt.Errorf("field %q has wrong optional", fieldName)
|
||||||
}
|
}
|
||||||
case option == optionalOption:
|
|
||||||
fieldOpts.Optional = true
|
|
||||||
case strings.HasPrefix(option, optionsOption):
|
case strings.HasPrefix(option, optionsOption):
|
||||||
val, err := parseProperty(fieldName, optionsOption, option)
|
val, err := parseProperty(fieldName, optionsOption, option)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -241,7 +241,8 @@ func TestValidatePtrWithZeroValue(t *testing.T) {
|
|||||||
|
|
||||||
func TestSetValueNotSettable(t *testing.T) {
|
func TestSetValueNotSettable(t *testing.T) {
|
||||||
var i int
|
var i int
|
||||||
assert.NotNil(t, setValueFromString(reflect.Int, reflect.ValueOf(i), "1"))
|
assert.Error(t, setValueFromString(reflect.Int, reflect.ValueOf(i), "1"))
|
||||||
|
assert.Error(t, validateAndSetValue(reflect.Int, reflect.ValueOf(i), "1", nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestParseKeyAndOptionsErrors(t *testing.T) {
|
func TestParseKeyAndOptionsErrors(t *testing.T) {
|
||||||
@@ -300,3 +301,36 @@ func TestSetValueFormatErrors(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestValidateValueRange(t *testing.T) {
|
||||||
|
t.Run("float", func(t *testing.T) {
|
||||||
|
assert.NoError(t, validateValueRange(1.2, nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("float number range", func(t *testing.T) {
|
||||||
|
assert.NoError(t, validateNumberRange(1.2, nil))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad float", func(t *testing.T) {
|
||||||
|
assert.Error(t, validateValueRange("a", &fieldOptionsWithContext{
|
||||||
|
Range: &numberRange{},
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("bad float validate", func(t *testing.T) {
|
||||||
|
var v struct {
|
||||||
|
Foo float32
|
||||||
|
}
|
||||||
|
assert.Error(t, validateAndSetValue(reflect.Int, reflect.ValueOf(&v).Elem().Field(0),
|
||||||
|
"1", &fieldOptionsWithContext{
|
||||||
|
Range: &numberRange{
|
||||||
|
left: 2,
|
||||||
|
right: 3,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetMatchedPrimitiveValue(t *testing.T) {
|
||||||
|
assert.Error(t, setMatchedPrimitiveValue(reflect.Func, reflect.ValueOf(2), "1"))
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user