Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(408)

Side by Side Diff: client/internal/lhttp/client_test.go

Issue 1135173003: Create packages client/internal/ retry and lhttp. (Closed) Base URL: git@github.com:luci/luci-go@3_UI
Patch Set: . Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698