github.com/treeverse/lakefs@v1.24.1-0.20240520134607-95648127bfb0/pkg/config/types_test.go (about) 1 package config_test 2 3 import ( 4 "errors" 5 "strings" 6 "testing" 7 8 "github.com/go-test/deep" 9 "github.com/mitchellh/mapstructure" 10 "github.com/treeverse/lakefs/pkg/config" 11 "github.com/treeverse/lakefs/pkg/testutil" 12 ) 13 14 type StringsStruct struct { 15 S config.Strings 16 I int 17 } 18 19 func TestStrings(t *testing.T) { 20 cases := []struct { 21 Name string 22 Source map[string]interface{} 23 Expected StringsStruct 24 }{ 25 { 26 Name: "single string", 27 Source: map[string]interface{}{ 28 "s": "value", 29 }, 30 Expected: StringsStruct{ 31 S: config.Strings{"value"}, 32 }, 33 }, { 34 Name: "comma-separated string", 35 Source: map[string]interface{}{ 36 "s": "the,quick,brown", 37 }, 38 Expected: StringsStruct{ 39 S: config.Strings{"the", "quick", "brown"}, 40 }, 41 }, { 42 Name: "multiple strings", 43 Source: map[string]interface{}{ 44 "s": []string{"the", "quick", "brown"}, 45 }, 46 Expected: StringsStruct{ 47 S: config.Strings{"the", "quick", "brown"}, 48 }, 49 }, { 50 Name: "other values", 51 Source: map[string]interface{}{ 52 "s": []string{"yes"}, 53 "i": 17, 54 }, 55 Expected: StringsStruct{ 56 S: config.Strings{"yes"}, 57 I: 17, 58 }, 59 }, 60 } 61 for _, c := range cases { 62 t.Run(c.Name, func(t *testing.T) { 63 var s StringsStruct 64 65 dc := mapstructure.DecoderConfig{ 66 DecodeHook: config.DecodeStrings, 67 Result: &s, 68 } 69 decoder, err := mapstructure.NewDecoder(&dc) 70 testutil.MustDo(t, "new decoder", err) 71 err = decoder.Decode(c.Source) 72 testutil.MustDo(t, "decode", err) 73 if diffs := deep.Equal(s, c.Expected); diffs != nil { 74 t.Error(diffs) 75 } 76 }) 77 } 78 } 79 80 type OnlyStringStruct struct { 81 S config.OnlyString 82 } 83 84 // errorsMatch returns true if errors.Is(err, target), or if the error message of err 85 // contains the error message of target as a substring. It is needed until 86 // mapstructure wraps errors returned from string functions (see PR 87 // mitchellh/mapstructure#282). 88 func errorsMatch(err, target error) bool { 89 return errors.Is(err, target) || 90 target != nil && err != nil && strings.Contains(err.Error(), target.Error()) 91 } 92 93 func TestOnlyString(t *testing.T) { 94 cases := []struct { 95 Name string 96 Source map[string]interface{} 97 Expected *OnlyStringStruct 98 Err error 99 }{ 100 { 101 Name: "String", 102 Source: map[string]interface{}{"S": "string"}, 103 Expected: &OnlyStringStruct{S: "string"}, 104 }, { 105 Name: "Number", 106 Source: map[string]interface{}{"S": 17}, 107 Err: config.ErrMustBeString, 108 }, { 109 Name: "Struct", 110 Source: map[string]interface{}{"S": OnlyStringStruct{S: "string nested in struct"}}, 111 Err: config.ErrMustBeString, 112 }, 113 } 114 for _, c := range cases { 115 t.Run(c.Name, func(t *testing.T) { 116 var o OnlyStringStruct 117 118 dc := mapstructure.DecoderConfig{ 119 DecodeHook: config.DecodeOnlyString, 120 Result: &o, 121 } 122 decoder, err := mapstructure.NewDecoder(&dc) 123 testutil.MustDo(t, "new decoder", err) 124 err = decoder.Decode(c.Source) 125 if c.Err != nil && !errorsMatch(err, c.Err) { 126 t.Errorf("Got value %+v, error %v when expecting error %v", o, err, c.Err) 127 } 128 if c.Err == nil && err != nil { 129 t.Errorf("Got error %v when expecting to succeed", err) 130 } 131 if c.Expected != nil { 132 if diffs := deep.Equal(o, *c.Expected); diffs != nil { 133 t.Errorf("Got unexpected value: %s", diffs) 134 } 135 } 136 }) 137 } 138 }