From 6b4d0d89c0d3d35cd2b0691e300c3870a4ab91a4 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Wed, 7 Jun 2023 00:46:43 +0800 Subject: [PATCH] chore: add more tests (#3324) --- core/mapping/unmarshaler.go | 7 +- core/mapping/unmarshaler_test.go | 216 ++++++++++++++++++++++--------- core/mapping/utils.go | 2 - core/mapping/utils_test.go | 36 +++++- 4 files changed, 199 insertions(+), 62 deletions(-) diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index 895f288f..d753d246 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -176,7 +176,12 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map switch dereffedBaseKind { case reflect.Struct: 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 } diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index 399b2b4e..72038e4e 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -557,6 +557,15 @@ func TestUnmarshalIntWithString(t *testing.T) { var in inner 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) { @@ -675,28 +684,54 @@ func TestUnmarshalFloatSliceWithDefault(t *testing.T) { } func TestUnmarshalStringSliceWithDefault(t *testing.T) { - type inner struct { - Strs []string `key:"strs,default=[foo,bar,woo]"` - Strps []*string `key:"strs,default=[foo,bar,woo]"` - Strpps []**string `key:"strs,default=[foo,bar,woo]"` - } + t.Run("slice with default", func(t *testing.T) { + type inner struct { + Strs []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 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 ss []string - for _, s := range in.Strps { - ss = append(ss, *s) + 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"}, ss) + }) - var sss []string - for _, s := range in.Strpps { - sss = append(sss, **s) + 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]"` } - assert.ElementsMatch(t, []string{"foo", "bar", "woo"}, sss) - } + + var in inner + assert.Error(t, UnmarshalKey(nil, &in)) + }) } func TestUnmarshalStringSliceWithDefaultHasSpaces(t *testing.T) { @@ -1327,25 +1362,71 @@ func TestUnmarshalStructOptionalDependsNotErrorDetails(t *testing.T) { } func TestUnmarshalStructOptionalDependsNotNested(t *testing.T) { - type address struct { - Optional string `key:",optional"` - OptionalDepends string `key:",optional=!Optional"` - } - type combo struct { - Name string `key:"name,optional"` - Address address `key:"address"` - } - type inner struct { - Name string `key:"name"` - Combo combo `key:"combo"` - } + t.Run("mutal optionals", func(t *testing.T) { + type address struct { + Optional string `key:",optional"` + OptionalDepends string `key:",optional=!Optional"` + } + 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", - } + m := map[string]any{ + "name": "kevin", + } - var in inner - assert.Error(t, UnmarshalKey(m, &in)) + var in inner + 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) { @@ -3855,20 +3936,37 @@ func TestUnmarshalValuer(t *testing.T) { } func TestUnmarshal_EnvString(t *testing.T) { - type Value struct { - Name string `key:"name,env=TEST_NAME_STRING"` - } + t.Run("valid env", func(t *testing.T) { + type Value struct { + Name string `key:"name,env=TEST_NAME_STRING"` + } - const ( - envName = "TEST_NAME_STRING" - envVal = "this is a name" - ) - t.Setenv(envName, envVal) + const ( + envName = "TEST_NAME_STRING" + envVal = "this is a name" + ) + t.Setenv(envName, envVal) - var v Value - if assert.NoError(t, UnmarshalKey(emptyMap, &v)) { - assert.Equal(t, envVal, v.Name) - } + var v Value + if assert.NoError(t, UnmarshalKey(emptyMap, &v)) { + 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) { @@ -4044,20 +4142,22 @@ func TestUnmarshal_EnvDurationBadValue(t *testing.T) { } func TestUnmarshal_EnvWithOptions(t *testing.T) { - type Value struct { - Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_MATCH,options=[abc,123,xyz]"` - } + t.Run("valid options", func(t *testing.T) { + type Value struct { + Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_MATCH,options=[abc,123,xyz]"` + } - const ( - envName = "TEST_NAME_ENV_OPTIONS_MATCH" - envVal = "123" - ) - t.Setenv(envName, envVal) + const ( + envName = "TEST_NAME_ENV_OPTIONS_MATCH" + envVal = "123" + ) + t.Setenv(envName, envVal) - var v Value - if assert.NoError(t, UnmarshalKey(emptyMap, &v)) { - assert.Equal(t, envVal, v.Name) - } + var v Value + if assert.NoError(t, UnmarshalKey(emptyMap, &v)) { + assert.Equal(t, envVal, v.Name) + } + }) } func TestUnmarshal_EnvWithOptionsWrongValueBool(t *testing.T) { diff --git a/core/mapping/utils.go b/core/mapping/utils.go index 91398033..931d3169 100644 --- a/core/mapping/utils.go +++ b/core/mapping/utils.go @@ -372,8 +372,6 @@ func parseOption(fieldOpts *fieldOptions, fieldName, option string) error { default: return fmt.Errorf("field %q has wrong optional", fieldName) } - case option == optionalOption: - fieldOpts.Optional = true case strings.HasPrefix(option, optionsOption): val, err := parseProperty(fieldName, optionsOption, option) if err != nil { diff --git a/core/mapping/utils_test.go b/core/mapping/utils_test.go index 84e0acf2..b8e7ea18 100644 --- a/core/mapping/utils_test.go +++ b/core/mapping/utils_test.go @@ -241,7 +241,8 @@ func TestValidatePtrWithZeroValue(t *testing.T) { func TestSetValueNotSettable(t *testing.T) { 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) { @@ -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")) +}