From 06fafd2153fa5de825e331b5425d2148e4a7a204 Mon Sep 17 00:00:00 2001 From: Kevin Wan Date: Fri, 18 Nov 2022 19:46:23 +0800 Subject: [PATCH] feat: validate value in options for mapping (#2616) --- core/mapping/unmarshaler.go | 4 + core/mapping/unmarshaler_test.go | 221 +++++++++++++++++++++---------- 2 files changed, 155 insertions(+), 70 deletions(-) diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go index c75bc50c..ee8c4d35 100644 --- a/core/mapping/unmarshaler.go +++ b/core/mapping/unmarshaler.go @@ -341,6 +341,10 @@ func (u *Unmarshaler) processFieldTextUnmarshaler(field reflect.StructField, val func (u *Unmarshaler) processFieldWithEnvValue(field reflect.StructField, value reflect.Value, envVal string, opts *fieldOptionsWithContext, fullName string) error { + if err := validateValueInOptions(envVal, opts.options()); err != nil { + return err + } + fieldKind := field.Type.Kind() switch fieldKind { case reflect.Bool: diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go index f806bfbb..51eb36c9 100644 --- a/core/mapping/unmarshaler_test.go +++ b/core/mapping/unmarshaler_test.go @@ -3254,91 +3254,85 @@ func TestUnmarshal_EnvDurationBadValue(t *testing.T) { assert.NotNil(t, UnmarshalKey(emptyMap, &v)) } -func BenchmarkUnmarshalString(b *testing.B) { - type inner struct { - Value string `key:"value"` - } - m := map[string]interface{}{ - "value": "first", +func TestUnmarshal_EnvWithOptions(t *testing.T) { + type Value struct { + Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_MATCH,options=[abc,123,xyz]"` } - for i := 0; i < b.N; i++ { - var in inner - if err := UnmarshalKey(m, &in); err != nil { - b.Fatal(err) - } - } -} + const ( + envName = "TEST_NAME_ENV_OPTIONS_MATCH" + envVal = "123" + ) + os.Setenv(envName, envVal) + defer os.Unsetenv(envName) -func BenchmarkUnmarshalStruct(b *testing.B) { - b.ReportAllocs() + var v Value + assert.NoError(t, UnmarshalKey(emptyMap, &v)) + assert.Equal(t, envVal, v.Name) +} - m := map[string]interface{}{ - "Ids": []map[string]interface{}{ - { - "First": 1, - "Second": 2, - }, - }, +func TestUnmarshal_EnvWithOptionsWrongValueBool(t *testing.T) { + type Value struct { + Enable bool `key:"enable,env=TEST_NAME_ENV_OPTIONS_BOOL,options=[true]"` } - for i := 0; i < b.N; i++ { - var v struct { - Ids []struct { - First int - Second int - } - } - if err := UnmarshalKey(m, &v); err != nil { - b.Fatal(err) - } - } + const ( + envName = "TEST_NAME_ENV_OPTIONS_BOOL" + envVal = "false" + ) + os.Setenv(envName, envVal) + defer os.Unsetenv(envName) + + var v Value + assert.Error(t, UnmarshalKey(emptyMap, &v)) } -func BenchmarkMapToStruct(b *testing.B) { - data := map[string]interface{}{ - "valid": "1", - "age": "5", - "name": "liao", - } - type anonymous struct { - Valid bool - Age int - Name string +func TestUnmarshal_EnvWithOptionsWrongValueDuration(t *testing.T) { + type Value struct { + Duration time.Duration `key:"duration,env=TEST_NAME_ENV_OPTIONS_DURATION,options=[1s,2s,3s]"` } - for i := 0; i < b.N; i++ { - var an anonymous - if valid, ok := data["valid"]; ok { - an.Valid = valid == "1" - } - if age, ok := data["age"]; ok { - ages, _ := age.(string) - an.Age, _ = strconv.Atoi(ages) - } - if name, ok := data["name"]; ok { - names, _ := name.(string) - an.Name = names - } - } + const ( + envName = "TEST_NAME_ENV_OPTIONS_DURATION" + envVal = "4s" + ) + os.Setenv(envName, envVal) + defer os.Unsetenv(envName) + + var v Value + assert.Error(t, UnmarshalKey(emptyMap, &v)) } -func BenchmarkUnmarshal(b *testing.B) { - data := map[string]interface{}{ - "valid": "1", - "age": "5", - "name": "liao", - } - type anonymous struct { - Valid bool `key:"valid,string"` - Age int `key:"age,string"` - Name string `key:"name"` +func TestUnmarshal_EnvWithOptionsWrongValueNumber(t *testing.T) { + type Value struct { + Age int `key:"age,env=TEST_NAME_ENV_OPTIONS_AGE,options=[18,19,20]"` } - for i := 0; i < b.N; i++ { - var an anonymous - UnmarshalKey(data, &an) + const ( + envName = "TEST_NAME_ENV_OPTIONS_AGE" + envVal = "30" + ) + os.Setenv(envName, envVal) + defer os.Unsetenv(envName) + + var v Value + assert.Error(t, UnmarshalKey(emptyMap, &v)) +} + +func TestUnmarshal_EnvWithOptionsWrongValueString(t *testing.T) { + type Value struct { + Name string `key:"name,env=TEST_NAME_ENV_OPTIONS_STRING,options=[abc,123,xyz]"` } + + const ( + envName = "TEST_NAME_ENV_OPTIONS_STRING" + envVal = "this is a name" + ) + os.Setenv(envName, envVal) + defer os.Unsetenv(envName) + + var v Value + assert.Error(t, UnmarshalKey(emptyMap, &v)) } func TestUnmarshalJsonReaderMultiArray(t *testing.T) { @@ -3581,3 +3575,90 @@ func BenchmarkDefaultValue(b *testing.B) { } } } + +func BenchmarkUnmarshalString(b *testing.B) { + type inner struct { + Value string `key:"value"` + } + m := map[string]interface{}{ + "value": "first", + } + + for i := 0; i < b.N; i++ { + var in inner + if err := UnmarshalKey(m, &in); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkUnmarshalStruct(b *testing.B) { + b.ReportAllocs() + + m := map[string]interface{}{ + "Ids": []map[string]interface{}{ + { + "First": 1, + "Second": 2, + }, + }, + } + + for i := 0; i < b.N; i++ { + var v struct { + Ids []struct { + First int + Second int + } + } + if err := UnmarshalKey(m, &v); err != nil { + b.Fatal(err) + } + } +} + +func BenchmarkMapToStruct(b *testing.B) { + data := map[string]interface{}{ + "valid": "1", + "age": "5", + "name": "liao", + } + type anonymous struct { + Valid bool + Age int + Name string + } + + for i := 0; i < b.N; i++ { + var an anonymous + if valid, ok := data["valid"]; ok { + an.Valid = valid == "1" + } + if age, ok := data["age"]; ok { + ages, _ := age.(string) + an.Age, _ = strconv.Atoi(ages) + } + if name, ok := data["name"]; ok { + names, _ := name.(string) + an.Name = names + } + } +} + +func BenchmarkUnmarshal(b *testing.B) { + data := map[string]interface{}{ + "valid": "1", + "age": "5", + "name": "liao", + } + type anonymous struct { + Valid bool `key:"valid,string"` + Age int `key:"age,string"` + Name string `key:"name"` + } + + for i := 0; i < b.N; i++ { + var an anonymous + UnmarshalKey(data, &an) + } +}