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 }