go.uber.org/yarpc@v1.72.1/internal/config/attributemap_test.go (about) 1 // Copyright (c) 2022 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package config 22 23 import ( 24 "testing" 25 26 "github.com/stretchr/testify/assert" 27 "github.com/stretchr/testify/require" 28 ) 29 30 func TestAttributeMapPopBool(t *testing.T) { 31 tests := []struct { 32 desc string 33 give AttributeMap 34 giveName string 35 36 want bool 37 wantErrors []string 38 }{ 39 { 40 desc: "true decode", 41 give: AttributeMap{ 42 "true": true, 43 }, 44 giveName: "true", 45 want: true, 46 }, 47 { 48 desc: "false decode", 49 give: AttributeMap{ 50 "false": false, 51 }, 52 giveName: "false", 53 want: false, 54 }, 55 { 56 desc: "invalid decode", 57 give: AttributeMap{ 58 "test": "teeeeeest", 59 }, 60 giveName: "test", 61 wantErrors: []string{"failed to read attribute", `"test"`, `teeeeeest`}, 62 }, 63 { 64 desc: "named string decode", 65 give: AttributeMap{ 66 "test": "true", 67 }, 68 giveName: "test", 69 want: true, 70 }, 71 { 72 desc: "named string decode false", 73 give: AttributeMap{ 74 "test": "false", 75 }, 76 giveName: "test", 77 want: false, 78 }, 79 { 80 desc: "missing decode", 81 give: AttributeMap{}, 82 giveName: "test", 83 want: false, 84 }, 85 } 86 87 for _, tt := range tests { 88 t.Run(tt.desc, func(t *testing.T) { 89 res, err := tt.give.PopBool(tt.giveName) 90 91 if len(tt.wantErrors) > 0 { 92 require.Error(t, err) 93 for _, msg := range tt.wantErrors { 94 assert.Contains(t, err.Error(), msg) 95 } 96 return 97 } 98 require.NoError(t, err) 99 100 assert.Equal(t, tt.want, res) 101 }) 102 } 103 } 104 105 func TestAttributeMapPopString(t *testing.T) { 106 tests := []struct { 107 desc string 108 give AttributeMap 109 giveName string 110 111 want string 112 wantErrors []string 113 }{ 114 { 115 desc: "value decode", 116 give: AttributeMap{ 117 "test": "value", 118 }, 119 giveName: "test", 120 want: "value", 121 }, 122 { 123 desc: "invalid decode", 124 give: AttributeMap{ 125 "test": []byte("value"), 126 }, 127 giveName: "test", 128 wantErrors: []string{`failed to read attribute "test"`}, 129 }, 130 { 131 desc: "invalid name", 132 give: AttributeMap{}, 133 giveName: "test", 134 want: "", // we default to the Nil value 135 }, 136 } 137 138 for _, tt := range tests { 139 t.Run(tt.desc, func(t *testing.T) { 140 res, err := tt.give.PopString(tt.giveName) 141 142 if len(tt.wantErrors) > 0 { 143 require.Error(t, err) 144 for _, msg := range tt.wantErrors { 145 assert.Contains(t, err.Error(), msg) 146 } 147 return 148 } 149 require.NoError(t, err) 150 151 assert.Equal(t, tt.want, res) 152 }) 153 } 154 } 155 156 func TestAttributeMapPop(t *testing.T) { 157 type someStruct struct { 158 SomeString string `config:",interpolate"` 159 AnotherString string 160 } 161 162 tests := []struct { 163 desc string 164 give AttributeMap 165 giveName string 166 giveInto interface{} 167 168 env map[string]string 169 170 want interface{} 171 wantMissing bool 172 wantErrors []string 173 }{ 174 { 175 desc: "bad string", 176 give: AttributeMap{ 177 "test": map[string]interface{}{ 178 "someString": "hello ${NAME:world", 179 }, 180 }, 181 giveInto: &someStruct{}, 182 giveName: "test", 183 wantErrors: []string{ 184 `failed to read attribute "test"`, 185 `failed to parse "hello ${NAME:world" for interpolation`, 186 }, 187 }, 188 { 189 desc: "bad string uninterpolated field", 190 give: AttributeMap{ 191 "test": map[string]interface{}{ 192 "anotherString": "hello ${NAME:world", 193 }, 194 }, 195 giveInto: &someStruct{}, 196 giveName: "test", 197 want: &someStruct{AnotherString: "hello ${NAME:world"}, 198 }, 199 { 200 desc: "string interpolation", 201 give: AttributeMap{ 202 "test": map[string]interface{}{ 203 "someString": "hello ${NAME:world}", 204 }, 205 }, 206 giveInto: &someStruct{}, 207 giveName: "test", 208 env: map[string]string{"NAME": "foo"}, 209 want: &someStruct{SomeString: "hello foo"}, 210 }, 211 { 212 desc: "missing field", 213 give: AttributeMap{}, 214 giveInto: &someStruct{}, 215 giveName: "test", 216 want: &someStruct{}, 217 wantMissing: true, 218 }, 219 } 220 221 for _, tt := range tests { 222 t.Run(tt.desc, func(t *testing.T) { 223 ok, err := tt.give.Pop(tt.giveName, tt.giveInto, InterpolateWith(mapVariableResolver(tt.env))) 224 225 if len(tt.wantErrors) > 0 { 226 require.Error(t, err) 227 for _, msg := range tt.wantErrors { 228 assert.Contains(t, err.Error(), msg) 229 } 230 return 231 } 232 require.NoError(t, err) 233 234 assert.Equal(t, tt.wantMissing, !ok) 235 assert.Equal(t, tt.want, tt.giveInto) 236 }) 237 } 238 } 239 240 func TestAttributeMapKeys(t *testing.T) { 241 tests := []struct { 242 desc string 243 give AttributeMap 244 245 wantKeys []string 246 }{ 247 { 248 desc: "no keys", 249 give: AttributeMap{}, 250 wantKeys: []string{}, 251 }, 252 { 253 desc: "one key", 254 give: AttributeMap{ 255 "test": map[string]interface{}{ 256 "anotherString": "hello ${NAME:world", 257 }, 258 }, 259 wantKeys: []string{"test"}, 260 }, 261 { 262 desc: "multi keys", 263 give: AttributeMap{ 264 "test": map[string]interface{}{ 265 "anotherString": "hello ${NAME:world", 266 }, 267 "test1": true, 268 "test2": "teeeeest", 269 "test3": 1234, 270 }, 271 wantKeys: []string{"test", "test1", "test2", "test3"}, 272 }, 273 } 274 275 for _, tt := range tests { 276 t.Run(tt.desc, func(t *testing.T) { 277 keys := tt.give.Keys() 278 279 require.Equal(t, len(tt.wantKeys), len(keys)) 280 281 keyMap := make(map[string]struct{}, len(tt.wantKeys)) 282 for _, key := range keys { 283 keyMap[key] = struct{}{} 284 } 285 286 for _, wantKey := range tt.wantKeys { 287 _, ok := keyMap[wantKey] 288 assert.True(t, ok, "key %q was not in map", wantKey) 289 } 290 }) 291 } 292 } 293 294 func TestAttributeMapDecode(t *testing.T) { 295 type someStruct struct { 296 SomeString string `config:",interpolate"` 297 AnotherString string 298 } 299 300 tests := []struct { 301 desc string 302 give AttributeMap 303 giveInto interface{} 304 305 env map[string]string 306 307 want interface{} 308 wantErrors []string 309 }{ 310 { 311 desc: "bad string", 312 give: AttributeMap{ 313 "someString": "hello ${NAME:world", 314 }, 315 giveInto: &someStruct{}, 316 wantErrors: []string{ 317 `failed to parse "hello ${NAME:world" for interpolation`, 318 }, 319 }, 320 { 321 desc: "bad string uninterpolated field", 322 give: AttributeMap{ 323 "anotherString": "hello ${NAME:world", 324 }, 325 giveInto: &someStruct{}, 326 want: &someStruct{AnotherString: "hello ${NAME:world"}, 327 }, 328 { 329 desc: "string interpolation", 330 give: AttributeMap{ 331 "someString": "hello ${NAME:world}", 332 }, 333 giveInto: &someStruct{}, 334 env: map[string]string{"NAME": "foo"}, 335 want: &someStruct{SomeString: "hello foo"}, 336 }, 337 } 338 339 for _, tt := range tests { 340 t.Run(tt.desc, func(t *testing.T) { 341 err := tt.give.Decode(tt.giveInto, InterpolateWith(mapVariableResolver(tt.env))) 342 343 if len(tt.wantErrors) > 0 { 344 require.Error(t, err) 345 for _, msg := range tt.wantErrors { 346 assert.Contains(t, err.Error(), msg) 347 } 348 return 349 } 350 require.NoError(t, err) 351 352 assert.Equal(t, tt.want, tt.giveInto) 353 }) 354 } 355 }