knative.dev/pkg@v0.0.0-20260602142205-ac97e43f6622/reconciler/retry_test.go (about)

     1  /*
     2  Copyright 2020 The Knative Authors
     3  
     4  Licensed under the Apache License, Veroute.on 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 reconciler
    18  
    19  import (
    20  	"errors"
    21  	"testing"
    22  
    23  	apierrs "k8s.io/apimachinery/pkg/api/errors"
    24  
    25  	v1 "k8s.io/api/autoscaling/v1"
    26  )
    27  
    28  func TestRetryUpdateConflicts(t *testing.T) {
    29  	errAny := errors.New("foo")
    30  	errConflict := apierrs.NewConflict(v1.Resource("foo"), "bar", errAny)
    31  
    32  	tests := []struct {
    33  		name         string
    34  		returns      []error
    35  		want         error
    36  		wantAttempts int
    37  	}{{
    38  		name:         "all good",
    39  		returns:      []error{nil},
    40  		want:         nil,
    41  		wantAttempts: 1,
    42  	}, {
    43  		name:         "not retry on non-conflict error",
    44  		returns:      []error{errAny},
    45  		want:         errAny,
    46  		wantAttempts: 1,
    47  	}, {
    48  		name:         "retry up to 5 times on conflicts",
    49  		returns:      []error{errConflict, errConflict, errConflict, errConflict, errConflict, errConflict},
    50  		want:         errConflict,
    51  		wantAttempts: 5,
    52  	}, {
    53  		name:         "eventually succeed",
    54  		returns:      []error{errConflict, errConflict, nil},
    55  		want:         nil,
    56  		wantAttempts: 3,
    57  	}, {
    58  		name:         "eventually fail",
    59  		returns:      []error{errConflict, errConflict, errAny},
    60  		want:         errAny,
    61  		wantAttempts: 3,
    62  	}}
    63  
    64  	for _, test := range tests {
    65  		t.Run(test.name, func(t *testing.T) {
    66  			attempts := 0
    67  			got := RetryUpdateConflicts(func(i int) error {
    68  				attempts++
    69  				return test.returns[i]
    70  			})
    71  
    72  			if !errors.Is(got, test.want) {
    73  				t.Errorf("RetryUpdateConflicts() = %v, want %v", got, test.want)
    74  			}
    75  			if attempts != test.wantAttempts {
    76  				t.Errorf("attempts = %d, want %d", attempts, test.wantAttempts)
    77  			}
    78  		})
    79  	}
    80  }
    81  
    82  func TestRetryTestErrors(t *testing.T) {
    83  	errAny := errors.New("foo")
    84  	errGKE := errors.New(`operation cannot be fulfilled on resourcequotas "gke-resource-quotas": StorageError: invalid object, Code: 4, Key: /registry/resourcequotas/serving-tests/gke-resource-quotas, ResourceVersion: 0, AdditionalErrorMsg: Precondition failed: UID in precondition: 7aaedbdf-caa8-41e7-94cb-f8c053038e86, UID in object meta`)
    85  	errEtcd := errors.New("etcdserver: request timed out")
    86  	errConflict := apierrs.NewConflict(v1.Resource("foo"), "bar", errAny)
    87  
    88  	tests := []struct {
    89  		name         string
    90  		returns      []error
    91  		want         error
    92  		wantAttempts int
    93  	}{{
    94  		name:         "all good",
    95  		returns:      []error{nil},
    96  		want:         nil,
    97  		wantAttempts: 1,
    98  	}, {
    99  		name:         "not retry on non-conflict error",
   100  		returns:      []error{errAny},
   101  		want:         errAny,
   102  		wantAttempts: 1,
   103  	}, {
   104  		name:         "retry up to 5 times",
   105  		returns:      []error{errConflict, errGKE, errEtcd, errConflict, errGKE, errEtcd},
   106  		want:         errGKE,
   107  		wantAttempts: 5,
   108  	}, {
   109  		name:         "eventually succeed",
   110  		returns:      []error{errConflict, errGKE, errEtcd, nil},
   111  		want:         nil,
   112  		wantAttempts: 4,
   113  	}, {
   114  		name:         "eventually fail",
   115  		returns:      []error{errConflict, errConflict, errAny},
   116  		want:         errAny,
   117  		wantAttempts: 3,
   118  	}}
   119  
   120  	for _, test := range tests {
   121  		t.Run(test.name, func(t *testing.T) {
   122  			attempts := 0
   123  			got := RetryTestErrors(func(i int) error {
   124  				attempts++
   125  				return test.returns[i]
   126  			})
   127  
   128  			if !errors.Is(got, test.want) {
   129  				t.Errorf("RetryTestErrors() = %v, want %v", got, test.want)
   130  			}
   131  			if attempts != test.wantAttempts {
   132  				t.Errorf("attempts = %d, want %d", attempts, test.wantAttempts)
   133  			}
   134  		})
   135  	}
   136  }