github.com/spotmaxtech/k8s-apimachinery-v0260@v0.0.1/pkg/api/errors/errors_test.go (about)

     1  /*
     2  Copyright 2014 The Kubernetes Authors.
     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 errors
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"net/http"
    23  	"reflect"
    24  	"testing"
    25  
    26  	metav1 "github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/apis/meta/v1"
    27  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime"
    28  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/runtime/schema"
    29  	"github.com/spotmaxtech/k8s-apimachinery-v0260/pkg/util/validation/field"
    30  )
    31  
    32  func resource(resource string) schema.GroupResource {
    33  	return schema.GroupResource{Group: "", Resource: resource}
    34  }
    35  func kind(kind string) schema.GroupKind {
    36  	return schema.GroupKind{Group: "", Kind: kind}
    37  }
    38  
    39  func TestErrorNew(t *testing.T) {
    40  	err := NewAlreadyExists(resource("tests"), "1")
    41  	if !IsAlreadyExists(err) {
    42  		t.Errorf("expected to be %s", metav1.StatusReasonAlreadyExists)
    43  	}
    44  	if IsConflict(err) {
    45  		t.Errorf("expected to not be %s", metav1.StatusReasonConflict)
    46  	}
    47  	if IsNotFound(err) {
    48  		t.Errorf(fmt.Sprintf("expected to not be %s", metav1.StatusReasonNotFound))
    49  	}
    50  	if IsInvalid(err) {
    51  		t.Errorf("expected to not be %s", metav1.StatusReasonInvalid)
    52  	}
    53  	if IsBadRequest(err) {
    54  		t.Errorf("expected to not be %s", metav1.StatusReasonBadRequest)
    55  	}
    56  	if IsForbidden(err) {
    57  		t.Errorf("expected to not be %s", metav1.StatusReasonForbidden)
    58  	}
    59  	if IsServerTimeout(err) {
    60  		t.Errorf("expected to not be %s", metav1.StatusReasonServerTimeout)
    61  	}
    62  	if IsMethodNotSupported(err) {
    63  		t.Errorf("expected to not be %s", metav1.StatusReasonMethodNotAllowed)
    64  	}
    65  
    66  	if !IsConflict(NewConflict(resource("tests"), "2", errors.New("message"))) {
    67  		t.Errorf("expected to be %s", metav1.StatusReasonAlreadyExists)
    68  	}
    69  	if !IsNotFound(NewNotFound(resource("tests"), "3")) {
    70  		t.Errorf("expected to be %s", metav1.StatusReasonNotFound)
    71  	}
    72  	if !IsInvalid(NewInvalid(kind("Test"), "2", nil)) {
    73  		t.Errorf("expected to be %s", metav1.StatusReasonInvalid)
    74  	}
    75  	if !IsBadRequest(NewBadRequest("reason")) {
    76  		t.Errorf("expected to be %s", metav1.StatusReasonBadRequest)
    77  	}
    78  	if !IsForbidden(NewForbidden(resource("tests"), "2", errors.New("reason"))) {
    79  		t.Errorf("expected to be %s", metav1.StatusReasonForbidden)
    80  	}
    81  	if !IsUnauthorized(NewUnauthorized("reason")) {
    82  		t.Errorf("expected to be %s", metav1.StatusReasonUnauthorized)
    83  	}
    84  	if !IsServerTimeout(NewServerTimeout(resource("tests"), "reason", 0)) {
    85  		t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
    86  	}
    87  	if !IsMethodNotSupported(NewMethodNotSupported(resource("foos"), "delete")) {
    88  		t.Errorf("expected to be %s", metav1.StatusReasonMethodNotAllowed)
    89  	}
    90  
    91  	if !IsAlreadyExists(NewGenerateNameConflict(resource("tests"), "3", 1)) {
    92  		t.Errorf("expected to be %s", metav1.StatusReasonAlreadyExists)
    93  	}
    94  	if time, ok := SuggestsClientDelay(NewGenerateNameConflict(resource("tests"), "3", 1)); time != 1 || !ok {
    95  		t.Errorf("unexpected %d", time)
    96  	}
    97  
    98  	if time, ok := SuggestsClientDelay(NewServerTimeout(resource("tests"), "doing something", 10)); time != 10 || !ok {
    99  		t.Errorf("unexpected %d", time)
   100  	}
   101  	if time, ok := SuggestsClientDelay(NewServerTimeout(resource("tests"), "doing something", 0)); time != 0 || !ok {
   102  		t.Errorf("unexpected %d", time)
   103  	}
   104  	if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok {
   105  		t.Errorf("unexpected %d", time)
   106  	}
   107  	if time, ok := SuggestsClientDelay(NewTooManyRequests("doing something", 10)); time != 10 || !ok {
   108  		t.Errorf("unexpected %d", time)
   109  	}
   110  	if time, ok := SuggestsClientDelay(NewTooManyRequests("doing something", 1)); time != 1 || !ok {
   111  		t.Errorf("unexpected %d", time)
   112  	}
   113  	if time, ok := SuggestsClientDelay(NewGenericServerResponse(429, "get", resource("tests"), "test", "doing something", 10, true)); time != 10 || !ok {
   114  		t.Errorf("unexpected %d", time)
   115  	}
   116  	if time, ok := SuggestsClientDelay(NewGenericServerResponse(500, "get", resource("tests"), "test", "doing something", 10, true)); time != 10 || !ok {
   117  		t.Errorf("unexpected %d", time)
   118  	}
   119  	if time, ok := SuggestsClientDelay(NewGenericServerResponse(429, "get", resource("tests"), "test", "doing something", 0, true)); time != 0 || ok {
   120  		t.Errorf("unexpected %d", time)
   121  	}
   122  }
   123  
   124  func TestNewInvalid(t *testing.T) {
   125  	testCases := []struct {
   126  		Err     *field.Error
   127  		Details *metav1.StatusDetails
   128  		Msg     string
   129  	}{
   130  		{
   131  			field.Duplicate(field.NewPath("field[0].name"), "bar"),
   132  			&metav1.StatusDetails{
   133  				Kind: "Kind",
   134  				Name: "name",
   135  				Causes: []metav1.StatusCause{{
   136  					Type:  metav1.CauseTypeFieldValueDuplicate,
   137  					Field: "field[0].name",
   138  				}},
   139  			},
   140  			`Kind "name" is invalid: field[0].name: Duplicate value: "bar"`,
   141  		},
   142  		{
   143  			field.Invalid(field.NewPath("field[0].name"), "bar", "detail"),
   144  			&metav1.StatusDetails{
   145  				Kind: "Kind",
   146  				Name: "name",
   147  				Causes: []metav1.StatusCause{{
   148  					Type:  metav1.CauseTypeFieldValueInvalid,
   149  					Field: "field[0].name",
   150  				}},
   151  			},
   152  			`Kind "name" is invalid: field[0].name: Invalid value: "bar": detail`,
   153  		},
   154  		{
   155  			field.NotFound(field.NewPath("field[0].name"), "bar"),
   156  			&metav1.StatusDetails{
   157  				Kind: "Kind",
   158  				Name: "name",
   159  				Causes: []metav1.StatusCause{{
   160  					Type:  metav1.CauseTypeFieldValueNotFound,
   161  					Field: "field[0].name",
   162  				}},
   163  			},
   164  			`Kind "name" is invalid: field[0].name: Not found: "bar"`,
   165  		},
   166  		{
   167  			field.NotSupported(field.NewPath("field[0].name"), "bar", nil),
   168  			&metav1.StatusDetails{
   169  				Kind: "Kind",
   170  				Name: "name",
   171  				Causes: []metav1.StatusCause{{
   172  					Type:  metav1.CauseTypeFieldValueNotSupported,
   173  					Field: "field[0].name",
   174  				}},
   175  			},
   176  			`Kind "name" is invalid: field[0].name: Unsupported value: "bar"`,
   177  		},
   178  		{
   179  			field.Required(field.NewPath("field[0].name"), ""),
   180  			&metav1.StatusDetails{
   181  				Kind: "Kind",
   182  				Name: "name",
   183  				Causes: []metav1.StatusCause{{
   184  					Type:  metav1.CauseTypeFieldValueRequired,
   185  					Field: "field[0].name",
   186  				}},
   187  			},
   188  			`Kind "name" is invalid: field[0].name: Required value`,
   189  		},
   190  		{
   191  			nil,
   192  			&metav1.StatusDetails{
   193  				Kind:   "Kind",
   194  				Name:   "name",
   195  				Causes: []metav1.StatusCause{},
   196  			},
   197  			`Kind "name" is invalid`,
   198  		},
   199  	}
   200  	for i, testCase := range testCases {
   201  		vErr, expected := testCase.Err, testCase.Details
   202  		if vErr != nil && expected != nil {
   203  			expected.Causes[0].Message = vErr.ErrorBody()
   204  		}
   205  		var errList field.ErrorList
   206  		if vErr != nil {
   207  			errList = append(errList, vErr)
   208  		}
   209  		err := NewInvalid(kind("Kind"), "name", errList)
   210  		status := err.ErrStatus
   211  		if status.Code != 422 || status.Reason != metav1.StatusReasonInvalid {
   212  			t.Errorf("%d: unexpected status: %#v", i, status)
   213  		}
   214  		if !reflect.DeepEqual(expected, status.Details) {
   215  			t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details)
   216  		}
   217  		if testCase.Msg != status.Message {
   218  			t.Errorf("%d: expected\n%s\ngot\n%s", i, testCase.Msg, status.Message)
   219  		}
   220  	}
   221  }
   222  
   223  func TestReasonForError(t *testing.T) {
   224  	if e, a := metav1.StatusReasonUnknown, ReasonForError(nil); e != a {
   225  		t.Errorf("unexpected reason type: %#v", a)
   226  	}
   227  }
   228  
   229  type TestType struct{}
   230  
   231  func (obj *TestType) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
   232  func (obj *TestType) DeepCopyObject() runtime.Object {
   233  	if obj == nil {
   234  		return nil
   235  	}
   236  	clone := *obj
   237  	return &clone
   238  }
   239  
   240  func TestFromObject(t *testing.T) {
   241  	table := []struct {
   242  		obj     runtime.Object
   243  		message string
   244  	}{
   245  		{&metav1.Status{Message: "foobar"}, "foobar"},
   246  		{&TestType{}, "unexpected object: &{}"},
   247  	}
   248  
   249  	for _, item := range table {
   250  		if e, a := item.message, FromObject(item.obj).Error(); e != a {
   251  			t.Errorf("Expected %v, got %v", e, a)
   252  		}
   253  	}
   254  }
   255  
   256  func TestReasonForErrorSupportsWrappedErrors(t *testing.T) {
   257  	testCases := []struct {
   258  		name           string
   259  		err            error
   260  		expectedReason metav1.StatusReason
   261  	}{
   262  		{
   263  			name:           "Direct match",
   264  			err:            &StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonUnauthorized}},
   265  			expectedReason: metav1.StatusReasonUnauthorized,
   266  		},
   267  		{
   268  			name:           "No match",
   269  			err:            errors.New("some other error"),
   270  			expectedReason: metav1.StatusReasonUnknown,
   271  		},
   272  		{
   273  			name:           "Nested match",
   274  			err:            fmt.Errorf("wrapping: %w", fmt.Errorf("some more: %w", &StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonAlreadyExists}})),
   275  			expectedReason: metav1.StatusReasonAlreadyExists,
   276  		},
   277  		{
   278  			name:           "Nested, no match",
   279  			err:            fmt.Errorf("wrapping: %w", fmt.Errorf("some more: %w", errors.New("hello"))),
   280  			expectedReason: metav1.StatusReasonUnknown,
   281  		},
   282  		{
   283  			name:           "Nil",
   284  			expectedReason: metav1.StatusReasonUnknown,
   285  		},
   286  	}
   287  
   288  	for _, tc := range testCases {
   289  		t.Run(tc.name, func(t *testing.T) {
   290  			if result := ReasonForError(tc.err); result != tc.expectedReason {
   291  				t.Errorf("expected reason: %q, but got known reason: %q", tc.expectedReason, result)
   292  			}
   293  		})
   294  	}
   295  }
   296  
   297  func TestIsTooManyRequestsSupportsWrappedErrors(t *testing.T) {
   298  	testCases := []struct {
   299  		name        string
   300  		err         error
   301  		expectMatch bool
   302  	}{
   303  		{
   304  			name:        "Direct match via status reason",
   305  			err:         &StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonTooManyRequests}},
   306  			expectMatch: true,
   307  		},
   308  		{
   309  			name:        "Direct match via status code",
   310  			err:         &StatusError{ErrStatus: metav1.Status{Code: http.StatusTooManyRequests}},
   311  			expectMatch: true,
   312  		},
   313  		{
   314  			name:        "No match",
   315  			err:         &StatusError{},
   316  			expectMatch: false,
   317  		},
   318  		{
   319  			name:        "Nested match via status reason",
   320  			err:         fmt.Errorf("Wrapping: %w", &StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonTooManyRequests}}),
   321  			expectMatch: true,
   322  		},
   323  		{
   324  			name:        "Nested match via status code",
   325  			err:         fmt.Errorf("Wrapping: %w", &StatusError{ErrStatus: metav1.Status{Code: http.StatusTooManyRequests}}),
   326  			expectMatch: true,
   327  		},
   328  		{
   329  			name:        "Nested,no match",
   330  			err:         fmt.Errorf("Wrapping: %w", &StatusError{ErrStatus: metav1.Status{Code: http.StatusNotFound}}),
   331  			expectMatch: false,
   332  		},
   333  		{
   334  			name:        "Nil",
   335  			expectMatch: false,
   336  		},
   337  	}
   338  
   339  	for _, tc := range testCases {
   340  		if result := IsTooManyRequests(tc.err); result != tc.expectMatch {
   341  			t.Errorf("Expect match %t, got match %t", tc.expectMatch, result)
   342  		}
   343  	}
   344  }
   345  func TestIsRequestEntityTooLargeErrorSupportsWrappedErrors(t *testing.T) {
   346  	testCases := []struct {
   347  		name        string
   348  		err         error
   349  		expectMatch bool
   350  	}{
   351  		{
   352  			name:        "Direct match via status reason",
   353  			err:         &StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonRequestEntityTooLarge}},
   354  			expectMatch: true,
   355  		},
   356  		{
   357  			name:        "Direct match via status code",
   358  			err:         &StatusError{ErrStatus: metav1.Status{Code: http.StatusRequestEntityTooLarge}},
   359  			expectMatch: true,
   360  		},
   361  		{
   362  			name:        "No match",
   363  			err:         &StatusError{},
   364  			expectMatch: false,
   365  		},
   366  		{
   367  			name:        "Nested match via status reason",
   368  			err:         fmt.Errorf("Wrapping: %w", &StatusError{ErrStatus: metav1.Status{Reason: metav1.StatusReasonRequestEntityTooLarge}}),
   369  			expectMatch: true,
   370  		},
   371  		{
   372  			name:        "Nested match via status code",
   373  			err:         fmt.Errorf("Wrapping: %w", &StatusError{ErrStatus: metav1.Status{Code: http.StatusRequestEntityTooLarge}}),
   374  			expectMatch: true,
   375  		},
   376  		{
   377  			name:        "Nested,no match",
   378  			err:         fmt.Errorf("Wrapping: %w", &StatusError{ErrStatus: metav1.Status{Code: http.StatusNotFound}}),
   379  			expectMatch: false,
   380  		},
   381  		{
   382  			name:        "Nil",
   383  			expectMatch: false,
   384  		},
   385  	}
   386  
   387  	for _, tc := range testCases {
   388  		if result := IsRequestEntityTooLargeError(tc.err); result != tc.expectMatch {
   389  			t.Errorf("Expect match %t, got match %t", tc.expectMatch, result)
   390  		}
   391  	}
   392  }
   393  
   394  func TestIsUnexpectedServerError(t *testing.T) {
   395  	unexpectedServerErr := func() error {
   396  		return &StatusError{
   397  			ErrStatus: metav1.Status{
   398  				Details: &metav1.StatusDetails{
   399  					Causes: []metav1.StatusCause{{Type: metav1.CauseTypeUnexpectedServerResponse}},
   400  				},
   401  			},
   402  		}
   403  	}
   404  	testCases := []struct {
   405  		name        string
   406  		err         error
   407  		expectMatch bool
   408  	}{
   409  		{
   410  			name:        "Direct match",
   411  			err:         unexpectedServerErr(),
   412  			expectMatch: true,
   413  		},
   414  		{
   415  			name:        "No match",
   416  			err:         errors.New("some other error"),
   417  			expectMatch: false,
   418  		},
   419  		{
   420  			name:        "Nested match",
   421  			err:         fmt.Errorf("wrapping: %w", unexpectedServerErr()),
   422  			expectMatch: true,
   423  		},
   424  		{
   425  			name:        "Nested, no match",
   426  			err:         fmt.Errorf("wrapping: %w", fmt.Errorf("some more: %w", errors.New("hello"))),
   427  			expectMatch: false,
   428  		},
   429  		{
   430  			name:        "Nil",
   431  			expectMatch: false,
   432  		},
   433  	}
   434  
   435  	for _, tc := range testCases {
   436  		t.Run(tc.name, func(t *testing.T) {
   437  			if result := IsUnexpectedServerError(tc.err); result != tc.expectMatch {
   438  				t.Errorf("expected match: %t, but got match: %t", tc.expectMatch, result)
   439  			}
   440  		})
   441  	}
   442  }
   443  
   444  func TestIsUnexpectedObjectError(t *testing.T) {
   445  	unexpectedObjectErr := func() error {
   446  		return &UnexpectedObjectError{}
   447  	}
   448  	testCases := []struct {
   449  		name        string
   450  		err         error
   451  		expectMatch bool
   452  	}{
   453  		{
   454  			name:        "Direct match",
   455  			err:         unexpectedObjectErr(),
   456  			expectMatch: true,
   457  		},
   458  		{
   459  			name:        "No match",
   460  			err:         errors.New("some other error"),
   461  			expectMatch: false,
   462  		},
   463  		{
   464  			name:        "Nested match",
   465  			err:         fmt.Errorf("wrapping: %w", unexpectedObjectErr()),
   466  			expectMatch: true,
   467  		},
   468  		{
   469  			name:        "Nested, no match",
   470  			err:         fmt.Errorf("wrapping: %w", fmt.Errorf("some more: %w", errors.New("hello"))),
   471  			expectMatch: false,
   472  		},
   473  		{
   474  			name:        "Nil",
   475  			expectMatch: false,
   476  		},
   477  	}
   478  
   479  	for _, tc := range testCases {
   480  		t.Run(tc.name, func(t *testing.T) {
   481  			if result := IsUnexpectedObjectError(tc.err); result != tc.expectMatch {
   482  				t.Errorf("expected match: %t, but got match: %t", tc.expectMatch, result)
   483  			}
   484  		})
   485  	}
   486  }
   487  
   488  func TestSuggestsClientDelaySupportsWrapping(t *testing.T) {
   489  	suggestsClientDelayErr := func() error {
   490  		return &StatusError{
   491  			ErrStatus: metav1.Status{
   492  				Reason:  metav1.StatusReasonServerTimeout,
   493  				Details: &metav1.StatusDetails{},
   494  			},
   495  		}
   496  	}
   497  	testCases := []struct {
   498  		name        string
   499  		err         error
   500  		expectMatch bool
   501  	}{
   502  		{
   503  			name:        "Direct match",
   504  			err:         suggestsClientDelayErr(),
   505  			expectMatch: true,
   506  		},
   507  		{
   508  			name:        "No match",
   509  			err:         errors.New("some other error"),
   510  			expectMatch: false,
   511  		},
   512  		{
   513  			name:        "Nested match",
   514  			err:         fmt.Errorf("wrapping: %w", suggestsClientDelayErr()),
   515  			expectMatch: true,
   516  		},
   517  		{
   518  			name:        "Nested, no match",
   519  			err:         fmt.Errorf("wrapping: %w", fmt.Errorf("some more: %w", errors.New("hello"))),
   520  			expectMatch: false,
   521  		},
   522  		{
   523  			name:        "Nil",
   524  			expectMatch: false,
   525  		},
   526  	}
   527  
   528  	for _, tc := range testCases {
   529  		t.Run(tc.name, func(t *testing.T) {
   530  			if _, result := SuggestsClientDelay(tc.err); result != tc.expectMatch {
   531  				t.Errorf("expected match: %t, but got match: %t", tc.expectMatch, result)
   532  			}
   533  		})
   534  	}
   535  }
   536  
   537  func TestIsErrorTypesByReasonAndCode(t *testing.T) {
   538  	testCases := []struct {
   539  		name                  string
   540  		knownReason           metav1.StatusReason
   541  		otherReason           metav1.StatusReason
   542  		otherReasonConsidered bool
   543  		code                  int32
   544  		fn                    func(error) bool
   545  	}{
   546  		{
   547  			name:                  "IsRequestEntityTooLarge",
   548  			knownReason:           metav1.StatusReasonRequestEntityTooLarge,
   549  			otherReason:           metav1.StatusReasonForbidden,
   550  			otherReasonConsidered: false,
   551  			code:                  http.StatusRequestEntityTooLarge,
   552  			fn:                    IsRequestEntityTooLargeError,
   553  		}, {
   554  			name:                  "TooManyRequests",
   555  			knownReason:           metav1.StatusReasonTooManyRequests,
   556  			otherReason:           metav1.StatusReasonForbidden,
   557  			otherReasonConsidered: false,
   558  			code:                  http.StatusTooManyRequests,
   559  			fn:                    IsTooManyRequests,
   560  		}, {
   561  			name:                  "Forbidden",
   562  			knownReason:           metav1.StatusReasonForbidden,
   563  			otherReason:           metav1.StatusReasonNotFound,
   564  			otherReasonConsidered: true,
   565  			code:                  http.StatusForbidden,
   566  			fn:                    IsForbidden,
   567  		}, {
   568  			name:                  "NotFound",
   569  			knownReason:           metav1.StatusReasonNotFound,
   570  			otherReason:           metav1.StatusReasonForbidden,
   571  			otherReasonConsidered: true,
   572  			code:                  http.StatusNotFound,
   573  			fn:                    IsNotFound,
   574  		},
   575  	}
   576  
   577  	for _, tc := range testCases {
   578  		t.Run(tc.name, func(t *testing.T) {
   579  			t.Run("by known reason", func(t *testing.T) {
   580  				err := &StatusError{
   581  					metav1.Status{
   582  						Reason: tc.knownReason,
   583  					},
   584  				}
   585  
   586  				got := tc.fn(err)
   587  				if !got {
   588  					t.Errorf("expected reason %s to match", tc.knownReason)
   589  				}
   590  			})
   591  
   592  			t.Run("by code and unknown reason", func(t *testing.T) {
   593  				err := &StatusError{
   594  					metav1.Status{
   595  						Reason: metav1.StatusReasonUnknown, // this could be _any_ reason that isn't in knownReasons.
   596  						Code:   tc.code,
   597  					},
   598  				}
   599  
   600  				got := tc.fn(err)
   601  				if !got {
   602  					t.Errorf("expected code %d with reason %s to match", tc.code, tc.otherReason)
   603  				}
   604  			})
   605  
   606  			if !tc.otherReasonConsidered {
   607  				return
   608  			}
   609  
   610  			t.Run("by code and other known reason", func(t *testing.T) {
   611  				err := &StatusError{
   612  					metav1.Status{
   613  						Reason: tc.otherReason,
   614  						Code:   tc.code,
   615  					},
   616  				}
   617  
   618  				got := tc.fn(err)
   619  				if got {
   620  					t.Errorf("expected code %d with reason %s to not match", tc.code, tc.otherReason)
   621  				}
   622  			})
   623  
   624  		})
   625  
   626  	}
   627  }
   628  
   629  func TestStatusCauseSupportsWrappedErrors(t *testing.T) {
   630  	err := &StatusError{ErrStatus: metav1.Status{
   631  		Details: &metav1.StatusDetails{
   632  			Causes: []metav1.StatusCause{{Type: "SomeCause"}},
   633  		},
   634  	}}
   635  
   636  	if cause, ok := StatusCause(nil, "SomeCause"); ok {
   637  		t.Errorf("expected no cause for nil, got %v: %#v", ok, cause)
   638  	}
   639  	if cause, ok := StatusCause(errors.New("boom"), "SomeCause"); ok {
   640  		t.Errorf("expected no cause for wrong type, got %v: %#v", ok, cause)
   641  	}
   642  
   643  	if cause, ok := StatusCause(err, "Other"); ok {
   644  		t.Errorf("expected no cause for wrong name, got %v: %#v", ok, cause)
   645  	}
   646  	if cause, ok := StatusCause(err, "SomeCause"); !ok || cause != err.ErrStatus.Details.Causes[0] {
   647  		t.Errorf("expected cause, got %v: %#v", ok, cause)
   648  	}
   649  
   650  	wrapped := fmt.Errorf("once: %w", err)
   651  	if cause, ok := StatusCause(wrapped, "SomeCause"); !ok || cause != err.ErrStatus.Details.Causes[0] {
   652  		t.Errorf("expected cause when wrapped, got %v: %#v", ok, cause)
   653  	}
   654  
   655  	nested := fmt.Errorf("twice: %w", wrapped)
   656  	if cause, ok := StatusCause(nested, "SomeCause"); !ok || cause != err.ErrStatus.Details.Causes[0] {
   657  		t.Errorf("expected cause when nested, got %v: %#v", ok, cause)
   658  	}
   659  }
   660  
   661  func BenchmarkIsAlreadyExistsWrappedErrors(b *testing.B) {
   662  	err := NewAlreadyExists(schema.GroupResource{}, "")
   663  	wrapped := fmt.Errorf("once: %w", err)
   664  
   665  	b.Run("Nil", func(b *testing.B) {
   666  		for i := 0; i < b.N; i++ {
   667  			IsAlreadyExists(nil)
   668  		}
   669  	})
   670  
   671  	b.Run("Bare", func(b *testing.B) {
   672  		for i := 0; i < b.N; i++ {
   673  			IsAlreadyExists(err)
   674  		}
   675  	})
   676  
   677  	b.Run("Wrapped", func(b *testing.B) {
   678  		for i := 0; i < b.N; i++ {
   679  			IsAlreadyExists(wrapped)
   680  		}
   681  	})
   682  }
   683  
   684  func BenchmarkIsNotFoundWrappedErrors(b *testing.B) {
   685  	err := NewNotFound(schema.GroupResource{}, "")
   686  	wrapped := fmt.Errorf("once: %w", err)
   687  
   688  	b.Run("Nil", func(b *testing.B) {
   689  		for i := 0; i < b.N; i++ {
   690  			IsNotFound(nil)
   691  		}
   692  	})
   693  
   694  	b.Run("Bare", func(b *testing.B) {
   695  		for i := 0; i < b.N; i++ {
   696  			IsNotFound(err)
   697  		}
   698  	})
   699  
   700  	b.Run("Wrapped", func(b *testing.B) {
   701  		for i := 0; i < b.N; i++ {
   702  			IsNotFound(wrapped)
   703  		}
   704  	})
   705  }