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