Repository: zeromicro/go-zero
Pull request #3282 (2023-05-25 16:09:46)

chore: add more tests

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/zeromicro/go-zero/pull/3282?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#3282](https://app.codecov.io/gh/zeromicro/go-zero/pull/3282?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (705cbaf) into [master](https://app.codecov.io/gh/zeromicro/go-zero/commit/4a2a8d9e453b673712dbc2449a42b522b5eff4f6?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (4a2a8d9) will **increase** coverage by `0.26%`.
> The diff coverage is `83.33%`.


| [Impacted Files](https://app.codecov.io/gh/zeromicro/go-zero/pull/3282?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [core/fs/temps.go](https://app.codecov.io/gh/zeromicro/go-zero/pull/3282?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y29yZS9mcy90ZW1wcy5nbw==) | `36.84% <66.66%> (ø)` | |
| [core/mapping/unmarshaler.go](https://app.codecov.io/gh/zeromicro/go-zero/pull/3282?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y29yZS9tYXBwaW5nL3VubWFyc2hhbGVyLmdv) | `96.31% <100.00%> (+5.95%)` | :arrow_up: |

... and [2 files with indirect coverage changes](https://app.codecov.io/gh/zeromicro/go-zero/pull/3282/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


Patch:
diff --git a/core/mapping/unmarshaler.go b/core/mapping/unmarshaler.go
index 1f5da75b..5d315085 100644
--- a/core/mapping/unmarshaler.go
+++ b/core/mapping/unmarshaler.go
@@ -148,14 +148,17 @@ func (u *Unmarshaler) fillSlice(fieldType reflect.Type, value reflect.Value, map
 		return errValueNotSettable
 	}
 
-	baseType := fieldType.Elem()
-	dereffedBaseType := Deref(baseType)
-	dereffedBaseKind := dereffedBaseType.Kind()
 	refValue := reflect.ValueOf(mapValue)
+	if refValue.Kind() != reflect.Slice {
+		return errTypeMismatch
+	}
 	if refValue.IsNil() {
 		return nil
 	}
 
+	baseType := fieldType.Elem()
+	dereffedBaseType := Deref(baseType)
+	dereffedBaseKind := dereffedBaseType.Kind()
 	conv := reflect.MakeSlice(reflect.SliceOf(baseType), refValue.Len(), refValue.Cap())
 	if refValue.Len() == 0 {
 		value.Set(conv)


Test patch:
diff --git a/core/mapping/unmarshaler_test.go b/core/mapping/unmarshaler_test.go
index ed21f32f..425dc80c 100644
--- a/core/mapping/unmarshaler_test.go
+++ b/core/mapping/unmarshaler_test.go
@@ -306,30 +306,61 @@ func TestUnmarshalIntPtr(t *testing.T) {
 }
 
 func TestUnmarshalIntSliceOfPtr(t *testing.T) {
-	type inner struct {
-		Ints  []*int  `key:"ints"`
-		Intps []**int `key:"intps"`
-	}
-	m := map[string]any{
-		"ints":  []int{1, 2, 3},
-		"intps": []int{1, 2, 3, 4},
-	}
+	t.Run("int slice", func(t *testing.T) {
+		type inner struct {
+			Ints  []*int  `key:"ints"`
+			Intps []**int `key:"intps"`
+		}
+		m := map[string]any{
+			"ints":  []int{1, 2, 3},
+			"intps": []int{1, 2, 3, 4},
+		}
 
-	var in inner
-	if assert.NoError(t, UnmarshalKey(m, &in)) {
-		assert.NotEmpty(t, in.Ints)
-		var ints []int
-		for _, i := range in.Ints {
-			ints = append(ints, *i)
+		var in inner
+		if assert.NoError(t, UnmarshalKey(m, &in)) {
+			assert.NotEmpty(t, in.Ints)
+			var ints []int
+			for _, i := range in.Ints {
+				ints = append(ints, *i)
+			}
+			assert.EqualValues(t, []int{1, 2, 3}, ints)
+
+			var intps []int
+			for _, i := range in.Intps {
+				intps = append(intps, **i)
+			}
+			assert.EqualValues(t, []int{1, 2, 3, 4}, intps)
 		}
-		assert.EqualValues(t, []int{1, 2, 3}, ints)
+	})
 
-		var intps []int
-		for _, i := range in.Intps {
-			intps = append(intps, **i)
+	t.Run("int slice with error", func(t *testing.T) {
+		type inner struct {
+			Ints  []*int  `key:"ints"`
+			Intps []**int `key:"intps"`
 		}
-		assert.EqualValues(t, []int{1, 2, 3, 4}, intps)
-	}
+		m := map[string]any{
+			"ints":  []any{1, 2, "a"},
+			"intps": []int{1, 2, 3, 4},
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
+
+	t.Run("int slice with nil", func(t *testing.T) {
+		type inner struct {
+			Ints []int `key:"ints"`
+		}
+
+		m := map[string]any{
+			"ints": []any{nil},
+		}
+
+		var in inner
+		if assert.NoError(t, UnmarshalKey(m, &in)) {
+			assert.Empty(t, in.Ints)
+		}
+	})
 }
 
 func TestUnmarshalIntWithDefault(t *testing.T) {
@@ -373,6 +404,42 @@ func TestUnmarshalIntWithString(t *testing.T) {
 		}
 	})
 
+	t.Run("int wrong range", func(t *testing.T) {
+		type inner struct {
+			Int   int64   `key:"int,string,range=[2:3]"`
+			Intp  *int64  `key:"intp,range=[2:3]"`
+			Intpp **int64 `key:"intpp,range=[2:3]"`
+		}
+		m := map[string]any{
+			"int":   json.Number("1"),
+			"intp":  json.Number("2"),
+			"intpp": json.Number("3"),
+		}
+
+		var in inner
+		assert.ErrorIs(t, UnmarshalKey(m, &in), errNumberRange)
+	})
+
+	t.Run("int with wrong type", func(t *testing.T) {
+		type (
+			myString string
+
+			inner struct {
+				Int   int64   `key:"int,string"`
+				Intp  *int64  `key:"intp,string"`
+				Intpp **int64 `key:"intpp,string"`
+			}
+		)
+		m := map[string]any{
+			"int":   myString("1"),
+			"intp":  myString("2"),
+			"intpp": myString("3"),
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
+
 	t.Run("int with ptr", func(t *testing.T) {
 		type inner struct {
 			Int *int64 `key:"int"`
@@ -387,6 +454,54 @@ func TestUnmarshalIntWithString(t *testing.T) {
 		}
 	})
 
+	t.Run("int with invalid value", func(t *testing.T) {
+		type inner struct {
+			Int int64 `key:"int"`
+		}
+		m := map[string]any{
+			"int": json.Number("a"),
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
+
+	t.Run("uint with invalid value", func(t *testing.T) {
+		type inner struct {
+			Int uint64 `key:"int"`
+		}
+		m := map[string]any{
+			"int": json.Number("a"),
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
+
+	t.Run("float with invalid value", func(t *testing.T) {
+		type inner struct {
+			Value float64 `key:"float"`
+		}
+		m := map[string]any{
+			"float": json.Number("a"),
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
+
+	t.Run("float with invalid value", func(t *testing.T) {
+		type inner struct {
+			Value string `key:"value"`
+		}
+		m := map[string]any{
+			"value": json.Number("a"),
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
+
 	t.Run("int with ptr of ptr", func(t *testing.T) {
 		type inner struct {
 			Int **int64 `key:"int"`
@@ -491,14 +606,25 @@ func TestUnmarshalBoolSliceEmpty(t *testing.T) {
 }
 
 func TestUnmarshalBoolSliceWithDefault(t *testing.T) {
-	type inner struct {
-		Bools []bool `key:"bools,default=[true,false]"`
-	}
+	t.Run("slice with default", func(t *testing.T) {
+		type inner struct {
+			Bools []bool `key:"bools,default=[true,false]"`
+		}
 
-	var in inner
-	if assert.NoError(t, UnmarshalKey(nil, &in)) {
-		assert.ElementsMatch(t, []bool{true, false}, in.Bools)
-	}
+		var in inner
+		if assert.NoError(t, UnmarshalKey(nil, &in)) {
+			assert.ElementsMatch(t, []bool{true, false}, in.Bools)
+		}
+	})
+
+	t.Run("slice with default error", func(t *testing.T) {
+		type inner struct {
+			Bools []bool `key:"bools,default=[true,fal]"`
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(nil, &in))
+	})
 }
 
 func TestUnmarshalIntSliceWithDefault(t *testing.T) {
@@ -726,19 +852,45 @@ func TestUnmarshalStringWithMissing(t *testing.T) {
 }
 
 func TestUnmarshalStringSliceFromString(t *testing.T) {
-	var v struct {
-		Names []string `key:"names"`
-	}
-	m := map[string]any{
-		"names": `["first", "second"]`,
-	}
+	t.Run("slice from string", func(t *testing.T) {
+		var v struct {
+			Names []string `key:"names"`
+		}
+		m := map[string]any{
+			"names": `["first", "second"]`,
+		}
 
-	ast := assert.New(t)
-	if ast.NoError(UnmarshalKey(m, &v)) {
-		ast.Equal(2, len(v.Names))
-		ast.Equal("first", v.Names[0])
-		ast.Equal("second", v.Names[1])
-	}
+		ast := assert.New(t)
+		if ast.NoError(UnmarshalKey(m, &v)) {
+			ast.Equal(2, len(v.Names))
+			ast.Equal("first", v.Names[0])
+			ast.Equal("second", v.Names[1])
+		}
+	})
+
+	t.Run("slice from string with slice error", func(t *testing.T) {
+		var v struct {
+			Names []int `key:"names"`
+		}
+		m := map[string]any{
+			"names": `["first", 1]`,
+		}
+
+		assert.Error(t, UnmarshalKey(m, &v))
+	})
+
+	t.Run("slice from string with error", func(t *testing.T) {
+		type myString string
+
+		var v struct {
+			Names []string `key:"names"`
+		}
+		m := map[string]any{
+			"names": myString("not a slice"),
+		}
+
+		assert.Error(t, UnmarshalKey(m, &v))
+	})
 }
 
 func TestUnmarshalIntSliceFromString(t *testing.T) {
@@ -800,19 +952,32 @@ func (c CustomStringer) String() string {
 }
 
 func TestUnmarshalStringMapFromStringer(t *testing.T) {
-	var v struct {
-		Sort map[string]string `key:"sort"`
-	}
-	m := map[string]any{
-		"sort": CustomStringer(`"value":"ascend","emptyStr":""`),
-	}
+	t.Run("CustomStringer", func(t *testing.T) {
+		var v struct {
+			Sort map[string]string `key:"sort"`
+		}
+		m := map[string]any{
+			"sort": CustomStringer(`"value":"ascend","emptyStr":""`),
+		}
 
-	ast := assert.New(t)
-	if ast.NoError(UnmarshalKey(m, &v)) {
-		ast.Equal(2, len(v.Sort))
-		ast.Equal("ascend", v.Sort["value"])
-		ast.Equal("", v.Sort["emptyStr"])
-	}
+		ast := assert.New(t)
+		if ast.NoError(UnmarshalKey(m, &v)) {
+			ast.Equal(2, len(v.Sort))
+			ast.Equal("ascend", v.Sort["value"])
+			ast.Equal("", v.Sort["emptyStr"])
+		}
+	})
+
+	t.Run("CustomStringer incorrect", func(t *testing.T) {
+		var v struct {
+			Sort map[string]string `key:"sort"`
+		}
+		m := map[string]any{
+			"sort": CustomStringer(`"value"`),
+		}
+
+		assert.Error(t, UnmarshalKey(m, &v))
+	})
 }
 
 func TestUnmarshalStringMapFromUnsupportedType(t *testing.T) {
@@ -906,48 +1071,83 @@ func TestUnmarshalStringSliceMapFromString(t *testing.T) {
 }
 
 func TestUnmarshalStruct(t *testing.T) {
-	type address struct {
-		City          string `key:"city"`
-		ZipCode       int    `key:"zipcode,string"`
-		DefaultString string `key:"defaultstring,default=hello"`
-		Optional      string `key:",optional"`
-	}
-	type inner struct {
-		Name      string    `key:"name"`
-		Address   address   `key:"address"`
-		AddressP  *address  `key:"addressp"`
-		AddressPP **address `key:"addresspp"`
-	}
-	m := map[string]any{
-		"name": "kevin",
-		"address": map[string]any{
-			"city":    "shanghai",
-			"zipcode": "200000",
-		},
-		"addressp": map[string]any{
-			"city":    "beijing",
-			"zipcode": "300000",
-		},
-		"addresspp": map[string]any{
-			"city":    "guangzhou",
-			"zipcode": "400000",
-		},
-	}
+	t.Run("struct", func(t *testing.T) {
+		type address struct {
+			City          string `key:"city"`
+			ZipCode       int    `key:"zipcode,string"`
+			DefaultString string `key:"defaultstring,default=hello"`
+			Optional      string `key:",optional"`
+		}
+		type inner struct {
+			Name      string    `key:"name"`
+			Address   address   `key:"address"`
+			AddressP  *address  `key:"addressp"`
+			AddressPP **address `key:"addresspp"`
+		}
+		m := map[string]any{
+			"name": "kevin",
+			"address": map[string]any{
+				"city":    "shanghai",
+				"zipcode": "200000",
+			},
+			"addressp": map[string]any{
+				"city":    "beijing",
+				"zipcode": "300000",
+			},
+			"addresspp": map[string]any{
+				"city":    "guangzhou",
+				"zipcode": "400000",
+			},
+		}
 
-	var in inner
-	ast := assert.New(t)
-	if ast.NoError(UnmarshalKey(m, &in)) {
-		ast.Equal("kevin", in.Name)
-		ast.Equal("shanghai", in.Address.City)
-		ast.Equal(200000, in.Address.ZipCode)
-		ast.Equal("hello", in.AddressP.DefaultString)
-		ast.Equal("beijing", in.AddressP.City)
-		ast.Equal(300000, in.AddressP.ZipCode)
-		ast.Equal("hello", in.AddressP.DefaultString)
-		ast.Equal("guangzhou", (*in.AddressPP).City)
-		ast.Equal(400000, (*in.AddressPP).ZipCode)
-		ast.Equal("hello", (*in.AddressPP).DefaultString)
-	}
+		var in inner
+		ast := assert.New(t)
+		if ast.NoError(UnmarshalKey(m, &in)) {
+			ast.Equal("kevin", in.Name)
+			ast.Equal("shanghai", in.Address.City)
+			ast.Equal(200000, in.Address.ZipCode)
+			ast.Equal("hello", in.AddressP.DefaultString)
+			ast.Equal("beijing", in.AddressP.City)
+			ast.Equal(300000, in.AddressP.ZipCode)
+			ast.Equal("hello", in.AddressP.DefaultString)
+			ast.Equal("guangzhou", (*in.AddressPP).City)
+			ast.Equal(400000, (*in.AddressPP).ZipCode)
+			ast.Equal("hello", (*in.AddressPP).DefaultString)
+		}
+	})
+
+	t.Run("struct with error", func(t *testing.T) {
+		type address struct {
+			City          string `key:"city"`
+			ZipCode       int    `key:"zipcode,string"`
+			DefaultString string `key:"defaultstring,default=hello"`
+			Optional      string `key:",optional"`
+		}
+		type inner struct {
+			Name      string    `key:"name"`
+			Address   address   `key:"address"`
+			AddressP  *address  `key:"addressp"`
+			AddressPP **address `key:"addresspp"`
+		}
+		m := map[string]any{
+			"name": "kevin",
+			"address": map[string]any{
+				"city":    "shanghai",
+				"zipcode": "200000",
+			},
+			"addressp": map[string]any{
+				"city":    "beijing",
+				"zipcode": "300000",
+			},
+			"addresspp": map[string]any{
+				"city":    "guangzhou",
+				"zipcode": "a",
+			},
+		}
+
+		var in inner
+		assert.Error(t, UnmarshalKey(m, &in))
+	})
 }
 
 func TestUnmarshalStructOptionalDepends(t *testing.T) {
@@ -1086,21 +1286,44 @@ func TestUnmarshalStructOptionalDependsNot(t *testing.T) {
 }
 
 func TestUnmarshalStructOptionalDependsNotErrorDetails(t *testing.T) {
-	type address struct {
-		Optional        string `key:",optional"`
-		OptionalDepends string `key:",optional=!Optional"`
-	}
-	type inner struct {
-		Name    string  `key:"name"`
-		Address address `key:"address"`
-	}
+	t.Run("mutal optionals", func(t *testing.T) {
+		type address struct {
+			Optional        string `key:",optional"`
+			OptionalDepends string `key:",optional=!Optional"`
+		}
+		type inner struct {
+			Name    string  `key:"name"`
+			Address address `key:"address"`
+		}
 
-	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("with default", func(t *testing.T) {
+		type address struct {
+			Optional        string `key:",optional"`
+			OptionalDepends string `key:",default=value,optional"`
+		}
+		type inner struct {
+			Name    string  `key:"name"`
+			Address address `key:"address"`
+		}
+
+		m := map[string]any{
+			"name": "kevin",
+		}
+
+		var in inner
+		if assert.NoError(t, UnmarshalKey(m, &in)) {
+			assert.Equal(t, "kevin", in.Name)
+			assert.Equal(t, "value", in.Address.OptionalDepends)
+		}
+	})
 }
 
 func TestUnmarshalStructOptionalDependsNotNested(t *testing.T) {
@@ -1440,16 +1663,48 @@ func TestUnmarshalMapOfInt(t *testing.T) {
 	}
 }
 
-func TestUnmarshalMapOfStructError(t *testing.T) {
-	m := map[string]any{
-		"Ids": map[string]any{"first": "second"},
-	}
-	var v struct {
-		Ids map[string]struct {
-			Name string
+func TestUnmarshalMapOfStruct(t *testing.T) {
+	t.Run("map of struct with error", func(t *testing.T) {
+		m := map[string]any{
+			"Ids": map[string]any{"first": "second"},
 		}
-	}
-	assert.Error(t, UnmarshalKey(m, &v))
+		var v struct {
+			Ids map[string]struct {
+				Name string
+			}
+		}
+		assert.Error(t, UnmarshalKey(m, &v))
+	})
+
+	t.Run("map of struct", func(t *testing.T) {
+		m := map[string]any{
+			"Ids": map[string]any{
+				"foo": map[string]any{"Name": "foo"},
+			},
+		}
+		var v struct {
+			Ids map[string]struct {
+				Name string
+			}
+		}
+		if assert.NoError(t, UnmarshalKey(m, &v)) {
+			assert.Equal(t, "foo", v.Ids["foo"].Name)
+		}
+	})
+
+	t.Run("map of struct error", func(t *testing.T) {
+		m := map[string]any{
+			"Ids": map[string]any{
+				"foo": map[string]any{"name": "foo"},
+			},
+		}
+		var v struct {
+			Ids map[string]struct {
+				Name string
+			}
+		}
+		assert.Error(t, UnmarshalKey(m, &v))
+	})
 }
 
 func TestUnmarshalSlice(t *testing.T) {
@@ -1492,26 +1747,46 @@ func TestUnmarshalSlice(t *testing.T) {
 }
 
 func TestUnmarshalSliceOfStruct(t *testing.T) {
-	m := map[string]any{
-		"Ids": []map[string]any{
-			{
-				"First":  1,
-				"Second": 2,
+	t.Run("slice of struct", func(t *testing.T) {
+		m := map[string]any{
+			"Ids": []map[string]any{
+				{
+					"First":  1,
+					"Second": 2,
+				},
 			},
-		},
-	}
-	var v struct {
-		Ids []struct {
-			First  int
-			Second int
 		}
-	}
-	ast := assert.New(t)
-	if ast.NoError(UnmarshalKey(m, &v)) {
-		ast.Equal(1, len(v.Ids))
-		ast.Equal(1, v.Ids[0].First)
-		ast.Equal(2, v.Ids[0].Second)
-	}
+		var v struct {
+			Ids []struct {
+				First  int
+				Second int
+			}
+		}
+		ast := assert.New(t)
+		if ast.NoError(UnmarshalKey(m, &v)) {
+			ast.Equal(1, len(v.Ids))
+			ast.Equal(1, v.Ids[0].First)
+			ast.Equal(2, v.Ids[0].Second)
+		}
+	})
+
+	t.Run("slice of struct", func(t *testing.T) {
+		m := map[string]any{
+			"Ids": []map[string]any{
+				{
+					"First":  "a",
+					"Second": 2,
+				},
+			},
+		}
+		var v struct {
+			Ids []struct {
+				First  int
+				Second int
+			}
+		}
+		assert.Error(t, UnmarshalKey(m, &v))
+	})
 }
 
 func TestUnmarshalWithStringOptionsCorrect(t *testing.T) {
@@ -3122,21 +3397,57 @@ func TestUnmarshalRangeError(t *testing.T) {
 }
 
 func TestUnmarshalNestedMap(t *testing.T) {
-	var c struct {
-		Anything map[string]map[string]string `json:"anything"`
-	}
-	m := map[string]any{
-		"anything": map[string]map[string]any{
-			"inner": {
-				"id":   "1",
-				"name": "any",
+	t.Run("nested map", func(t *testing.T) {
+		var c struct {
+			Anything map[string]map[string]string `json:"anything"`
+		}
+		m := map[string]any{
+			"anything": map[string]map[string]any{
+				"inner": {
+					"id":   "1",
+					"name": "any",
+				},
 			},
-		},
-	}
+		}
 
-	if assert.NoError(t, NewUnmarshaler("json").Unmarshal(m, &c)) {
-		assert.Equal(t, "1", c.Anything["inner"]["id"])
-	}
+		if assert.NoError(t, NewUnmarshaler("json").Unmarshal(m, &c)) {
+			assert.Equal(t, "1", c.Anything["inner"]["id"])
+		}
+	})
+
+	t.Run("nested map with slice element", func(t *testing.T) {
+		var c struct {
+			Anything map[string][]string `json:"anything"`
+		}
+		m := map[string]any{
+			"anything": map[string][]any{
+				"inner": {
+					"id",
+					"name",
+				},
+			},
+		}
+
+		if assert.NoError(t, NewUnmarshaler("json").Unmarshal(m, &c)) {
+			assert.Equal(t, []string{"id", "name"}, c.Anything["inner"])
+		}
+	})
+
+	t.Run("nested map with slice element error", func(t *testing.T) {
+		var c struct {
+			Anything map[string][]string `json:"anything"`
+		}
+		m := map[string]any{
+			"anything": map[string][]any{
+				"inner": {
+					"id",
+					1,
+				},
+			},
+		}
+
+		assert.Error(t, NewUnmarshaler("json").Unmarshal(m, &c))
+	})
 }
 
 func TestUnmarshalNestedMapMismatch(t *testing.T) {
@@ -3810,15 +4121,27 @@ func TestUnmarshal_EnvWithOptionsWrongValueString(t *testing.T) {
 }
 
 func TestUnmarshalJsonReaderMultiArray(t *testing.T) {
-	var res struct {
-		A string     `json:"a"`
-		B [][]string `json:"b"`
-	}
-	payload := `{"a": "133", "b": [["add", "cccd"], ["eeee"]]}`
-	reader := strings.NewReader(payload)
-	if assert.NoError(t, UnmarshalJsonReader(reader, &res)) {
-		assert.Equal(t, 2, len(res.B))
-	}
+	t.Run("reader multi array", func(t *testing.T) {
+		var res struct {
+			A string     `json:"a"`
+			B [][]string `json:"b"`
+		}
+		payload := `{"a": "133", "b": [["add", "cccd"], ["eeee"]]}`
+		reader := strings.NewReader(payload)
+		if assert.NoError(t, UnmarshalJsonReader(reader, &res)) {
+			assert.Equal(t, 2, len(res.B))
+		}
+	})
+
+	t.Run("reader multi array with error", func(t *testing.T) {
+		var res struct {
+			A string     `json:"a"`
+			B [][]string `json:"b"`
+		}
+		payload := `{"a": "133", "b": ["eeee"]}`
+		reader := strings.NewReader(payload)
+		assert.Error(t, UnmarshalJsonReader(reader, &res))
+	})
 }
 
 func TestUnmarshalJsonReaderPtrMultiArrayString(t *testing.T) {
@@ -4059,12 +4382,24 @@ func TestUnmarshalJsonReaderWithTypeMismatchBool(t *testing.T) {
 	assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(strings.NewReader(body), &req))
 }
 
-func TestUnmarshalJsonReaderWithTypeMismatchString(t *testing.T) {
-	var req struct {
-		Params map[string]string `json:"params"`
-	}
-	body := `{"params":{"a":{"a":123}}}`
-	assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(strings.NewReader(body), &req))
+func TestUnmarshalJsonReaderWithTypeString(t *testing.T) {
+	t.Run("string type", func(t *testing.T) {
+		var req struct {
+			Params map[string]string `json:"params"`
+		}
+		body := `{"params":{"a":"b"}}`
+		if assert.NoError(t, UnmarshalJsonReader(strings.NewReader(body), &req)) {
+			assert.Equal(t, "b", req.Params["a"])
+		}
+	})
+
+	t.Run("string type mismatch", func(t *testing.T) {
+		var req struct {
+			Params map[string]string `json:"params"`
+		}
+		body := `{"params":{"a":{"a":123}}}`
+		assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(strings.NewReader(body), &req))
+	})
 }
 
 func TestUnmarshalJsonReaderWithMismatchType(t *testing.T) {
@@ -4077,43 +4412,95 @@ func TestUnmarshalJsonReaderWithMismatchType(t *testing.T) {
 	assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(strings.NewReader(body), &req))
 }
 
-func TestUnmarshalJsonReaderWithMismatchTypeBool(t *testing.T) {
-	type Req struct {
-		Params map[string]bool `json:"params"`
-	}
+func TestUnmarshalJsonReaderWithTypeBool(t *testing.T) {
+	t.Run("bool type", func(t *testing.T) {
+		type Req struct {
+			Params map[string]bool `json:"params"`
+		}
 
-	tests := []struct {
-		name  string
-		input string
-	}{
-		{
-			name:  "int",
-			input: `{"params":{"a":123}}`,
-		},
-		{
-			name:  "int",
-			input: `{"params":{"a":"123"}}`,
-		},
-	}
+		tests := []struct {
+			name   string
+			input  string
+			expect bool
+		}{
+			{
+				name:   "int",
+				input:  `{"params":{"a":1}}`,
+				expect: true,
+			},
+			{
+				name:   "int",
+				input:  `{"params":{"a":0}}`,
+				expect: false,
+			},
+		}
 
-	for _, test := range tests {
-		test := test
-		t.Run(test.name, func(t *testing.T) {
-			var req Req
-			assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(strings.NewReader(test.input), &req))
-		})
-	}
+		for _, test := range tests {
+			test := test
+			t.Run(test.name, func(t *testing.T) {
+				var req Req
+				if assert.NoError(t, UnmarshalJsonReader(strings.NewReader(test.input), &req)) {
+					assert.Equal(t, test.expect, req.Params["a"])
+				}
+			})
+		}
+	})
+
+	t.Run("bool type mismatch", func(t *testing.T) {
+		type Req struct {
+			Params map[string]bool `json:"params"`
+		}
+
+		tests := []struct {
+			name  string
+			input string
+		}{
+			{
+				name:  "int",
+				input: `{"params":{"a":123}}`,
+			},
+			{
+				name:  "int",
+				input: `{"params":{"a":"123"}}`,
+			},
+		}
+
+		for _, test := range tests {
+			test := test
+			t.Run(test.name, func(t *testing.T) {
+				var req Req
+				assert.Equal(t, errTypeMismatch, UnmarshalJsonReader(strings.NewReader(test.input), &req))
+			})
+		}
+	})
 }
 
-func TestUnmarshalJsonReaderWithMismatchTypeBoolMap(t *testing.T) {
-	var req struct {
-		Params map[string]string `json:"params"`
-	}
-	assert.Equal(t, errTypeMismatch, UnmarshalJsonMap(map[string]any{
-		"params": map[string]any{
-			"a": true,
-		},
-	}, &req))
+func TestUnmarshalJsonReaderWithTypeBoolMap(t *testing.T) {
+	t.Run("bool map", func(t *testing.T) {
+		var req struct {
+			Params map[string]bool `json:"params"`
+		}
+		if assert.NoError(t, UnmarshalJsonMap(map[string]any{
+			"params": map[string]any{
+				"a": true,
+			},
+		}, &req)) {
+			assert.Equal(t, map[string]bool{
+				"a": true,
+			}, req.Params)
+		}
+	})
+
+	t.Run("bool map with error", func(t *testing.T) {
+		var req struct {
+			Params map[string]string `json:"params"`
+		}
+		assert.Equal(t, errTypeMismatch, UnmarshalJsonMap(map[string]any{
+			"params": map[string]any{
+				"a": true,
+			},
+		}, &req))
+	})
 }
 
 func TestUnmarshalJsonBytesSliceOfMaps(t *testing.T) {
@@ -4446,6 +4833,55 @@ func Test_UnmarshalMap(t *testing.T) {
 		var customer Customer
 		assert.ErrorIs(t, UnmarshalKey(input, &customer), errTypeMismatch)
 	})
+
+	t.Run("map from string", func(t *testing.T) {
+		type Customer struct {
+			Names map[string]string `key:"names,string"`
+		}
+
+		input := map[string]any{
+			"names": `{"name": "Tom"}`,
+		}
+
+		var customer Customer
+		assert.NoError(t, UnmarshalKey(input, &customer))
+		assert.Equal(t, "Tom", customer.Names["name"])
+	})
+
+	t.Run("map from string with error", func(t *testing.T) {
+		type Customer struct {
+			Names map[string]any `key:"names,string"`
+		}
+
+		input := map[string]any{
+			"names": `"name"`,
+		}
+
+		var customer Customer
+		assert.Error(t, UnmarshalKey(input, &customer))
+	})
+}
+
+// func TestUnmarshalWithFillPrimitives(t *testing.T) {
+// 	t.Run("fill primitives", func(t *testing.T) {
+// 		type St struct {
+// 			A int `key:"a,string,range=[5,10]"`
+// 		}
+// 		var st St
+// 		err := UnmarshalKey(map[string]any{
+// 			"a": "1",
+// 		}, &st)
+// 		assert.ErrorIs(t, err, errNumberRange)
+// 	})
+// }
+
+func TestUnmarshaler_Unmarshal(t *testing.T) {
+	t.Run("not struct", func(t *testing.T) {
+		var i int
+		unmarshaler := NewUnmarshaler(jsonTagKey)
+		err := unmarshaler.UnmarshalValuer(nil, &i)
+		assert.Error(t, err)
+	})
 }
 
 func TestGetValueWithChainedKeys(t *testing.T) {