| OLD | NEW |
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 package lhttp |
| 6 |
| 7 import ( |
| 8 "bytes" |
| 9 "encoding/json" |
| 10 "errors" |
| 11 "fmt" |
| 12 "io" |
| 13 "io/ioutil" |
| 14 "net/http" |
| 15 "net/http/httptest" |
| 16 "net/url" |
| 17 "testing" |
| 18 "time" |
| 19 |
| 20 "github.com/luci/luci-go/client/internal/retry" |
| 21 "github.com/maruel/ut" |
| 22 ) |
| 23 |
| 24 func TestNewRequestGET(t *testing.T) { |
| 25 // First call returns HTTP 500, second succeeds. |
| 26 serverCalls := 0 |
| 27 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 28 serverCalls++ |
| 29 content, err := ioutil.ReadAll(r.Body) |
| 30 ut.ExpectEqual(t, nil, err) |
| 31 ut.ExpectEqual(t, []byte{}, content) |
| 32 if serverCalls == 1 { |
| 33 w.WriteHeader(500) |
| 34 } else { |
| 35 fmt.Fprintf(w, "Hello, client\n") |
| 36 } |
| 37 })) |
| 38 defer ts.Close() |
| 39 |
| 40 httpReq, err := http.NewRequest("GET", ts.URL, nil) |
| 41 ut.AssertEqual(t, nil, err) |
| 42 |
| 43 clientCalls := 0 |
| 44 clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *htt
p.Response) error { |
| 45 clientCalls++ |
| 46 content, err := ioutil.ReadAll(resp.Body) |
| 47 ut.AssertEqual(t, nil, err) |
| 48 ut.AssertEqual(t, "Hello, client\n", string(content)) |
| 49 ut.AssertEqual(t, nil, resp.Body.Close()) |
| 50 return nil |
| 51 }) |
| 52 ut.AssertEqual(t, nil, err) |
| 53 |
| 54 ut.AssertEqual(t, nil, fast.Do(clientReq)) |
| 55 ut.AssertEqual(t, 2, serverCalls) |
| 56 ut.AssertEqual(t, 1, clientCalls) |
| 57 } |
| 58 |
| 59 func TestNewRequestPOST(t *testing.T) { |
| 60 // First call returns HTTP 500, second succeeds. |
| 61 serverCalls := 0 |
| 62 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 63 serverCalls++ |
| 64 content, err := ioutil.ReadAll(r.Body) |
| 65 ut.ExpectEqual(t, nil, err) |
| 66 // The same data is sent twice. |
| 67 ut.ExpectEqual(t, "foo bar", string(content)) |
| 68 if serverCalls == 1 { |
| 69 w.WriteHeader(500) |
| 70 } else { |
| 71 fmt.Fprintf(w, "Hello, client\n") |
| 72 } |
| 73 })) |
| 74 defer ts.Close() |
| 75 |
| 76 httpReq, err := http.NewRequest("POST", ts.URL, NewReader([]byte("foo ba
r"))) |
| 77 ut.AssertEqual(t, nil, err) |
| 78 |
| 79 clientCalls := 0 |
| 80 clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *htt
p.Response) error { |
| 81 clientCalls++ |
| 82 content, err := ioutil.ReadAll(resp.Body) |
| 83 ut.AssertEqual(t, nil, err) |
| 84 ut.AssertEqual(t, "Hello, client\n", string(content)) |
| 85 ut.AssertEqual(t, nil, resp.Body.Close()) |
| 86 return nil |
| 87 }) |
| 88 ut.AssertEqual(t, nil, err) |
| 89 |
| 90 ut.AssertEqual(t, nil, fast.Do(clientReq)) |
| 91 ut.AssertEqual(t, 2, serverCalls) |
| 92 ut.AssertEqual(t, 1, clientCalls) |
| 93 } |
| 94 |
| 95 func TestNewRequestNotSeeker(t *testing.T) { |
| 96 // bytes.NewReader() doesn't implement io.Seeker. |
| 97 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 98 t.Fail() |
| 99 })) |
| 100 defer ts.Close() |
| 101 httpReq, err := http.NewRequest("POST", ts.URL, bytes.NewReader([]byte("
foo bar"))) |
| 102 ut.AssertEqual(t, nil, err) |
| 103 |
| 104 clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *htt
p.Response) error { |
| 105 t.Fail() |
| 106 return nil |
| 107 }) |
| 108 |
| 109 ut.AssertEqual(t, nil, clientReq) |
| 110 ut.AssertEqual(t, errors.New("req.Body must implement io.Seeker"), err) |
| 111 } |
| 112 |
| 113 func TestNewRequestBadURL(t *testing.T) { |
| 114 httpReq, err := http.NewRequest("GET", "invalid url", nil) |
| 115 ut.AssertEqual(t, nil, err) |
| 116 |
| 117 clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *htt
p.Response) error { |
| 118 t.Fail() |
| 119 return nil |
| 120 }) |
| 121 ut.AssertEqual(t, nil, err) |
| 122 |
| 123 // The request must not be retried. |
| 124 err = slow.Do(clientReq) |
| 125 urlErr := err.(*url.Error) |
| 126 ut.AssertEqual(t, "Get", urlErr.Op) |
| 127 ut.AssertEqual(t, "invalid%20url", urlErr.URL) |
| 128 ut.AssertEqual(t, "unsupported protocol scheme \"\"", urlErr.Err.Error()
) |
| 129 } |
| 130 |
| 131 func TestNewRequestGETFail(t *testing.T) { |
| 132 serverCalls := 0 |
| 133 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 134 serverCalls++ |
| 135 w.WriteHeader(500) |
| 136 })) |
| 137 defer ts.Close() |
| 138 |
| 139 httpReq, err := http.NewRequest("GET", ts.URL, nil) |
| 140 ut.AssertEqual(t, nil, err) |
| 141 |
| 142 clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *htt
p.Response) error { |
| 143 t.Fail() |
| 144 return nil |
| 145 }) |
| 146 |
| 147 ut.AssertEqual(t, retry.Error{errors.New("http request failed: Internal
Server Error (HTTP 500)")}, fast.Do(clientReq)) |
| 148 ut.AssertEqual(t, fast.MaxTries, serverCalls) |
| 149 } |
| 150 |
| 151 func TestNewRequestJSONBadURL(t *testing.T) { |
| 152 clientReq, err := NewRequestJSON(http.DefaultClient, "GET", "invalid url
", nil, nil) |
| 153 ut.AssertEqual(t, nil, err) |
| 154 |
| 155 // The request must not be retried. |
| 156 ut.AssertEqual(t, "unsupported protocol scheme \"\"", slow.Do(clientReq)
.Error()) |
| 157 } |
| 158 |
| 159 func TestGetJSON(t *testing.T) { |
| 160 // First call returns HTTP 500, second succeeds. |
| 161 serverCalls := 0 |
| 162 ts := httptest.NewServer(handlerJSON(t, func(body io.Reader) interface{}
{ |
| 163 serverCalls++ |
| 164 content, err := ioutil.ReadAll(body) |
| 165 ut.ExpectEqual(t, nil, err) |
| 166 ut.ExpectEqual(t, []byte{}, content) |
| 167 if serverCalls == 1 { |
| 168 return nil |
| 169 } |
| 170 return map[string]string{"success": "yeah"} |
| 171 })) |
| 172 defer ts.Close() |
| 173 |
| 174 actual := map[string]string{} |
| 175 ut.AssertEqual(t, nil, GetJSON(fast, http.DefaultClient, ts.URL, &actual
)) |
| 176 ut.AssertEqual(t, map[string]string{"success": "yeah"}, actual) |
| 177 ut.AssertEqual(t, 2, serverCalls) |
| 178 } |
| 179 |
| 180 func TestGetJSONBadResult(t *testing.T) { |
| 181 serverCalls := 0 |
| 182 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 183 serverCalls++ |
| 184 w.Header().Set("Content-Type", jsonContentType) |
| 185 _, err := io.WriteString(w, "yo") |
| 186 ut.ExpectEqual(t, nil, err) |
| 187 })) |
| 188 defer ts.Close() |
| 189 |
| 190 actual := map[string]string{} |
| 191 expected := retry.Error{errors.New("bad response " + ts.URL + ": invalid
character 'y' looking for beginning of value")} |
| 192 ut.AssertEqual(t, expected, GetJSON(fast, http.DefaultClient, ts.URL, &a
ctual)) |
| 193 ut.AssertEqual(t, map[string]string{}, actual) |
| 194 ut.AssertEqual(t, fast.MaxTries, serverCalls) |
| 195 } |
| 196 |
| 197 func TestGetJSONBadResultIgnore(t *testing.T) { |
| 198 serverCalls := 0 |
| 199 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 200 serverCalls++ |
| 201 w.Header().Set("Content-Type", jsonContentType) |
| 202 _, err := io.WriteString(w, "yo") |
| 203 ut.ExpectEqual(t, nil, err) |
| 204 })) |
| 205 defer ts.Close() |
| 206 |
| 207 expected := retry.Error{errors.New("bad response " + ts.URL + ": invalid
character 'y' looking for beginning of value")} |
| 208 ut.AssertEqual(t, expected, GetJSON(fast, http.DefaultClient, ts.URL, ni
l)) |
| 209 } |
| 210 |
| 211 func TestGetJSONBadContentTypeIgnore(t *testing.T) { |
| 212 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r
*http.Request) { |
| 213 _, err := io.WriteString(w, "{}") |
| 214 ut.ExpectEqual(t, nil, err) |
| 215 })) |
| 216 defer ts.Close() |
| 217 |
| 218 expected := errors.New("unexpected Content-Type, expected \"application/
json; charset=utf-8\", got \"text/plain; charset=utf-8\"") |
| 219 ut.AssertEqual(t, expected, GetJSON(fast, http.DefaultClient, ts.URL, ni
l)) |
| 220 } |
| 221 |
| 222 func TestPostJSON(t *testing.T) { |
| 223 // First call returns HTTP 500, second succeeds. |
| 224 serverCalls := 0 |
| 225 ts := httptest.NewServer(handlerJSON(t, func(body io.Reader) interface{}
{ |
| 226 serverCalls++ |
| 227 data := map[string]string{} |
| 228 ut.ExpectEqual(t, nil, json.NewDecoder(body).Decode(&data)) |
| 229 ut.ExpectEqual(t, map[string]string{"in": "all"}, data) |
| 230 if serverCalls == 1 { |
| 231 return nil |
| 232 } |
| 233 return map[string]string{"success": "yeah"} |
| 234 })) |
| 235 defer ts.Close() |
| 236 |
| 237 in := map[string]string{"in": "all"} |
| 238 actual := map[string]string{} |
| 239 ut.AssertEqual(t, nil, PostJSON(fast, http.DefaultClient, ts.URL, in, &a
ctual)) |
| 240 ut.AssertEqual(t, map[string]string{"success": "yeah"}, actual) |
| 241 ut.AssertEqual(t, 2, serverCalls) |
| 242 } |
| 243 |
| 244 // Private details. |
| 245 |
| 246 var fast = &retry.Config{ |
| 247 MaxTries: 3, |
| 248 SleepMax: 0, |
| 249 } |
| 250 |
| 251 // slow is to be used when no retry should happen. The test will hang in that |
| 252 // case. |
| 253 var slow = &retry.Config{ |
| 254 MaxTries: 3, |
| 255 SleepMax: time.Hour, |
| 256 SleepBase: time.Hour, |
| 257 } |
| 258 |
| 259 type jsonAPI func(body io.Reader) interface{} |
| 260 |
| 261 // handlerJSON converts a jsonAPI http handler to a proper http.Handler. |
| 262 func handlerJSON(t *testing.T, handler jsonAPI) http.Handler { |
| 263 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 264 //ut.ExpectEqual(t, jsonContentType, r.Header.Get("Content-Type"
)) |
| 265 defer r.Body.Close() |
| 266 out := handler(r.Body) |
| 267 if out == nil { |
| 268 w.WriteHeader(500) |
| 269 } else { |
| 270 w.Header().Set("Content-Type", jsonContentType) |
| 271 ut.ExpectEqual(t, nil, json.NewEncoder(w).Encode(out)) |
| 272 } |
| 273 }) |
| 274 } |
| OLD | NEW |