github.1git.de/docker/cli@v26.1.3+incompatible/opts/opts_test.go (about) 1 package opts 2 3 import ( 4 "fmt" 5 "strings" 6 "testing" 7 8 "gotest.tools/v3/assert" 9 is "gotest.tools/v3/assert/cmp" 10 ) 11 12 func TestValidateIPAddress(t *testing.T) { 13 tests := []struct { 14 doc string 15 input string 16 expectedOut string 17 expectedErr string 18 }{ 19 { 20 doc: "IPv4 loopback", 21 input: `127.0.0.1`, 22 expectedOut: `127.0.0.1`, 23 }, 24 { 25 doc: "IPv4 loopback with whitespace", 26 input: ` 127.0.0.1 `, 27 expectedOut: `127.0.0.1`, 28 }, 29 { 30 doc: "IPv6 loopback long form", 31 input: `0:0:0:0:0:0:0:1`, 32 expectedOut: `::1`, 33 }, 34 { 35 doc: "IPv6 loopback", 36 input: `::1`, 37 expectedOut: `::1`, 38 }, 39 { 40 doc: "IPv6 loopback with whitespace", 41 input: ` ::1 `, 42 expectedOut: `::1`, 43 }, 44 { 45 doc: "IPv6 lowercase", 46 input: `2001:db8::68`, 47 expectedOut: `2001:db8::68`, 48 }, 49 { 50 doc: "IPv6 uppercase", 51 input: `2001:DB8::68`, 52 expectedOut: `2001:db8::68`, 53 }, 54 { 55 doc: "IPv6 with brackets", 56 input: `[::1]`, 57 expectedErr: `IP address is not correctly formatted: [::1]`, 58 }, 59 { 60 doc: "IPv4 partial", 61 input: `127`, 62 expectedErr: `IP address is not correctly formatted: 127`, 63 }, 64 { 65 doc: "random invalid string", 66 input: `random invalid string`, 67 expectedErr: `IP address is not correctly formatted: random invalid string`, 68 }, 69 } 70 71 for _, tc := range tests { 72 tc := tc 73 t.Run(tc.input, func(t *testing.T) { 74 actualOut, actualErr := ValidateIPAddress(tc.input) 75 assert.Check(t, is.Equal(tc.expectedOut, actualOut)) 76 if tc.expectedErr == "" { 77 assert.Check(t, actualErr) 78 } else { 79 assert.Check(t, is.Error(actualErr, tc.expectedErr)) 80 } 81 }) 82 } 83 } 84 85 func TestMapOpts(t *testing.T) { 86 tmpMap := make(map[string]string) 87 o := NewMapOpts(tmpMap, sampleValidator) 88 err := o.Set("valid-option=1") 89 if err != nil { 90 t.Error(err) 91 } 92 if o.String() != "map[valid-option:1]" { 93 t.Errorf("%s != [map[valid-option:1]", o.String()) 94 } 95 96 err = o.Set("valid-option2=2") 97 if err != nil { 98 t.Error(err) 99 } 100 if len(tmpMap) != 2 { 101 t.Errorf("map length %d != 2", len(tmpMap)) 102 } 103 104 if tmpMap["valid-option"] != "1" { 105 t.Errorf("valid-option = %s != 1", tmpMap["valid-option"]) 106 } 107 if tmpMap["valid-option2"] != "2" { 108 t.Errorf("valid-option2 = %s != 2", tmpMap["valid-option2"]) 109 } 110 111 if o.Set("dummy-val=3") == nil { 112 t.Error("validator is not being called") 113 } 114 } 115 116 func TestListOptsWithoutValidator(t *testing.T) { 117 o := NewListOpts(nil) 118 err := o.Set("foo") 119 if err != nil { 120 t.Error(err) 121 } 122 if o.String() != "[foo]" { 123 t.Errorf("%s != [foo]", o.String()) 124 } 125 err = o.Set("bar") 126 if err != nil { 127 t.Error(err) 128 } 129 if o.Len() != 2 { 130 t.Errorf("%d != 2", o.Len()) 131 } 132 err = o.Set("bar") 133 if err != nil { 134 t.Error(err) 135 } 136 if o.Len() != 3 { 137 t.Errorf("%d != 3", o.Len()) 138 } 139 if !o.Get("bar") { 140 t.Error("o.Get(\"bar\") == false") 141 } 142 if o.Get("baz") { 143 t.Error("o.Get(\"baz\") == true") 144 } 145 o.Delete("foo") 146 if o.String() != "[bar bar]" { 147 t.Errorf("%s != [bar bar]", o.String()) 148 } 149 listOpts := o.GetAll() 150 if len(listOpts) != 2 || listOpts[0] != "bar" || listOpts[1] != "bar" { 151 t.Errorf("Expected [[bar bar]], got [%v]", listOpts) 152 } 153 mapListOpts := o.GetMap() 154 if len(mapListOpts) != 1 { 155 t.Errorf("Expected [map[bar:{}]], got [%v]", mapListOpts) 156 } 157 } 158 159 func TestListOptsWithValidator(t *testing.T) { 160 o := NewListOpts(sampleValidator) 161 err := o.Set("foo") 162 if err == nil { 163 t.Error(err) 164 } 165 if o.String() != "" { 166 t.Errorf(`%s != ""`, o.String()) 167 } 168 err = o.Set("foo=bar") 169 if err == nil { 170 t.Error(err) 171 } 172 if o.String() != "" { 173 t.Errorf(`%s != ""`, o.String()) 174 } 175 err = o.Set("valid-option2=2") 176 if err != nil { 177 t.Error(err) 178 } 179 if o.Len() != 1 { 180 t.Errorf("%d != 1", o.Len()) 181 } 182 if !o.Get("valid-option2=2") { 183 t.Error(`o.Get("valid-option2=2") == false`) 184 } 185 if o.Get("baz") { 186 t.Error(`o.Get("baz") == true`) 187 } 188 o.Delete("valid-option2=2") 189 if o.String() != "" { 190 t.Errorf(`%s != ""`, o.String()) 191 } 192 } 193 194 //nolint:lll 195 func TestValidateDNSSearch(t *testing.T) { 196 valid := []string{ 197 `.`, 198 `a`, 199 `a.`, 200 `1.foo`, 201 `17.foo`, 202 `foo.bar`, 203 `foo.bar.baz`, 204 `foo.bar.`, 205 `foo.bar.baz`, 206 `foo1.bar2`, 207 `foo1.bar2.baz`, 208 `1foo.2bar.`, 209 `1foo.2bar.baz`, 210 `foo-1.bar-2`, 211 `foo-1.bar-2.baz`, 212 `foo-1.bar-2.`, 213 `foo-1.bar-2.baz`, 214 `1-foo.2-bar`, 215 `1-foo.2-bar.baz`, 216 `1-foo.2-bar.`, 217 `1-foo.2-bar.baz`, 218 } 219 220 invalid := []string{ 221 ``, 222 ` `, 223 ` `, 224 `17`, 225 `17.`, 226 `.17`, 227 `17-.`, 228 `17-.foo`, 229 `.foo`, 230 `foo-.bar`, 231 `-foo.bar`, 232 `foo.bar-`, 233 `foo.bar-.baz`, 234 `foo.-bar`, 235 `foo.-bar.baz`, 236 `foo.bar.baz.this.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbethis.should.fail.on.long.name.because.it.is.longer.thanisshouldbe`, 237 } 238 239 for _, domain := range valid { 240 if ret, err := ValidateDNSSearch(domain); err != nil || ret == "" { 241 t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err) 242 } 243 } 244 245 for _, domain := range invalid { 246 if ret, err := ValidateDNSSearch(domain); err == nil || ret != "" { 247 t.Fatalf("ValidateDNSSearch(`"+domain+"`) got %s %s", ret, err) 248 } 249 } 250 } 251 252 func TestValidateLabel(t *testing.T) { 253 tests := []struct { 254 name string 255 value string 256 expectedErr string 257 }{ 258 { 259 name: "empty", 260 expectedErr: `invalid label '': empty name`, 261 }, 262 { 263 name: "whitespace only ", 264 value: " ", 265 expectedErr: `invalid label ' ': empty name`, 266 }, 267 { 268 name: "whitespace around equal-sign", 269 value: " = ", 270 expectedErr: `invalid label ' = ': empty name`, 271 }, 272 { 273 name: "leading whitespace", 274 value: " label=value", 275 }, 276 { 277 name: "whitespaces in key without value", 278 value: "this is a label without value", 279 expectedErr: `label 'this is a label without value' contains whitespaces`, 280 }, 281 { 282 name: "whitespaces in key", 283 value: "this is a label=value", 284 expectedErr: `label 'this is a label' contains whitespaces`, 285 }, 286 { 287 name: "whitespaces in value", 288 value: "label=a value that has whitespace", 289 }, 290 { 291 name: "trailing whitespace in value", 292 value: "label=value ", 293 }, 294 { 295 name: "leading whitespace in value", 296 value: "label= value", 297 }, 298 { 299 name: "no value", 300 value: "label", 301 }, 302 { 303 name: "no key", 304 value: "=label", 305 expectedErr: `invalid label '=label': empty name`, 306 }, 307 { 308 name: "empty value", 309 value: "label=", 310 }, 311 { 312 name: "key value", 313 value: "key1=value1", 314 }, 315 { 316 name: "double equal-signs", 317 value: "key1=value1=value2", 318 }, 319 { 320 name: "multiple equal-signs", 321 value: "key1=value1=value2=value", 322 }, 323 { 324 name: "double quotes in key and value", 325 value: `key"with"quotes={"hello"}`, 326 }, 327 { 328 name: "double quotes around key and value", 329 value: `"quoted-label"="quoted value"`, 330 }, 331 { 332 name: "single quotes in key and value", 333 value: `key'with'quotes=hello'with'quotes`, 334 }, 335 { 336 name: "single quotes around key and value", 337 value: `'quoted-label'='quoted value''`, 338 }, 339 } 340 341 for _, tc := range tests { 342 tc := tc 343 t.Run(tc.name, func(t *testing.T) { 344 val, err := ValidateLabel(tc.value) 345 if tc.expectedErr != "" { 346 assert.Error(t, err, tc.expectedErr) 347 return 348 } 349 assert.NilError(t, err) 350 assert.Equal(t, val, tc.value) 351 }) 352 } 353 } 354 355 func sampleValidator(val string) (string, error) { 356 allowedKeys := map[string]string{"valid-option": "1", "valid-option2": "2"} 357 k, _, _ := strings.Cut(val, "=") 358 if allowedKeys[k] != "" { 359 return val, nil 360 } 361 return "", fmt.Errorf("invalid key %s", k) 362 } 363 364 func TestNamedListOpts(t *testing.T) { 365 var v []string 366 o := NewNamedListOptsRef("foo-name", &v, nil) 367 368 o.Set("foo") 369 if o.String() != "[foo]" { 370 t.Errorf("%s != [foo]", o.String()) 371 } 372 if o.Name() != "foo-name" { 373 t.Errorf("%s != foo-name", o.Name()) 374 } 375 if len(v) != 1 { 376 t.Errorf("expected foo to be in the values, got %v", v) 377 } 378 } 379 380 func TestNamedMapOpts(t *testing.T) { 381 tmpMap := make(map[string]string) 382 o := NewNamedMapOpts("max-name", tmpMap, nil) 383 384 o.Set("max-size=1") 385 if o.String() != "map[max-size:1]" { 386 t.Errorf("%s != [map[max-size:1]", o.String()) 387 } 388 if o.Name() != "max-name" { 389 t.Errorf("%s != max-name", o.Name()) 390 } 391 if _, exist := tmpMap["max-size"]; !exist { 392 t.Errorf("expected map-size to be in the values, got %v", tmpMap) 393 } 394 } 395 396 func TestValidateMACAddress(t *testing.T) { 397 if _, err := ValidateMACAddress(`92:d0:c6:0a:29:33`); err != nil { 398 t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:29:33`) got %s", err) 399 } 400 401 if _, err := ValidateMACAddress(`92:d0:c6:0a:33`); err == nil { 402 t.Fatalf("ValidateMACAddress(`92:d0:c6:0a:33`) succeeded; expected failure on invalid MAC") 403 } 404 405 if _, err := ValidateMACAddress(`random invalid string`); err == nil { 406 t.Fatalf("ValidateMACAddress(`random invalid string`) succeeded; expected failure on invalid MAC") 407 } 408 } 409 410 func TestValidateLink(t *testing.T) { 411 valid := []string{ 412 "name", 413 "dcdfbe62ecd0:alias", 414 "7a67485460b7642516a4ad82ecefe7f57d0c4916f530561b71a50a3f9c4e33da", 415 "angry_torvalds:linus", 416 } 417 invalid := map[string]string{ 418 "": "empty string specified for links", 419 "too:much:of:it": "bad format for links: too:much:of:it", 420 } 421 422 for _, link := range valid { 423 if _, err := ValidateLink(link); err != nil { 424 t.Fatalf("ValidateLink(`%q`) should succeed: error %q", link, err) 425 } 426 } 427 428 for link, expectedError := range invalid { 429 if _, err := ValidateLink(link); err == nil { 430 t.Fatalf("ValidateLink(`%q`) should have failed validation", link) 431 } else if !strings.Contains(err.Error(), expectedError) { 432 t.Fatalf("ValidateLink(`%q`) error should contain %q", link, expectedError) 433 } 434 } 435 } 436 437 func TestParseLink(t *testing.T) { 438 name, alias, err := ParseLink("name:alias") 439 if err != nil { 440 t.Fatalf("Expected not to error out on a valid name:alias format but got: %v", err) 441 } 442 if name != "name" { 443 t.Fatalf("Link name should have been name, got %s instead", name) 444 } 445 if alias != "alias" { 446 t.Fatalf("Link alias should have been alias, got %s instead", alias) 447 } 448 // short format definition 449 name, alias, err = ParseLink("name") 450 if err != nil { 451 t.Fatalf("Expected not to error out on a valid name only format but got: %v", err) 452 } 453 if name != "name" { 454 t.Fatalf("Link name should have been name, got %s instead", name) 455 } 456 if alias != "name" { 457 t.Fatalf("Link alias should have been name, got %s instead", alias) 458 } 459 // empty string link definition is not allowed 460 if _, _, err := ParseLink(""); err == nil || !strings.Contains(err.Error(), "empty string specified for links") { 461 t.Fatalf("Expected error 'empty string specified for links' but got: %v", err) 462 } 463 // more than two colons are not allowed 464 if _, _, err := ParseLink("link:alias:wrong"); err == nil || !strings.Contains(err.Error(), "bad format for links: link:alias:wrong") { 465 t.Fatalf("Expected error 'bad format for links: link:alias:wrong' but got: %v", err) 466 } 467 } 468 469 func TestGetAllOrEmptyReturnsNilOrValue(t *testing.T) { 470 opts := NewListOpts(nil) 471 assert.Check(t, is.DeepEqual(opts.GetAllOrEmpty(), []string{})) 472 opts.Set("foo") 473 assert.Check(t, is.DeepEqual(opts.GetAllOrEmpty(), []string{"foo"})) 474 } 475 476 func TestParseCPUsReturnZeroOnInvalidValues(t *testing.T) { 477 resValue, _ := ParseCPUs("foo") 478 var z1 int64 = 0 479 assert.Equal(t, z1, resValue) 480 resValue, _ = ParseCPUs("1e-32") 481 assert.Equal(t, z1, resValue) 482 }