github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/client/config/config_test.go (about) 1 package config 2 3 import ( 4 "testing" 5 "time" 6 7 "github.com/hashicorp/consul-template/config" 8 "github.com/hashicorp/nomad/ci" 9 "github.com/hashicorp/nomad/helper/pointer" 10 "github.com/stretchr/testify/require" 11 ) 12 13 func TestConfigRead(t *testing.T) { 14 ci.Parallel(t) 15 16 config := Config{} 17 18 actual := config.Read("cake") 19 if actual != "" { 20 t.Errorf("Expected empty string; found %s", actual) 21 } 22 23 expected := "chocolate" 24 config.Options = map[string]string{"cake": expected} 25 actual = config.Read("cake") 26 if actual != expected { 27 t.Errorf("Expected %s, found %s", expected, actual) 28 } 29 } 30 31 func TestConfigReadDefault(t *testing.T) { 32 ci.Parallel(t) 33 34 config := Config{} 35 36 expected := "vanilla" 37 actual := config.ReadDefault("cake", expected) 38 if actual != expected { 39 t.Errorf("Expected %s, found %s", expected, actual) 40 } 41 42 expected = "chocolate" 43 config.Options = map[string]string{"cake": expected} 44 actual = config.ReadDefault("cake", "vanilla") 45 if actual != expected { 46 t.Errorf("Expected %s, found %s", expected, actual) 47 } 48 } 49 50 func mockWaitConfig() *WaitConfig { 51 return &WaitConfig{ 52 Min: pointer.Of(5 * time.Second), 53 Max: pointer.Of(10 * time.Second), 54 } 55 } 56 57 func TestWaitConfig_Copy(t *testing.T) { 58 ci.Parallel(t) 59 60 cases := []struct { 61 Name string 62 Wait *WaitConfig 63 Expected *WaitConfig 64 }{ 65 { 66 "fully-populated", 67 mockWaitConfig(), 68 &WaitConfig{ 69 Min: pointer.Of(5 * time.Second), 70 Max: pointer.Of(10 * time.Second), 71 }, 72 }, 73 { 74 "min-only", 75 &WaitConfig{ 76 Min: pointer.Of(5 * time.Second), 77 }, 78 &WaitConfig{ 79 Min: pointer.Of(5 * time.Second), 80 }, 81 }, 82 { 83 "max-only", 84 &WaitConfig{ 85 Max: pointer.Of(5 * time.Second), 86 }, 87 &WaitConfig{ 88 Max: pointer.Of(5 * time.Second), 89 }, 90 }, 91 } 92 93 for _, _case := range cases { 94 t.Run(_case.Name, func(t *testing.T) { 95 result := _case.Expected.Equal(_case.Wait.Copy()) 96 if !result { 97 t.Logf("\nExpected %v\n Found %v", _case.Expected, result) 98 } 99 require.True(t, result) 100 }) 101 } 102 } 103 104 func TestWaitConfig_IsEmpty(t *testing.T) { 105 ci.Parallel(t) 106 107 cases := []struct { 108 Name string 109 Wait *WaitConfig 110 Expected bool 111 }{ 112 { 113 "is-nil", 114 nil, 115 true, 116 }, 117 { 118 "is-empty", 119 &WaitConfig{}, 120 true, 121 }, 122 { 123 "is-not-empty", 124 &WaitConfig{ 125 Min: pointer.Of(10 * time.Second), 126 }, 127 false, 128 }, 129 } 130 131 for _, _case := range cases { 132 t.Run(_case.Name, func(t *testing.T) { 133 require.Equal(t, _case.Expected, _case.Wait.IsEmpty()) 134 }) 135 } 136 } 137 138 func TestWaitConfig_IsEqual(t *testing.T) { 139 ci.Parallel(t) 140 141 cases := []struct { 142 Name string 143 Wait *WaitConfig 144 Other *WaitConfig 145 Expected bool 146 }{ 147 { 148 "are-equal", 149 mockWaitConfig(), 150 &WaitConfig{ 151 Min: pointer.Of(5 * time.Second), 152 Max: pointer.Of(10 * time.Second), 153 }, 154 true, 155 }, 156 { 157 "min-different", 158 mockWaitConfig(), 159 &WaitConfig{ 160 Min: pointer.Of(4 * time.Second), 161 Max: pointer.Of(10 * time.Second), 162 }, 163 false, 164 }, 165 { 166 "max-different", 167 mockWaitConfig(), 168 &WaitConfig{ 169 Min: pointer.Of(5 * time.Second), 170 Max: pointer.Of(9 * time.Second), 171 }, 172 false, 173 }, 174 } 175 176 for _, _case := range cases { 177 t.Run(_case.Name, func(t *testing.T) { 178 require.Equal(t, _case.Expected, _case.Wait.Equal(_case.Other)) 179 }) 180 } 181 } 182 183 func TestWaitConfig_IsValid(t *testing.T) { 184 ci.Parallel(t) 185 186 cases := []struct { 187 Name string 188 Retry *WaitConfig 189 Expected string 190 }{ 191 { 192 "is-valid", 193 &WaitConfig{ 194 Min: pointer.Of(5 * time.Second), 195 Max: pointer.Of(10 * time.Second), 196 }, 197 "", 198 }, 199 { 200 "is-nil", 201 nil, 202 "is nil", 203 }, 204 { 205 "is-empty", 206 &WaitConfig{}, 207 "or empty", 208 }, 209 { 210 "min-greater-than-max", 211 &WaitConfig{ 212 Min: pointer.Of(10 * time.Second), 213 Max: pointer.Of(5 * time.Second), 214 }, 215 "greater than", 216 }, 217 { 218 "max-not-set", 219 &WaitConfig{ 220 Min: pointer.Of(10 * time.Second), 221 }, 222 "", 223 }, 224 } 225 226 for _, _case := range cases { 227 t.Run(_case.Name, func(t *testing.T) { 228 if _case.Expected == "" { 229 require.Nil(t, _case.Retry.Validate()) 230 } else { 231 err := _case.Retry.Validate() 232 require.Contains(t, err.Error(), _case.Expected) 233 } 234 }) 235 } 236 } 237 238 func TestWaitConfig_Merge(t *testing.T) { 239 ci.Parallel(t) 240 241 cases := []struct { 242 Name string 243 Target *WaitConfig 244 Other *WaitConfig 245 Expected *WaitConfig 246 }{ 247 { 248 "all-fields", 249 mockWaitConfig(), 250 &WaitConfig{ 251 Min: pointer.Of(4 * time.Second), 252 Max: pointer.Of(9 * time.Second), 253 }, 254 &WaitConfig{ 255 Min: pointer.Of(4 * time.Second), 256 Max: pointer.Of(9 * time.Second), 257 }, 258 }, 259 { 260 "min-only", 261 mockWaitConfig(), 262 &WaitConfig{ 263 Min: pointer.Of(4 * time.Second), 264 Max: pointer.Of(10 * time.Second), 265 }, 266 &WaitConfig{ 267 Min: pointer.Of(4 * time.Second), 268 Max: pointer.Of(10 * time.Second), 269 }, 270 }, 271 { 272 "max-only", 273 mockWaitConfig(), 274 &WaitConfig{ 275 Min: pointer.Of(5 * time.Second), 276 Max: pointer.Of(9 * time.Second), 277 }, 278 &WaitConfig{ 279 Min: pointer.Of(5 * time.Second), 280 Max: pointer.Of(9 * time.Second), 281 }, 282 }, 283 } 284 285 for _, _case := range cases { 286 t.Run(_case.Name, func(t *testing.T) { 287 merged := _case.Target.Merge(_case.Other) 288 result := _case.Expected.Equal(merged) 289 if !result { 290 t.Logf("\nExpected %v\n Found %v", _case.Expected, merged) 291 } 292 require.True(t, result) 293 }) 294 } 295 } 296 297 func TestWaitConfig_ToConsulTemplate(t *testing.T) { 298 ci.Parallel(t) 299 300 expected := config.WaitConfig{ 301 Enabled: pointer.Of(true), 302 Min: pointer.Of(5 * time.Second), 303 Max: pointer.Of(10 * time.Second), 304 } 305 306 clientWaitConfig := &WaitConfig{ 307 Min: pointer.Of(5 * time.Second), 308 Max: pointer.Of(10 * time.Second), 309 } 310 311 actual, err := clientWaitConfig.ToConsulTemplate() 312 require.NoError(t, err) 313 require.Equal(t, *expected.Min, *actual.Min) 314 require.Equal(t, *expected.Max, *actual.Max) 315 } 316 317 func mockRetryConfig() *RetryConfig { 318 return &RetryConfig{ 319 Attempts: pointer.Of(5), 320 Backoff: pointer.Of(5 * time.Second), 321 BackoffHCL: "5s", 322 MaxBackoff: pointer.Of(10 * time.Second), 323 MaxBackoffHCL: "10s", 324 } 325 } 326 func TestRetryConfig_Copy(t *testing.T) { 327 ci.Parallel(t) 328 329 cases := []struct { 330 Name string 331 Retry *RetryConfig 332 Expected *RetryConfig 333 }{ 334 { 335 "fully-populated", 336 mockRetryConfig(), 337 &RetryConfig{ 338 Attempts: pointer.Of(5), 339 Backoff: pointer.Of(5 * time.Second), 340 BackoffHCL: "5s", 341 MaxBackoff: pointer.Of(10 * time.Second), 342 MaxBackoffHCL: "10s", 343 }, 344 }, 345 { 346 "attempts-only", 347 &RetryConfig{ 348 Attempts: pointer.Of(5), 349 }, 350 &RetryConfig{ 351 Attempts: pointer.Of(5), 352 }, 353 }, 354 { 355 "backoff-only", 356 &RetryConfig{ 357 Backoff: pointer.Of(5 * time.Second), 358 }, 359 &RetryConfig{ 360 Backoff: pointer.Of(5 * time.Second), 361 }, 362 }, 363 { 364 "backoff-hcl-only", 365 &RetryConfig{ 366 BackoffHCL: "5s", 367 }, 368 &RetryConfig{ 369 BackoffHCL: "5s", 370 }, 371 }, 372 { 373 "max-backoff-only", 374 &RetryConfig{ 375 MaxBackoff: pointer.Of(10 * time.Second), 376 }, 377 &RetryConfig{ 378 MaxBackoff: pointer.Of(10 * time.Second), 379 }, 380 }, 381 { 382 "max-backoff-hcl-only", 383 &RetryConfig{ 384 MaxBackoffHCL: "10s", 385 }, 386 &RetryConfig{ 387 MaxBackoffHCL: "10s", 388 }, 389 }, 390 } 391 392 for _, _case := range cases { 393 t.Run(_case.Name, func(t *testing.T) { 394 result := _case.Expected.Equal(_case.Retry.Copy()) 395 if !result { 396 t.Logf("\nExpected %v\n Found %v", _case.Expected, result) 397 } 398 require.True(t, result) 399 }) 400 } 401 } 402 403 func TestRetryConfig_IsEmpty(t *testing.T) { 404 ci.Parallel(t) 405 406 cases := []struct { 407 Name string 408 Retry *RetryConfig 409 Expected bool 410 }{ 411 { 412 "is-nil", 413 nil, 414 true, 415 }, 416 { 417 "is-empty", 418 &RetryConfig{}, 419 true, 420 }, 421 { 422 "is-not-empty", 423 &RetryConfig{ 424 Attempts: pointer.Of(12), 425 }, 426 false, 427 }, 428 } 429 430 for _, _case := range cases { 431 t.Run(_case.Name, func(t *testing.T) { 432 require.Equal(t, _case.Expected, _case.Retry.IsEmpty()) 433 }) 434 } 435 } 436 437 func TestRetryConfig_IsEqual(t *testing.T) { 438 ci.Parallel(t) 439 440 cases := []struct { 441 Name string 442 Retry *RetryConfig 443 Other *RetryConfig 444 Expected bool 445 }{ 446 { 447 "are-equal", 448 mockRetryConfig(), 449 &RetryConfig{ 450 Attempts: pointer.Of(5), 451 Backoff: pointer.Of(5 * time.Second), 452 BackoffHCL: "5s", 453 MaxBackoff: pointer.Of(10 * time.Second), 454 MaxBackoffHCL: "10s", 455 }, 456 true, 457 }, 458 { 459 "attempts-different", 460 mockRetryConfig(), 461 &RetryConfig{ 462 Attempts: pointer.Of(4), 463 Backoff: pointer.Of(5 * time.Second), 464 BackoffHCL: "5s", 465 MaxBackoff: pointer.Of(10 * time.Second), 466 MaxBackoffHCL: "10s", 467 }, 468 false, 469 }, 470 { 471 "backoff-different", 472 mockRetryConfig(), 473 &RetryConfig{ 474 Attempts: pointer.Of(5), 475 Backoff: pointer.Of(4 * time.Second), 476 BackoffHCL: "5s", 477 MaxBackoff: pointer.Of(10 * time.Second), 478 MaxBackoffHCL: "10s", 479 }, 480 false, 481 }, 482 { 483 "backoff-hcl-different", 484 mockRetryConfig(), 485 &RetryConfig{ 486 Attempts: pointer.Of(5), 487 Backoff: pointer.Of(5 * time.Second), 488 BackoffHCL: "4s", 489 MaxBackoff: pointer.Of(10 * time.Second), 490 MaxBackoffHCL: "10s", 491 }, 492 false, 493 }, 494 { 495 "max-backoff-different", 496 mockRetryConfig(), 497 &RetryConfig{ 498 Attempts: pointer.Of(5), 499 Backoff: pointer.Of(5 * time.Second), 500 BackoffHCL: "5s", 501 MaxBackoff: pointer.Of(9 * time.Second), 502 MaxBackoffHCL: "10s", 503 }, 504 false, 505 }, 506 { 507 "max-backoff-hcl-different", 508 mockRetryConfig(), 509 &RetryConfig{ 510 Attempts: pointer.Of(5), 511 Backoff: pointer.Of(5 * time.Second), 512 BackoffHCL: "5s", 513 MaxBackoff: pointer.Of(10 * time.Second), 514 MaxBackoffHCL: "9s", 515 }, 516 false, 517 }, 518 } 519 520 for _, _case := range cases { 521 t.Run(_case.Name, func(t *testing.T) { 522 require.Equal(t, _case.Expected, _case.Retry.Equal(_case.Other)) 523 }) 524 } 525 } 526 527 func TestRetryConfig_IsValid(t *testing.T) { 528 ci.Parallel(t) 529 530 cases := []struct { 531 Name string 532 Retry *RetryConfig 533 Expected string 534 }{ 535 { 536 "is-valid", 537 &RetryConfig{ 538 Backoff: pointer.Of(5 * time.Second), 539 MaxBackoff: pointer.Of(10 * time.Second), 540 }, 541 "", 542 }, 543 { 544 "is-nil", 545 nil, 546 "is nil", 547 }, 548 { 549 "is-empty", 550 &RetryConfig{}, 551 "or empty", 552 }, 553 { 554 "backoff-greater-than-max-backoff", 555 &RetryConfig{ 556 Backoff: pointer.Of(10 * time.Second), 557 MaxBackoff: pointer.Of(5 * time.Second), 558 }, 559 "greater than max_backoff", 560 }, 561 { 562 "backoff-not-set", 563 &RetryConfig{ 564 MaxBackoff: pointer.Of(10 * time.Second), 565 }, 566 "", 567 }, 568 { 569 "max-backoff-not-set", 570 &RetryConfig{ 571 Backoff: pointer.Of(2 * time.Minute), 572 }, 573 "greater than default", 574 }, 575 { 576 "max-backoff-unbounded", 577 &RetryConfig{ 578 Backoff: pointer.Of(10 * time.Second), 579 MaxBackoff: pointer.Of(0 * time.Second), 580 }, 581 "", 582 }, 583 } 584 585 for _, _case := range cases { 586 t.Run(_case.Name, func(t *testing.T) { 587 if _case.Expected == "" { 588 require.Nil(t, _case.Retry.Validate()) 589 } else { 590 err := _case.Retry.Validate() 591 require.Contains(t, err.Error(), _case.Expected) 592 } 593 }) 594 } 595 } 596 597 func TestRetryConfig_Merge(t *testing.T) { 598 ci.Parallel(t) 599 600 cases := []struct { 601 Name string 602 Target *RetryConfig 603 Other *RetryConfig 604 Expected *RetryConfig 605 }{ 606 { 607 "all-fields", 608 mockRetryConfig(), 609 &RetryConfig{ 610 Attempts: pointer.Of(4), 611 Backoff: pointer.Of(4 * time.Second), 612 BackoffHCL: "4s", 613 MaxBackoff: pointer.Of(9 * time.Second), 614 MaxBackoffHCL: "9s", 615 }, 616 &RetryConfig{ 617 Attempts: pointer.Of(4), 618 Backoff: pointer.Of(4 * time.Second), 619 BackoffHCL: "4s", 620 MaxBackoff: pointer.Of(9 * time.Second), 621 MaxBackoffHCL: "9s", 622 }, 623 }, 624 { 625 "attempts-only", 626 mockRetryConfig(), 627 &RetryConfig{ 628 Attempts: pointer.Of(4), 629 Backoff: pointer.Of(5 * time.Second), 630 BackoffHCL: "5s", 631 MaxBackoff: pointer.Of(10 * time.Second), 632 MaxBackoffHCL: "10s", 633 }, 634 &RetryConfig{ 635 Attempts: pointer.Of(4), 636 Backoff: pointer.Of(5 * time.Second), 637 BackoffHCL: "5s", 638 MaxBackoff: pointer.Of(10 * time.Second), 639 MaxBackoffHCL: "10s", 640 }, 641 }, 642 { 643 "multi-field", 644 mockRetryConfig(), 645 &RetryConfig{ 646 Attempts: pointer.Of(5), 647 Backoff: pointer.Of(4 * time.Second), 648 BackoffHCL: "4s", 649 MaxBackoff: pointer.Of(9 * time.Second), 650 MaxBackoffHCL: "9s", 651 }, 652 &RetryConfig{ 653 Attempts: pointer.Of(5), 654 Backoff: pointer.Of(4 * time.Second), 655 BackoffHCL: "4s", 656 MaxBackoff: pointer.Of(9 * time.Second), 657 MaxBackoffHCL: "9s", 658 }, 659 }, 660 } 661 662 for _, _case := range cases { 663 t.Run(_case.Name, func(t *testing.T) { 664 merged := _case.Target.Merge(_case.Other) 665 result := _case.Expected.Equal(merged) 666 if !result { 667 t.Logf("\nExpected %v\n Found %v", _case.Expected, merged) 668 } 669 require.True(t, result) 670 }) 671 } 672 } 673 674 func TestRetryConfig_ToConsulTemplate(t *testing.T) { 675 ci.Parallel(t) 676 677 expected := config.RetryConfig{ 678 Enabled: pointer.Of(true), 679 Attempts: pointer.Of(5), 680 Backoff: pointer.Of(5 * time.Second), 681 MaxBackoff: pointer.Of(10 * time.Second), 682 } 683 684 actual := mockRetryConfig() 685 686 require.Equal(t, *expected.Attempts, *actual.Attempts) 687 require.Equal(t, *expected.Backoff, *actual.Backoff) 688 require.Equal(t, *expected.MaxBackoff, *actual.MaxBackoff) 689 }