mirror of https://github.com/hashicorp/consul
208 lines
4.4 KiB
Go
208 lines
4.4 KiB
Go
|
package decode
|
||
|
|
||
|
import (
|
||
|
"reflect"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/mitchellh/mapstructure"
|
||
|
"github.com/stretchr/testify/require"
|
||
|
)
|
||
|
|
||
|
func TestHookTranslateKeys(t *testing.T) {
|
||
|
var testcases = []struct {
|
||
|
name string
|
||
|
data interface{}
|
||
|
expected interface{}
|
||
|
}{
|
||
|
{
|
||
|
name: "target of type struct, with struct receiver",
|
||
|
data: map[string]interface{}{
|
||
|
"S": map[string]interface{}{
|
||
|
"None": "no translation",
|
||
|
"OldOne": "value1",
|
||
|
"oldtwo": "value2",
|
||
|
},
|
||
|
},
|
||
|
expected: Config{
|
||
|
S: TypeStruct{
|
||
|
One: "value1",
|
||
|
Two: "value2",
|
||
|
None: "no translation",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "target of type ptr, with struct receiver",
|
||
|
data: map[string]interface{}{
|
||
|
"PS": map[string]interface{}{
|
||
|
"None": "no translation",
|
||
|
"OldOne": "value1",
|
||
|
"oldtwo": "value2",
|
||
|
},
|
||
|
},
|
||
|
expected: Config{
|
||
|
PS: &TypeStruct{
|
||
|
One: "value1",
|
||
|
Two: "value2",
|
||
|
None: "no translation",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "target of type ptr, with ptr receiver",
|
||
|
data: map[string]interface{}{
|
||
|
"PTR": map[string]interface{}{
|
||
|
"None": "no translation",
|
||
|
"old_THREE": "value3",
|
||
|
"oldfour": "value4",
|
||
|
},
|
||
|
},
|
||
|
expected: Config{
|
||
|
PTR: &TypePtrToStruct{
|
||
|
Three: "value3",
|
||
|
Four: "value4",
|
||
|
None: "no translation",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "target of type ptr, with struct receiver",
|
||
|
data: map[string]interface{}{
|
||
|
"PTRS": map[string]interface{}{
|
||
|
"None": "no translation",
|
||
|
"old_THREE": "value3",
|
||
|
"old_four": "value4",
|
||
|
},
|
||
|
},
|
||
|
expected: Config{
|
||
|
PTRS: TypePtrToStruct{
|
||
|
Three: "value3",
|
||
|
Four: "value4",
|
||
|
None: "no translation",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "target of type map",
|
||
|
data: map[string]interface{}{
|
||
|
"Blob": map[string]interface{}{
|
||
|
"one": 1,
|
||
|
"two": 2,
|
||
|
},
|
||
|
},
|
||
|
expected: Config{
|
||
|
Blob: map[string]interface{}{
|
||
|
"one": 1,
|
||
|
"two": 2,
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
{
|
||
|
name: "value already exists for canonical key",
|
||
|
data: map[string]interface{}{
|
||
|
"PS": map[string]interface{}{
|
||
|
"OldOne": "value1",
|
||
|
"One": "original1",
|
||
|
"oldTWO": "value2",
|
||
|
"two": "original2",
|
||
|
},
|
||
|
},
|
||
|
expected: Config{
|
||
|
PS: &TypeStruct{
|
||
|
One: "original1",
|
||
|
Two: "original2",
|
||
|
},
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
for _, tc := range testcases {
|
||
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
cfg := Config{}
|
||
|
md := new(mapstructure.Metadata)
|
||
|
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||
|
DecodeHook: HookTranslateKeys,
|
||
|
Metadata: md,
|
||
|
Result: &cfg,
|
||
|
})
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
require.NoError(t, decoder.Decode(tc.data))
|
||
|
require.Equal(t, cfg, tc.expected, "decode metadata: %#v", md)
|
||
|
})
|
||
|
}
|
||
|
}
|
||
|
|
||
|
type Config struct {
|
||
|
S TypeStruct
|
||
|
PS *TypeStruct
|
||
|
PTR *TypePtrToStruct
|
||
|
PTRS TypePtrToStruct
|
||
|
Blob map[string]interface{}
|
||
|
}
|
||
|
|
||
|
type TypeStruct struct {
|
||
|
One string `alias:"oldone"`
|
||
|
Two string `alias:"oldtwo"`
|
||
|
None string
|
||
|
}
|
||
|
|
||
|
type TypePtrToStruct struct {
|
||
|
Three string `alias:"old_three"`
|
||
|
Four string `alias:"old_four,oldfour"`
|
||
|
None string
|
||
|
}
|
||
|
|
||
|
func TestHookTranslateKeys_TargetStructHasPointerReceiver(t *testing.T) {
|
||
|
target := &TypePtrToStruct{}
|
||
|
md := new(mapstructure.Metadata)
|
||
|
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
|
||
|
DecodeHook: HookTranslateKeys,
|
||
|
Metadata: md,
|
||
|
Result: target,
|
||
|
})
|
||
|
require.NoError(t, err)
|
||
|
|
||
|
data := map[string]interface{}{
|
||
|
"None": "no translation",
|
||
|
"Old_Three": "value3",
|
||
|
"OldFour": "value4",
|
||
|
}
|
||
|
expected := &TypePtrToStruct{
|
||
|
None: "no translation",
|
||
|
Three: "value3",
|
||
|
Four: "value4",
|
||
|
}
|
||
|
require.NoError(t, decoder.Decode(data))
|
||
|
require.Equal(t, expected, target, "decode metadata: %#v", md)
|
||
|
}
|
||
|
|
||
|
type translateExample struct {
|
||
|
FieldDefaultCanonical string `alias:"first"`
|
||
|
FieldWithMapstructureTag string `alias:"second" mapstructure:"field_with_mapstruct_tag"`
|
||
|
FieldWithMapstructureTagOmit string `mapstructure:"field_with_mapstruct_omit,omitempty" alias:"third"`
|
||
|
FieldWithEmptyTag string `mapstructure:"" alias:"forth"`
|
||
|
}
|
||
|
|
||
|
func TestTranslationsForType(t *testing.T) {
|
||
|
to := reflect.TypeOf(translateExample{})
|
||
|
actual := translationsForType(to)
|
||
|
expected := map[string]string{
|
||
|
"first": "fielddefaultcanonical",
|
||
|
"second": "field_with_mapstruct_tag",
|
||
|
"third": "field_with_mapstruct_omit",
|
||
|
"forth": "fieldwithemptytag",
|
||
|
}
|
||
|
require.Equal(t, expected, actual)
|
||
|
}
|
||
|
|
||
|
type nested struct {
|
||
|
O map[string]interface{}
|
||
|
Slice []Item
|
||
|
Item Item
|
||
|
}
|
||
|
|
||
|
type Item struct {
|
||
|
Name string
|
||
|
}
|