github.com/vmware/govmomi@v0.51.0/fault/fault_test.go (about) 1 // © Broadcom. All Rights Reserved. 2 // The term “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. 3 // SPDX-License-Identifier: Apache-2.0 4 5 package fault_test 6 7 import ( 8 "errors" 9 "fmt" 10 "reflect" 11 "testing" 12 13 "github.com/stretchr/testify/assert" 14 15 "github.com/vmware/govmomi/fault" 16 "github.com/vmware/govmomi/task" 17 "github.com/vmware/govmomi/vim25/soap" 18 "github.com/vmware/govmomi/vim25/types" 19 ) 20 21 func asPtr[T any](args ...T) *T { 22 if len(args) == 0 { 23 var t T 24 return &t 25 } 26 return &args[0] 27 } 28 29 var ( 30 nilSystemErr = asPtr[*types.SystemError]() 31 nilInvalidVmConfig = asPtr[*types.InvalidVmConfig]() 32 ) 33 34 type nilErrWrapper struct{} 35 36 func (e nilErrWrapper) Unwrap() error { 37 return nil 38 } 39 40 type unwrappableErrSlice []error 41 42 func (e unwrappableErrSlice) Unwrap() []error { 43 return e 44 } 45 46 type nilBaseMethodFault struct{} 47 48 func (f nilBaseMethodFault) GetMethodFault() *types.MethodFault { 49 return nil 50 } 51 52 type asFaulter struct { 53 val any 54 okay bool 55 msg string 56 } 57 58 func (f asFaulter) AsFault(target any) (string, bool) { 59 if !f.okay { 60 return "", false 61 } 62 targetVal := reflect.ValueOf(target) 63 targetVal.Elem().Set(reflect.ValueOf(f.val)) 64 return f.msg, true 65 } 66 67 type isFaulter bool 68 69 func (f isFaulter) GetMethodFault() *types.MethodFault { 70 return nil 71 } 72 73 func (f isFaulter) IsFault(target types.BaseMethodFault) bool { 74 return bool(f) 75 } 76 77 func TestAs(t *testing.T) { 78 79 testCases := []struct { 80 name string 81 err any 82 target any 83 expectedLocalizedMessage string 84 expectedOkay bool 85 expectedPanic any 86 expectedTarget any 87 }{ 88 89 { 90 name: "err is nil", 91 err: nil, 92 target: asPtr[*types.SystemError](), 93 expectedTarget: asPtr[*types.SystemError](), 94 }, 95 { 96 name: "err is not supported", 97 err: struct{}{}, 98 target: asPtr[*types.SystemError](), 99 expectedTarget: asPtr[*types.SystemError](), 100 }, 101 { 102 name: "target is nil", 103 err: errors.New("error"), 104 target: nil, 105 expectedTarget: nil, 106 expectedPanic: "fault: target cannot be nil", 107 }, 108 { 109 name: "target is not pointer", 110 err: errors.New("error"), 111 target: types.SystemError{}, 112 expectedTarget: types.SystemError{}, 113 expectedPanic: "fault: target must be a non-nil pointer", 114 }, 115 { 116 name: "target is not pointer to expected type", 117 err: errors.New("error"), 118 target: &types.SystemError{}, 119 expectedTarget: &types.SystemError{}, 120 expectedPanic: "fault: *target must be interface or implement BaseMethodFault", 121 }, 122 { 123 name: "err is task.Error with fault", 124 err: task.Error{ 125 LocalizedMethodFault: &types.LocalizedMethodFault{ 126 Fault: &types.SystemError{}, 127 }, 128 }, 129 target: asPtr[*types.SystemError](), 130 expectedTarget: asPtr(&types.SystemError{}), 131 expectedOkay: true, 132 expectedLocalizedMessage: "", 133 }, 134 { 135 name: "err unwraps to nil error", 136 err: nilErrWrapper{}, 137 target: asPtr[*types.SystemError](), 138 expectedTarget: asPtr[*types.SystemError](), 139 }, 140 { 141 name: "err unwraps to nil error slice", 142 err: unwrappableErrSlice{}, 143 target: asPtr[*types.SystemError](), 144 expectedTarget: asPtr[*types.SystemError](), 145 }, 146 { 147 name: "err is wrapped task.Error with fault", 148 err: fmt.Errorf("my error: %w", task.Error{ 149 LocalizedMethodFault: &types.LocalizedMethodFault{ 150 Fault: &types.SystemError{}, 151 }, 152 }), 153 target: asPtr[*types.SystemError](), 154 expectedTarget: asPtr(&types.SystemError{}), 155 expectedOkay: true, 156 expectedLocalizedMessage: "", 157 }, 158 { 159 name: "err is wrapped nil error and task.Error with fault", 160 err: unwrappableErrSlice{ 161 nil, 162 task.Error{ 163 LocalizedMethodFault: &types.LocalizedMethodFault{ 164 Fault: &types.SystemError{}, 165 }, 166 }, 167 }, 168 target: asPtr[*types.SystemError](), 169 expectedTarget: asPtr(&types.SystemError{}), 170 expectedOkay: true, 171 expectedLocalizedMessage: "", 172 }, 173 { 174 name: "err is types.BaseMethodFault", 175 err: &types.SystemError{}, 176 target: asPtr[*types.SystemError](), 177 expectedTarget: asPtr(&types.SystemError{}), 178 expectedOkay: true, 179 expectedLocalizedMessage: "", 180 }, 181 { 182 name: "err is types.BaseMethodFault that returns nil", 183 err: nilBaseMethodFault{}, 184 target: asPtr[*types.SystemError](), 185 expectedTarget: asPtr[*types.SystemError](), 186 expectedLocalizedMessage: "", 187 }, 188 { 189 name: "err is asFaulter that returns true and fault", 190 err: asFaulter{okay: true, msg: "Hello, world.", val: &types.SystemError{}}, 191 target: asPtr[*types.SystemError](), 192 expectedTarget: asPtr(&types.SystemError{}), 193 expectedOkay: true, 194 expectedLocalizedMessage: "Hello, world.", 195 }, 196 { 197 name: "err is asFaulter that returns false", 198 err: asFaulter{okay: false, msg: "Hello, world."}, 199 target: asPtr[*types.SystemError](), 200 expectedTarget: asPtr[*types.SystemError](), 201 expectedLocalizedMessage: "", 202 }, 203 { 204 name: "err is *types.LocalizedMethodFault", 205 err: &types.LocalizedMethodFault{ 206 LocalizedMessage: "fake", 207 Fault: &types.SystemError{}, 208 }, 209 target: asPtr[*types.SystemError](), 210 expectedTarget: asPtr(&types.SystemError{}), 211 expectedOkay: true, 212 expectedLocalizedMessage: "fake", 213 }, 214 { 215 name: "err is *types.LocalizedMethodFault w nil fault", 216 err: &types.LocalizedMethodFault{ 217 LocalizedMessage: "fake", 218 Fault: nil, 219 }, 220 target: asPtr[*types.SystemError](), 221 expectedTarget: asPtr[*types.SystemError](), 222 expectedOkay: false, 223 }, 224 { 225 name: "err is task.Error with nested fault", 226 err: &types.LocalizedMethodFault{ 227 LocalizedMessage: "fake1", 228 Fault: &types.SystemError{ 229 RuntimeFault: types.RuntimeFault{ 230 MethodFault: types.MethodFault{ 231 FaultCause: &types.LocalizedMethodFault{ 232 LocalizedMessage: "fake2", 233 Fault: &types.InvalidVmConfig{}, 234 }, 235 }, 236 }, 237 }, 238 }, 239 target: asPtr[*types.InvalidVmConfig](), 240 expectedTarget: asPtr(&types.InvalidVmConfig{}), 241 expectedOkay: true, 242 expectedLocalizedMessage: "fake2", 243 }, 244 { 245 name: "err is soap fault", 246 err: soap.WrapSoapFault(&soap.Fault{ 247 Detail: struct { 248 Fault types.AnyType "xml:\",any,typeattr\"" 249 }{ 250 Fault: &types.SystemError{ 251 RuntimeFault: types.RuntimeFault{ 252 MethodFault: types.MethodFault{ 253 FaultCause: &types.LocalizedMethodFault{ 254 Fault: &types.InvalidVmConfig{}, 255 }, 256 }, 257 }, 258 }, 259 }, 260 }), 261 target: asPtr[*types.InvalidVmConfig](), 262 expectedTarget: asPtr(&types.InvalidVmConfig{}), 263 expectedOkay: true, 264 }, 265 { 266 name: "err is soap fault with nil vim fault", 267 err: soap.WrapSoapFault(&soap.Fault{ 268 Detail: struct { 269 Fault types.AnyType "xml:\",any,typeattr\"" 270 }{ 271 Fault: nil, 272 }, 273 }), 274 target: asPtr[*types.InvalidVmConfig](), 275 expectedTarget: asPtr[*types.InvalidVmConfig](), 276 expectedOkay: false, 277 }, 278 { 279 name: "err is vim fault", 280 err: soap.WrapVimFault(&types.SystemError{ 281 RuntimeFault: types.RuntimeFault{ 282 MethodFault: types.MethodFault{ 283 FaultCause: &types.LocalizedMethodFault{ 284 Fault: &types.InvalidVmConfig{}, 285 }, 286 }, 287 }, 288 }), 289 target: asPtr[*types.InvalidVmConfig](), 290 expectedTarget: asPtr(&types.InvalidVmConfig{}), 291 expectedOkay: true, 292 }, 293 { 294 name: "err is vim fault with nil vim fault", 295 err: soap.WrapVimFault(&types.SystemError{ 296 RuntimeFault: types.RuntimeFault{ 297 MethodFault: types.MethodFault{ 298 FaultCause: &types.LocalizedMethodFault{ 299 Fault: nil, 300 }, 301 }, 302 }, 303 }), 304 target: asPtr[*types.InvalidVmConfig](), 305 expectedTarget: asPtr[*types.InvalidVmConfig](), 306 expectedOkay: false, 307 }, 308 } 309 310 for i := range testCases { 311 tc := testCases[i] 312 t.Run(tc.name, func(t *testing.T) { 313 t.Parallel() 314 315 var ( 316 okay bool 317 localizedMessage string 318 ) 319 320 if tc.expectedPanic != nil { 321 assert.PanicsWithValue( 322 t, 323 tc.expectedPanic, 324 func() { 325 localizedMessage, okay = fault.As(tc.err, tc.target) 326 }) 327 } else { 328 localizedMessage, okay = fault.As(tc.err, tc.target) 329 } 330 331 assert.Equal(t, tc.expectedOkay, okay) 332 assert.Equal(t, tc.expectedLocalizedMessage, localizedMessage) 333 assert.Equal(t, tc.expectedTarget, tc.target) 334 }) 335 } 336 } 337 338 type faultResult struct { 339 fault types.BaseMethodFault 340 localizedMessage string 341 localizableMessages []types.LocalizableMessage 342 } 343 344 type testHarness struct { 345 isNil bool 346 numCalls int 347 numCallsToReturnTrue int 348 results []faultResult 349 } 350 351 func (h *testHarness) Callback( 352 fault types.BaseMethodFault, 353 localizedMessage string, 354 localizableMessages []types.LocalizableMessage) bool { 355 356 h.numCalls++ 357 358 rvFault := reflect.ValueOf(fault) 359 if rvFault.Kind() == reflect.Pointer { 360 rvFault = rvFault.Elem() 361 } 362 emptyFault := reflect.New(rvFault.Type()) 363 364 h.results = append(h.results, faultResult{ 365 fault: emptyFault.Interface().(types.BaseMethodFault), 366 localizedMessage: localizedMessage, 367 localizableMessages: localizableMessages, 368 }) 369 return h.numCallsToReturnTrue == h.numCalls 370 } 371 372 func TestIn(t *testing.T) { 373 const unsupported = "fault: err must implement error, types.BaseMethodFault, or types.HasLocalizedMethodFault" 374 375 testCases := []struct { 376 name string 377 err any 378 callback *testHarness 379 expectedNumCalls int 380 expectedResults []faultResult 381 expectedPanic any 382 }{ 383 { 384 name: "err is nil", 385 err: nil, 386 callback: &testHarness{}, 387 expectedNumCalls: 0, 388 expectedPanic: unsupported, 389 }, 390 { 391 name: "callback is nil", 392 err: errors.New("error"), 393 callback: &testHarness{isNil: true}, 394 expectedNumCalls: 0, 395 expectedPanic: "fault: onFaultFn must not be nil", 396 }, 397 { 398 name: "err is unsupported", 399 err: struct{}{}, 400 callback: &testHarness{}, 401 expectedNumCalls: 0, 402 expectedPanic: unsupported, 403 }, 404 { 405 name: "err is unsupported but still an err", 406 err: errors.New("error"), 407 callback: &testHarness{}, 408 expectedNumCalls: 0, 409 }, 410 { 411 name: "error is task.Error", 412 err: task.Error{}, 413 callback: &testHarness{}, 414 expectedNumCalls: 0, 415 }, 416 { 417 name: "error is task.Error with localized message and fault", 418 err: task.Error{ 419 LocalizedMethodFault: &types.LocalizedMethodFault{ 420 LocalizedMessage: "fake", 421 Fault: &types.SystemError{}, 422 }, 423 }, 424 callback: &testHarness{}, 425 expectedNumCalls: 1, 426 expectedResults: []faultResult{ 427 { 428 localizedMessage: "fake", 429 fault: &types.SystemError{}, 430 }, 431 }, 432 }, 433 { 434 name: "error is types.SystemError", 435 err: &types.SystemError{}, 436 callback: &testHarness{}, 437 expectedNumCalls: 1, 438 expectedResults: []faultResult{ 439 { 440 fault: &types.SystemError{}, 441 }, 442 }, 443 }, 444 { 445 name: "error is task.Error with a localized message but no fault", 446 err: task.Error{ 447 LocalizedMethodFault: &types.LocalizedMethodFault{ 448 LocalizedMessage: "fake", 449 }, 450 }, 451 callback: &testHarness{}, 452 expectedNumCalls: 0, 453 }, 454 { 455 name: "error is multiple, wrapped errors", 456 err: unwrappableErrSlice{ 457 nil, 458 task.Error{ 459 LocalizedMethodFault: &types.LocalizedMethodFault{ 460 LocalizedMessage: "fake", 461 Fault: &types.SystemError{}, 462 }, 463 }, 464 }, 465 callback: &testHarness{}, 466 expectedNumCalls: 1, 467 expectedResults: []faultResult{ 468 { 469 localizedMessage: "fake", 470 fault: &types.SystemError{}, 471 }, 472 }, 473 }, 474 { 475 name: "error has nested task.Error with localized message and fault", 476 err: fmt.Errorf( 477 "error 1: %w", 478 fmt.Errorf( 479 "error 2: %w", 480 task.Error{ 481 LocalizedMethodFault: &types.LocalizedMethodFault{ 482 LocalizedMessage: "fake", 483 Fault: &types.SystemError{}, 484 }, 485 }, 486 ), 487 ), 488 callback: &testHarness{}, 489 expectedNumCalls: 1, 490 expectedResults: []faultResult{ 491 { 492 localizedMessage: "fake", 493 fault: &types.SystemError{}, 494 }, 495 }, 496 }, 497 { 498 name: "error is task.Error with localized message and fault with localizable messages", 499 err: task.Error{ 500 LocalizedMethodFault: &types.LocalizedMethodFault{ 501 LocalizedMessage: "fake", 502 Fault: &types.SystemError{ 503 RuntimeFault: types.RuntimeFault{ 504 MethodFault: types.MethodFault{ 505 FaultMessage: []types.LocalizableMessage{ 506 { 507 Key: "fake.id", 508 Message: "fake", 509 }, 510 }, 511 }, 512 }, 513 }, 514 }, 515 }, 516 callback: &testHarness{}, 517 expectedNumCalls: 1, 518 expectedResults: []faultResult{ 519 { 520 fault: &types.SystemError{}, 521 localizedMessage: "fake", 522 localizableMessages: []types.LocalizableMessage{ 523 { 524 Key: "fake.id", 525 Message: "fake", 526 }, 527 }, 528 }, 529 }, 530 }, 531 { 532 name: "error is task.Error with nested faults", 533 err: task.Error{ 534 LocalizedMethodFault: &types.LocalizedMethodFault{ 535 LocalizedMessage: "fake1", 536 Fault: &types.SystemError{ 537 RuntimeFault: types.RuntimeFault{ 538 MethodFault: types.MethodFault{ 539 FaultMessage: []types.LocalizableMessage{ 540 { 541 Key: "fake1.id", 542 Message: "fake1", 543 }, 544 }, 545 FaultCause: &types.LocalizedMethodFault{ 546 LocalizedMessage: "fake2", 547 Fault: &types.NotSupported{ 548 RuntimeFault: types.RuntimeFault{ 549 MethodFault: types.MethodFault{ 550 FaultMessage: []types.LocalizableMessage{ 551 { 552 Key: "fake2.id", 553 Message: "fake2", 554 }, 555 }, 556 }, 557 }, 558 }, 559 }, 560 }, 561 }, 562 }, 563 }, 564 }, 565 callback: &testHarness{}, 566 expectedNumCalls: 2, 567 expectedResults: []faultResult{ 568 { 569 fault: &types.SystemError{}, 570 localizedMessage: "fake1", 571 localizableMessages: []types.LocalizableMessage{ 572 { 573 Key: "fake1.id", 574 Message: "fake1", 575 }, 576 }, 577 }, 578 { 579 fault: &types.NotSupported{}, 580 localizedMessage: "fake2", 581 localizableMessages: []types.LocalizableMessage{ 582 { 583 Key: "fake2.id", 584 Message: "fake2", 585 }, 586 }, 587 }, 588 }, 589 }, 590 { 591 name: "error is task.Error with nested faults but returns after single fault", 592 err: task.Error{ 593 LocalizedMethodFault: &types.LocalizedMethodFault{ 594 LocalizedMessage: "fake1", 595 Fault: &types.SystemError{ 596 RuntimeFault: types.RuntimeFault{ 597 MethodFault: types.MethodFault{ 598 FaultMessage: []types.LocalizableMessage{ 599 { 600 Key: "fake1.id", 601 Message: "fake1", 602 }, 603 }, 604 FaultCause: &types.LocalizedMethodFault{ 605 LocalizedMessage: "fake2", 606 Fault: &types.NotSupported{ 607 RuntimeFault: types.RuntimeFault{ 608 MethodFault: types.MethodFault{ 609 FaultMessage: []types.LocalizableMessage{ 610 { 611 Key: "fake2.id", 612 Message: "fake2", 613 }, 614 }, 615 }, 616 }, 617 }, 618 }, 619 }, 620 }, 621 }, 622 }, 623 }, 624 callback: &testHarness{numCallsToReturnTrue: 1}, 625 expectedNumCalls: 1, 626 expectedResults: []faultResult{ 627 { 628 fault: &types.SystemError{}, 629 localizedMessage: "fake1", 630 localizableMessages: []types.LocalizableMessage{ 631 { 632 Key: "fake1.id", 633 Message: "fake1", 634 }, 635 }, 636 }, 637 }, 638 }, 639 { 640 name: "err is soap fault", 641 err: soap.WrapSoapFault(&soap.Fault{ 642 Detail: struct { 643 Fault types.AnyType "xml:\",any,typeattr\"" 644 }{ 645 Fault: &types.SystemError{}, 646 }, 647 }), 648 callback: &testHarness{numCallsToReturnTrue: 1}, 649 expectedNumCalls: 1, 650 expectedResults: []faultResult{ 651 { 652 fault: &types.SystemError{}, 653 }, 654 }, 655 }, 656 { 657 name: "err is vim fault", 658 err: soap.WrapVimFault(&types.SystemError{}), 659 callback: &testHarness{numCallsToReturnTrue: 1}, 660 expectedNumCalls: 1, 661 expectedResults: []faultResult{ 662 { 663 fault: &types.SystemError{}, 664 }, 665 }, 666 }, 667 } 668 669 for i := range testCases { 670 tc := testCases[i] 671 t.Run(tc.name, func(t *testing.T) { 672 t.Parallel() 673 674 var callback fault.OnFaultFn 675 676 if !tc.callback.isNil { 677 callback = tc.callback.Callback 678 } 679 680 if tc.expectedPanic != nil { 681 assert.PanicsWithValue( 682 t, 683 tc.expectedPanic, 684 func() { 685 fault.In(tc.err, callback) 686 }) 687 } else { 688 fault.In(tc.err, callback) 689 } 690 691 assert.Equal(t, tc.expectedNumCalls, tc.callback.numCalls) 692 assert.Equal(t, tc.expectedResults, tc.callback.results) 693 }) 694 } 695 } 696 697 func TestIs(t *testing.T) { 698 699 testCases := []struct { 700 name string 701 err any 702 target types.BaseMethodFault 703 expectedOkay bool 704 expectedPanic any 705 }{ 706 { 707 name: "err is nil", 708 err: nil, 709 target: &types.SystemError{}, 710 expectedOkay: false, 711 }, 712 { 713 name: "target is nil", 714 err: &types.SystemError{}, 715 target: nil, 716 expectedOkay: false, 717 }, 718 { 719 name: "target and error are nil", 720 err: nil, 721 target: nil, 722 expectedOkay: true, 723 }, 724 { 725 name: "err is not supported", 726 err: struct{}{}, 727 expectedOkay: false, 728 }, 729 { 730 name: "err and target are same value type", 731 err: nilBaseMethodFault{}, 732 target: nilBaseMethodFault{}, 733 expectedOkay: true, 734 }, 735 { 736 name: "target implements IsFault", 737 err: isFaulter(true), 738 target: &types.SystemError{}, 739 expectedOkay: true, 740 }, 741 { 742 name: "err is *LocalizedMethodFault with nil fault", 743 err: &types.LocalizedMethodFault{}, 744 target: &types.SystemError{}, 745 expectedOkay: false, 746 }, 747 { 748 name: "err is *LocalizedMethodFault with SystemError fault", 749 err: &types.LocalizedMethodFault{ 750 Fault: &types.SystemError{}, 751 }, 752 target: &types.SystemError{}, 753 expectedOkay: true, 754 }, 755 { 756 name: "err is *LocalizedMethodFault with InvalidVmConfig fault", 757 err: &types.LocalizedMethodFault{ 758 Fault: &types.SystemError{}, 759 }, 760 target: &types.InvalidVmConfig{}, 761 expectedOkay: false, 762 }, 763 { 764 name: "err is task.Error with nested InvalidVmConfig fault", 765 err: task.Error{ 766 LocalizedMethodFault: &types.LocalizedMethodFault{ 767 Fault: &types.SystemError{ 768 RuntimeFault: types.RuntimeFault{ 769 MethodFault: types.MethodFault{ 770 FaultCause: &types.LocalizedMethodFault{ 771 Fault: &types.InvalidVmConfig{}, 772 }, 773 }, 774 }, 775 }, 776 }, 777 }, 778 target: &types.InvalidVmConfig{}, 779 expectedOkay: true, 780 }, 781 { 782 name: "err is *LocalizedMethodFault with nested InvalidVmConfig fault", 783 err: &types.LocalizedMethodFault{ 784 Fault: &types.SystemError{ 785 RuntimeFault: types.RuntimeFault{ 786 MethodFault: types.MethodFault{ 787 FaultCause: &types.LocalizedMethodFault{ 788 Fault: &types.InvalidVmConfig{}, 789 }, 790 }, 791 }, 792 }, 793 }, 794 target: &types.InvalidVmConfig{}, 795 expectedOkay: true, 796 }, 797 { 798 name: "err is *LocalizedMethodFault with nested nil fault", 799 err: &types.LocalizedMethodFault{ 800 Fault: &types.SystemError{ 801 RuntimeFault: types.RuntimeFault{ 802 MethodFault: types.MethodFault{ 803 FaultCause: &types.LocalizedMethodFault{ 804 Fault: nilBaseMethodFault{}, 805 }, 806 }, 807 }, 808 }, 809 }, 810 target: &types.InvalidVmConfig{}, 811 expectedOkay: false, 812 }, 813 { 814 name: "err is wrapped task.Error with nested InvalidVmConfig fault", 815 err: fmt.Errorf("my error: %w", task.Error{ 816 LocalizedMethodFault: &types.LocalizedMethodFault{ 817 Fault: &types.SystemError{ 818 RuntimeFault: types.RuntimeFault{ 819 MethodFault: types.MethodFault{ 820 FaultCause: &types.LocalizedMethodFault{ 821 Fault: &types.InvalidVmConfig{}, 822 }, 823 }, 824 }, 825 }, 826 }, 827 }), 828 target: &types.InvalidVmConfig{}, 829 expectedOkay: true, 830 }, 831 { 832 name: "err is wrapped nil error", 833 err: nilErrWrapper{}, 834 target: &types.InvalidVmConfig{}, 835 expectedOkay: false, 836 }, 837 { 838 name: "err is wrapped error slice with expected value", 839 err: unwrappableErrSlice{ 840 nil, 841 task.Error{ 842 LocalizedMethodFault: &types.LocalizedMethodFault{ 843 Fault: &types.SystemError{ 844 RuntimeFault: types.RuntimeFault{ 845 MethodFault: types.MethodFault{ 846 FaultCause: &types.LocalizedMethodFault{ 847 Fault: &types.InvalidVmConfig{}, 848 }, 849 }, 850 }, 851 }, 852 }, 853 }, 854 }, 855 target: &types.InvalidVmConfig{}, 856 expectedOkay: true, 857 }, 858 { 859 name: "err is wrapped error slice sans expected value", 860 err: unwrappableErrSlice{ 861 nil, 862 task.Error{ 863 LocalizedMethodFault: &types.LocalizedMethodFault{ 864 Fault: &types.SystemError{ 865 RuntimeFault: types.RuntimeFault{ 866 MethodFault: types.MethodFault{ 867 FaultCause: &types.LocalizedMethodFault{ 868 Fault: &types.SystemError{}, 869 }, 870 }, 871 }, 872 }, 873 }, 874 }, 875 }, 876 target: &types.InvalidVmConfig{}, 877 expectedOkay: false, 878 }, 879 { 880 name: "err is soap fault", 881 err: soap.WrapSoapFault(&soap.Fault{ 882 Detail: struct { 883 Fault types.AnyType "xml:\",any,typeattr\"" 884 }{ 885 Fault: &types.SystemError{ 886 RuntimeFault: types.RuntimeFault{ 887 MethodFault: types.MethodFault{ 888 FaultCause: &types.LocalizedMethodFault{ 889 Fault: &types.InvalidVmConfig{}, 890 }, 891 }, 892 }, 893 }, 894 }, 895 }), 896 target: &types.InvalidVmConfig{}, 897 expectedOkay: true, 898 }, 899 { 900 name: "err is soap fault with nil vim fault", 901 err: soap.WrapSoapFault(&soap.Fault{ 902 Detail: struct { 903 Fault types.AnyType "xml:\",any,typeattr\"" 904 }{ 905 Fault: nil, 906 }, 907 }), 908 target: &types.InvalidVmConfig{}, 909 expectedOkay: false, 910 }, 911 { 912 name: "err is vim fault", 913 err: soap.WrapVimFault(&types.SystemError{ 914 RuntimeFault: types.RuntimeFault{ 915 MethodFault: types.MethodFault{ 916 FaultCause: &types.LocalizedMethodFault{ 917 Fault: &types.InvalidVmConfig{}, 918 }, 919 }, 920 }, 921 }), 922 target: &types.InvalidVmConfig{}, 923 expectedOkay: true, 924 }, 925 { 926 name: "err is vim fault with nil vim fault", 927 err: soap.WrapVimFault(&types.SystemError{ 928 RuntimeFault: types.RuntimeFault{ 929 MethodFault: types.MethodFault{ 930 FaultCause: &types.LocalizedMethodFault{ 931 Fault: nil, 932 }, 933 }, 934 }, 935 }), 936 target: &types.InvalidVmConfig{}, 937 expectedOkay: false, 938 }, 939 } 940 941 for i := range testCases { 942 tc := testCases[i] 943 t.Run(tc.name, func(t *testing.T) { 944 t.Parallel() 945 946 var okay bool 947 948 if tc.expectedPanic != nil { 949 assert.PanicsWithValue( 950 t, 951 tc.expectedPanic, 952 func() { 953 okay = fault.Is(tc.err, tc.target) 954 }) 955 } else { 956 okay = fault.Is(tc.err, tc.target) 957 } 958 959 assert.Equal(t, tc.expectedOkay, okay) 960 }) 961 } 962 }