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