go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cipd/appengine/impl/gs/retry_test.go (about) 1 // Copyright 2017 The LUCI Authors. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gs 16 17 import ( 18 "context" 19 "fmt" 20 "testing" 21 "time" 22 23 "google.golang.org/api/googleapi" 24 25 "go.chromium.org/luci/common/clock" 26 "go.chromium.org/luci/common/clock/testclock" 27 "go.chromium.org/luci/common/retry/transient" 28 29 . "github.com/smartystreets/goconvey/convey" 30 . "go.chromium.org/luci/common/testing/assertions" 31 ) 32 33 func TestRetryAndStatusCode(t *testing.T) { 34 t.Parallel() 35 36 Convey("With clock", t, func() { 37 ctx, cl := testclock.UseTime(context.Background(), testclock.TestRecentTimeUTC) 38 cl.SetTimerCallback(func(d time.Duration, t clock.Timer) { cl.Add(d) }) 39 40 Convey("Happy path", func() { 41 calls := 0 42 err := withRetry(ctx, func() error { 43 calls++ 44 return nil 45 }) 46 So(calls, ShouldEqual, 1) 47 So(err, ShouldBeNil) 48 So(StatusCode(err), ShouldEqual, 200) 49 }) 50 51 Convey("Retrying generic connection error", func() { 52 calls := 0 53 err := withRetry(ctx, func() error { 54 calls++ 55 return fmt.Errorf("generic error") 56 }) 57 So(calls, ShouldEqual, 11) 58 So(err, ShouldErrLike, "generic error") 59 So(transient.Tag.In(err), ShouldBeTrue) 60 So(StatusCode(err), ShouldEqual, 0) 61 }) 62 63 Convey("Retrying transient API error", func() { 64 calls := 0 65 err := withRetry(ctx, func() error { 66 calls++ 67 return &googleapi.Error{Code: 500} 68 }) 69 So(calls, ShouldEqual, 11) 70 So(err, ShouldErrLike, "HTTP code 500") 71 So(transient.Tag.In(err), ShouldBeTrue) 72 So(StatusCode(err), ShouldEqual, 500) 73 }) 74 75 Convey("Giving up on fatal API error", func() { 76 calls := 0 77 err := withRetry(ctx, func() error { 78 calls++ 79 return &googleapi.Error{Code: 403} 80 }) 81 So(calls, ShouldEqual, 1) 82 So(err, ShouldErrLike, "HTTP code 403") 83 So(transient.Tag.In(err), ShouldBeFalse) 84 So(StatusCode(err), ShouldEqual, 403) 85 }) 86 87 Convey("Passes through *RestartUploadError", func() { 88 uploadErr := &RestartUploadError{Offset: 123} 89 calls := 0 90 err := withRetry(ctx, func() error { 91 calls++ 92 return uploadErr 93 }) 94 So(calls, ShouldEqual, 1) 95 So(err, ShouldEqual, uploadErr) // exact same error object 96 }) 97 }) 98 }