github.com/cilium/cilium@v1.16.2/pkg/command/map_string_test.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // Copyright Authors of Cilium 3 4 package command 5 6 import ( 7 "strings" 8 "testing" 9 10 "github.com/spf13/viper" 11 "github.com/stretchr/testify/assert" 12 ) 13 14 func TestGetStringMapString(t *testing.T) { 15 expectedResult := map[string]string{ 16 "k1": "v1", 17 "k2": "v2", 18 } 19 type args struct { 20 key string 21 value string 22 } 23 tests := []struct { 24 name string 25 args args 26 want map[string]string 27 wantErr assert.ErrorAssertionFunc 28 }{ 29 { 30 name: "valid json format", 31 args: args{ 32 key: "FOO_BAR", 33 value: `{"k1":"v1","k2":"v2"}`, 34 }, 35 want: expectedResult, 36 wantErr: assert.NoError, 37 }, 38 { 39 name: "valid empty json", 40 args: args{ 41 key: "FOO_BAR", 42 value: "{}", 43 }, 44 want: map[string]string{}, 45 wantErr: assert.NoError, 46 }, 47 { 48 name: "invalid json format with extra comma at the end", 49 args: args{ 50 key: "FOO_BAR", 51 value: `{"k1":"v1","k2":"v2",}`, 52 }, 53 want: map[string]string{}, 54 wantErr: assertErrorString("invalid character '}' looking for beginning of object key string"), 55 }, 56 { 57 name: "valid single kv format", 58 args: args{ 59 key: "FOO_BAR", 60 value: "k1=v1", 61 }, 62 want: map[string]string{"k1": "v1"}, 63 wantErr: assert.NoError, 64 }, 65 { 66 name: "valid kv format", 67 args: args{ 68 key: "FOO_BAR", 69 value: "k1=v1,k2=v2", 70 }, 71 want: expectedResult, 72 wantErr: assert.NoError, 73 }, 74 { 75 name: "valid kv format with @", 76 args: args{ 77 key: "FOO_BAR", 78 value: "k1=v1,k2=test@test.com", 79 }, 80 want: map[string]string{ 81 "k1": "v1", 82 "k2": "test@test.com", 83 }, 84 wantErr: assert.NoError, 85 }, 86 { 87 name: "valid kv format with empty value", 88 args: args{ 89 key: "FOO_BAR", 90 value: "k1=,k2=v2", 91 }, 92 want: map[string]string{ 93 "k1": "", 94 "k2": "v2", 95 }, 96 wantErr: assert.NoError, 97 }, 98 { 99 name: "valid kv format with a single key and commas in value", 100 args: args{ 101 key: "API_RATE_LIMIT", 102 value: "endpoint-create=rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true", 103 }, 104 want: map[string]string{ 105 "endpoint-create": "rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true", 106 }, 107 wantErr: assert.NoError, 108 }, 109 110 { 111 name: "valid kv format with multiple keys with commas in value", 112 args: args{ 113 key: "API_RATE_LIMIT", 114 value: "endpoint-create=rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true,endpoint-delete=rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true", 115 }, 116 want: map[string]string{ 117 "endpoint-create": "rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true", 118 "endpoint-delete": "rate-limit:10/s,rate-burst:10,parallel-requests:10,auto-adjust:true", 119 }, 120 wantErr: assert.NoError, 121 }, 122 { 123 name: "another valid kv format with comma in value", 124 args: args{ 125 key: "AWS_INSTANCE_LIMIT_MAPPING", 126 value: "c6a.2xlarge=4,15,15,m4.large=1,5,10", 127 }, 128 want: map[string]string{ 129 "c6a.2xlarge": "4,15,15", 130 "m4.large": "1,5,10", 131 }, 132 wantErr: assert.NoError, 133 }, 134 { 135 name: "valid kv format with forward slash", 136 args: args{ 137 key: "FOO_BAR", 138 value: "kubernetes.io/cluster/piano-eks-general-blue-01=owned,kubernetes.io/role/internal-elb=1", 139 }, 140 want: map[string]string{ 141 "kubernetes.io/cluster/piano-eks-general-blue-01": "owned", 142 "kubernetes.io/role/internal-elb": "1", 143 }, 144 wantErr: assert.NoError, 145 }, 146 { 147 name: "valid kv format with hyphens", 148 args: args{ 149 key: "FOO_BAR", 150 value: "cluster=my-cluster", 151 }, 152 want: map[string]string{ 153 "cluster": "my-cluster", 154 }, 155 wantErr: assert.NoError, 156 }, 157 { 158 name: "valid kv format with space", 159 args: args{ 160 key: "FOO_BAR", 161 value: "cluster=my cluster", 162 }, 163 want: map[string]string{ 164 "cluster": "my cluster", 165 }, 166 wantErr: assert.NoError, 167 }, 168 { 169 name: "valid kv format from issue #20666", 170 args: args{ 171 key: "FOO_BAR", 172 value: "a=b,c=d,E=F,G=h", 173 }, 174 want: map[string]string{ 175 "a": "b", 176 "c": "d", 177 "E": "F", 178 "G": "h", 179 }, 180 wantErr: assert.NoError, 181 }, 182 { 183 name: "valid kv format for cluster-pool-map", 184 args: args{ 185 key: "CLUSTER_POOL_MAP", 186 value: "mars=ipv4-cidrs:172.16.0.0/16,172.17.0.0/16;ipv4-mask-size:24,jupiter=ipv4-cidrs:192.168.0.0/19;ipv4-mask-size:26", 187 }, 188 want: map[string]string{ 189 "mars": "ipv4-cidrs:172.16.0.0/16,172.17.0.0/16;ipv4-mask-size:24", 190 "jupiter": "ipv4-cidrs:192.168.0.0/19;ipv4-mask-size:26", 191 }, 192 wantErr: assert.NoError, 193 }, 194 { 195 name: "invalid kv format with extra comma", 196 args: args{ 197 key: "FOO_BAR", 198 value: "k1=v1,k2=v2,", 199 }, 200 want: map[string]string{}, 201 wantErr: assertErrorString("'k1=v1,k2=v2,' is not formatted as key=value,key1=value1"), 202 }, 203 { 204 name: "invalid kv format with extra equal", 205 args: args{ 206 key: "FOO_BAR", 207 value: "k1=v1,k2==v2", 208 }, 209 want: map[string]string{}, 210 wantErr: assertErrorString("'k1=v1,k2==v2' is not formatted as key=value,key1=value1"), 211 }, 212 { 213 name: "invalid kv format with wrong space in between", 214 args: args{ 215 key: "FOO_BAR", 216 value: "k1=v1, k2=v2", 217 }, 218 want: map[string]string{}, 219 wantErr: assertErrorString("'k1=v1, k2=v2' is not formatted as key=value,key1=value1"), 220 }, 221 { 222 name: "malformed json format", 223 args: args{ 224 key: "FOO_BAR", 225 value: `{"k1": "v1",=sdlkfj`, 226 }, 227 want: map[string]string{}, 228 wantErr: assertErrorString("invalid character '=' looking for beginning of object key string"), 229 }, 230 { 231 name: "staring with valid json beginning value e.g. t, f, n, 0, -, \"", 232 args: args{ 233 key: "FOO_BAR", 234 value: "this is a sentence used in test", 235 }, 236 want: map[string]string{}, 237 wantErr: assertErrorString("'this is a sentence used in test' is not formatted as key=value,key1=value1"), 238 }, 239 } 240 241 for _, tt := range tests { 242 t.Run(tt.name, func(t *testing.T) { 243 vp := viper.New() 244 vp.AutomaticEnv() 245 t.Setenv(strings.ToUpper(tt.args.key), tt.args.value) 246 v, err := GetStringMapStringE(vp, strings.ToLower(tt.args.key)) 247 tt.wantErr(t, err) 248 assert.Equal(t, tt.want, v) 249 }) 250 } 251 } 252 253 func TestGetStringMapStringConversion(t *testing.T) { 254 vp := viper.New() 255 vp.Set("foo_bar", struct{}{}) 256 v, err := GetStringMapStringE(vp, "foo_bar") 257 assert.Error(t, err) 258 assert.Contains(t, err.Error(), "unable to cast struct {}{} of type struct {} to map[string]string") 259 assert.Equal(t, map[string]string{}, v) 260 } 261 262 func Test_isValidKeyValuePair(t *testing.T) { 263 type args struct { 264 str string 265 } 266 tests := []struct { 267 name string 268 args args 269 want bool 270 }{ 271 { 272 name: "valid format with one pair", 273 args: args{ 274 str: "k1=v1", 275 }, 276 want: true, 277 }, 278 { 279 name: "valid format with hyphen in k and v", 280 args: args{ 281 str: "k-1=v-1,k-2=v-2", 282 }, 283 want: true, 284 }, 285 { 286 name: "valid format with multiple hyphens", 287 args: args{ 288 str: "Cluster=piano-eks-general-blue-01", 289 }, 290 want: true, 291 }, 292 { 293 name: "valid format with colon", 294 args: args{ 295 str: "consul.address=127.0.0.1:8500", 296 }, 297 want: true, 298 }, 299 { 300 name: "valid format with forward slash", 301 args: args{ 302 str: "kubernetes.io/cluster/piano-eks-general-blue-01=owned", 303 }, 304 want: true, 305 }, 306 { 307 name: "valid format with multiple pairs", 308 args: args{ 309 str: "k1=v1,k2=v2,k3=v3,k4=v4,k4=v4,k4=v4", 310 }, 311 want: true, 312 }, 313 { 314 name: "valid format with multiple pairs with commas", 315 args: args{ 316 str: "k1=v,1,k2=v2,,k3=,v3,k4=v,4,k4=v4,k4=v,4", 317 }, 318 want: true, 319 }, 320 { 321 name: "empty value", 322 args: args{ 323 str: "", 324 }, 325 want: true, 326 }, 327 { 328 name: "space in between", 329 args: args{ 330 str: "k1=v1, k2=v2", 331 }, 332 want: false, 333 }, 334 { 335 name: "insufficient value", 336 args: args{ 337 str: "k1=v1,k2,=v2", 338 }, 339 want: false, 340 }, 341 { 342 name: "no pair at all", 343 args: args{ 344 str: "here-is-the-test", 345 }, 346 want: false, 347 }, 348 { 349 name: "ending with comma", 350 args: args{ 351 str: "k1=v1,k2=v2,", 352 }, 353 want: false, 354 }, 355 { 356 name: "ending with equal", 357 args: args{ 358 str: "k1=v1,k2=v2=", 359 }, 360 want: false, 361 }, 362 { 363 name: "kv separator as space", 364 args: args{ 365 str: "k1=v1 k2=v2=", 366 }, 367 want: false, 368 }, 369 { 370 name: "space in key", 371 args: args{ 372 str: "k1=v1, k2=v2", 373 }, 374 want: false, 375 }, 376 { 377 name: "value starts with space", 378 args: args{ 379 str: "k1= v1,k2=v2", 380 }, 381 want: false, 382 }, 383 { 384 name: "last value starts with space", 385 args: args{ 386 str: "k1=v1,k2= v2", 387 }, 388 want: false, 389 }, 390 { 391 name: "value ends with space", 392 args: args{ 393 str: "k1=v1 ,k2=v2", 394 }, 395 want: false, 396 }, 397 { 398 name: "last value ends with space", 399 args: args{ 400 str: "k1=v1,k2=v2 ", 401 }, 402 want: false, 403 }, 404 } 405 for _, tt := range tests { 406 t.Run(tt.name, func(t *testing.T) { 407 assert.Equalf(t, tt.want, isValidKeyValuePair(tt.args.str), "isValidKeyValuePair(%v)", tt.args.str) 408 }) 409 } 410 } 411 412 func assertErrorString(errString string) assert.ErrorAssertionFunc { 413 return func(t assert.TestingT, err error, msgAndArgs ...interface{}) bool { 414 return assert.EqualError(t, err, errString, msgAndArgs) 415 } 416 }