github.com/sacloud/iaas-api-go@v1.12.0/client_test.go (about) 1 // Copyright 2022-2023 The sacloud/iaas-api-go 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 iaas 16 17 import ( 18 "context" 19 "net/http" 20 "net/http/httptest" 21 "testing" 22 "time" 23 24 client "github.com/sacloud/api-client-go" 25 "github.com/stretchr/testify/require" 26 ) 27 28 type dummyHandler struct { 29 called []time.Time 30 responseCode int 31 } 32 33 func (s *dummyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 34 if s.responseCode == http.StatusMovedPermanently { 35 w.Header().Set("Location", "/index.html") 36 } 37 w.WriteHeader(s.responseCode) 38 switch s.responseCode { 39 case http.StatusMultipleChoices, 40 http.StatusMovedPermanently, 41 http.StatusFound, 42 http.StatusSeeOther, 43 http.StatusNotModified, 44 http.StatusUseProxy, 45 http.StatusTemporaryRedirect, 46 http.StatusPermanentRedirect: 47 s.responseCode = http.StatusOK 48 default: 49 s.called = append(s.called, time.Now()) 50 } 51 } 52 53 func (s *dummyHandler) isRetried() bool { 54 return len(s.called) > 1 55 } 56 57 func TestClient_Do_CheckRetryWithContext(t *testing.T) { 58 caller := NewClientWithOptions(&client.Options{ 59 RetryMax: 1, 60 RetryWaitMin: 1, 61 RetryWaitMax: 1, 62 }) 63 64 t.Run("context.Canceled", func(t *testing.T) { 65 h := &dummyHandler{ 66 responseCode: http.StatusServiceUnavailable, 67 } 68 dummyServer := httptest.NewServer(h) 69 defer dummyServer.Close() 70 71 ctx, cancel := context.WithCancel(context.Background()) 72 // make ctx to Canceled 73 cancel() 74 75 caller.Do(ctx, http.MethodGet, dummyServer.URL, nil) //nolint 76 require.False(t, h.isRetried(), "don't retry when context was canceled") 77 }) 78 79 t.Run("context.DeadlineExceeded", func(t *testing.T) { 80 h := &dummyHandler{ 81 responseCode: http.StatusServiceUnavailable, 82 } 83 dummyServer := httptest.NewServer(h) 84 defer dummyServer.Close() 85 86 ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond) 87 defer cancel() 88 // make ctx to DeadlineExceeded 89 time.Sleep(time.Millisecond) 90 91 caller.Do(ctx, http.MethodGet, dummyServer.URL, nil) //nolint 92 require.False(t, h.isRetried(), "don't retry when context exceeded deadline") 93 }) 94 } 95 96 func TestClient_RetryByStatusCode(t *testing.T) { 97 cases := []struct { 98 responseCode int 99 shouldRetry bool 100 }{ 101 {responseCode: http.StatusOK, shouldRetry: false}, 102 {responseCode: http.StatusCreated, shouldRetry: false}, 103 {responseCode: http.StatusAccepted, shouldRetry: false}, 104 {responseCode: http.StatusNoContent, shouldRetry: false}, 105 {responseCode: http.StatusMovedPermanently, shouldRetry: false}, 106 {responseCode: http.StatusFound, shouldRetry: false}, 107 {responseCode: http.StatusBadRequest, shouldRetry: false}, 108 {responseCode: http.StatusUnauthorized, shouldRetry: false}, 109 {responseCode: http.StatusForbidden, shouldRetry: false}, 110 {responseCode: http.StatusNotFound, shouldRetry: false}, 111 {responseCode: http.StatusLocked, shouldRetry: true}, // Locked: 423 112 {responseCode: http.StatusInternalServerError, shouldRetry: false}, 113 {responseCode: http.StatusBadGateway, shouldRetry: false}, 114 {responseCode: http.StatusServiceUnavailable, shouldRetry: true}, 115 {responseCode: http.StatusGatewayTimeout, shouldRetry: false}, 116 } 117 118 caller := NewClientWithOptions(&client.Options{ 119 RetryMax: 1, 120 RetryWaitMin: 1, 121 RetryWaitMax: 1, 122 }) 123 124 for _, tt := range cases { 125 h := &dummyHandler{ 126 responseCode: tt.responseCode, 127 } 128 dummyServer := httptest.NewServer(h) 129 caller.Do(context.Background(), http.MethodGet, dummyServer.URL, nil) //nolint 130 dummyServer.Close() 131 132 require.Equal(t, tt.shouldRetry, h.isRetried(), 133 "got unexpected retry status with status[%d]: expected:%t got:%t", tt.responseCode, tt.shouldRetry, h.isRetried()) 134 } 135 }