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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: client/internal/lhttp/client_test.go
diff --git a/client/internal/lhttp/client_test.go b/client/internal/lhttp/client_test.go
new file mode 100644
index 0000000000000000000000000000000000000000..090528e10afe109291a56b9e1ad06724ec2777ea
--- /dev/null
+++ b/client/internal/lhttp/client_test.go
@@ -0,0 +1,274 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package lhttp
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "net/http"
+ "net/http/httptest"
+ "net/url"
+ "testing"
+ "time"
+
+ "github.com/luci/luci-go/client/internal/retry"
+ "github.com/maruel/ut"
+)
+
+func TestNewRequestGET(t *testing.T) {
+ // First call returns HTTP 500, second succeeds.
+ serverCalls := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ serverCalls++
+ content, err := ioutil.ReadAll(r.Body)
+ ut.ExpectEqual(t, nil, err)
+ ut.ExpectEqual(t, []byte{}, content)
+ if serverCalls == 1 {
+ w.WriteHeader(500)
+ } else {
+ fmt.Fprintf(w, "Hello, client\n")
+ }
+ }))
+ defer ts.Close()
+
+ httpReq, err := http.NewRequest("GET", ts.URL, nil)
+ ut.AssertEqual(t, nil, err)
+
+ clientCalls := 0
+ clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *http.Response) error {
+ clientCalls++
+ content, err := ioutil.ReadAll(resp.Body)
+ ut.AssertEqual(t, nil, err)
+ ut.AssertEqual(t, "Hello, client\n", string(content))
+ ut.AssertEqual(t, nil, resp.Body.Close())
+ return nil
+ })
+ ut.AssertEqual(t, nil, err)
+
+ ut.AssertEqual(t, nil, fast.Do(clientReq))
+ ut.AssertEqual(t, 2, serverCalls)
+ ut.AssertEqual(t, 1, clientCalls)
+}
+
+func TestNewRequestPOST(t *testing.T) {
+ // First call returns HTTP 500, second succeeds.
+ serverCalls := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ serverCalls++
+ content, err := ioutil.ReadAll(r.Body)
+ ut.ExpectEqual(t, nil, err)
+ // The same data is sent twice.
+ ut.ExpectEqual(t, "foo bar", string(content))
+ if serverCalls == 1 {
+ w.WriteHeader(500)
+ } else {
+ fmt.Fprintf(w, "Hello, client\n")
+ }
+ }))
+ defer ts.Close()
+
+ httpReq, err := http.NewRequest("POST", ts.URL, NewReader([]byte("foo bar")))
+ ut.AssertEqual(t, nil, err)
+
+ clientCalls := 0
+ clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *http.Response) error {
+ clientCalls++
+ content, err := ioutil.ReadAll(resp.Body)
+ ut.AssertEqual(t, nil, err)
+ ut.AssertEqual(t, "Hello, client\n", string(content))
+ ut.AssertEqual(t, nil, resp.Body.Close())
+ return nil
+ })
+ ut.AssertEqual(t, nil, err)
+
+ ut.AssertEqual(t, nil, fast.Do(clientReq))
+ ut.AssertEqual(t, 2, serverCalls)
+ ut.AssertEqual(t, 1, clientCalls)
+}
+
+func TestNewRequestNotSeeker(t *testing.T) {
+ // bytes.NewReader() doesn't implement io.Seeker.
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ t.Fail()
+ }))
+ defer ts.Close()
+ httpReq, err := http.NewRequest("POST", ts.URL, bytes.NewReader([]byte("foo bar")))
+ ut.AssertEqual(t, nil, err)
+
+ clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *http.Response) error {
+ t.Fail()
+ return nil
+ })
+
+ ut.AssertEqual(t, nil, clientReq)
+ ut.AssertEqual(t, errors.New("req.Body must implement io.Seeker"), err)
+}
+
+func TestNewRequestBadURL(t *testing.T) {
+ httpReq, err := http.NewRequest("GET", "invalid url", nil)
+ ut.AssertEqual(t, nil, err)
+
+ clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *http.Response) error {
+ t.Fail()
+ return nil
+ })
+ ut.AssertEqual(t, nil, err)
+
+ // The request must not be retried.
+ err = slow.Do(clientReq)
+ urlErr := err.(*url.Error)
+ ut.AssertEqual(t, "Get", urlErr.Op)
+ ut.AssertEqual(t, "invalid%20url", urlErr.URL)
+ ut.AssertEqual(t, "unsupported protocol scheme \"\"", urlErr.Err.Error())
+}
+
+func TestNewRequestGETFail(t *testing.T) {
+ serverCalls := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ serverCalls++
+ w.WriteHeader(500)
+ }))
+ defer ts.Close()
+
+ httpReq, err := http.NewRequest("GET", ts.URL, nil)
+ ut.AssertEqual(t, nil, err)
+
+ clientReq, err := NewRequest(http.DefaultClient, httpReq, func(resp *http.Response) error {
+ t.Fail()
+ return nil
+ })
+
+ ut.AssertEqual(t, retry.Error{errors.New("http request failed: Internal Server Error (HTTP 500)")}, fast.Do(clientReq))
+ ut.AssertEqual(t, fast.MaxTries, serverCalls)
+}
+
+func TestNewRequestJSONBadURL(t *testing.T) {
+ clientReq, err := NewRequestJSON(http.DefaultClient, "GET", "invalid url", nil, nil)
+ ut.AssertEqual(t, nil, err)
+
+ // The request must not be retried.
+ ut.AssertEqual(t, "unsupported protocol scheme \"\"", slow.Do(clientReq).Error())
+}
+
+func TestGetJSON(t *testing.T) {
+ // First call returns HTTP 500, second succeeds.
+ serverCalls := 0
+ ts := httptest.NewServer(handlerJSON(t, func(body io.Reader) interface{} {
+ serverCalls++
+ content, err := ioutil.ReadAll(body)
+ ut.ExpectEqual(t, nil, err)
+ ut.ExpectEqual(t, []byte{}, content)
+ if serverCalls == 1 {
+ return nil
+ }
+ return map[string]string{"success": "yeah"}
+ }))
+ defer ts.Close()
+
+ actual := map[string]string{}
+ ut.AssertEqual(t, nil, GetJSON(fast, http.DefaultClient, ts.URL, &actual))
+ ut.AssertEqual(t, map[string]string{"success": "yeah"}, actual)
+ ut.AssertEqual(t, 2, serverCalls)
+}
+
+func TestGetJSONBadResult(t *testing.T) {
+ serverCalls := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ serverCalls++
+ w.Header().Set("Content-Type", jsonContentType)
+ _, err := io.WriteString(w, "yo")
+ ut.ExpectEqual(t, nil, err)
+ }))
+ defer ts.Close()
+
+ actual := map[string]string{}
+ expected := retry.Error{errors.New("bad response " + ts.URL + ": invalid character 'y' looking for beginning of value")}
+ ut.AssertEqual(t, expected, GetJSON(fast, http.DefaultClient, ts.URL, &actual))
+ ut.AssertEqual(t, map[string]string{}, actual)
+ ut.AssertEqual(t, fast.MaxTries, serverCalls)
+}
+
+func TestGetJSONBadResultIgnore(t *testing.T) {
+ serverCalls := 0
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ serverCalls++
+ w.Header().Set("Content-Type", jsonContentType)
+ _, err := io.WriteString(w, "yo")
+ ut.ExpectEqual(t, nil, err)
+ }))
+ defer ts.Close()
+
+ expected := retry.Error{errors.New("bad response " + ts.URL + ": invalid character 'y' looking for beginning of value")}
+ ut.AssertEqual(t, expected, GetJSON(fast, http.DefaultClient, ts.URL, nil))
+}
+
+func TestGetJSONBadContentTypeIgnore(t *testing.T) {
+ ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ _, err := io.WriteString(w, "{}")
+ ut.ExpectEqual(t, nil, err)
+ }))
+ defer ts.Close()
+
+ expected := errors.New("unexpected Content-Type, expected \"application/json; charset=utf-8\", got \"text/plain; charset=utf-8\"")
+ ut.AssertEqual(t, expected, GetJSON(fast, http.DefaultClient, ts.URL, nil))
+}
+
+func TestPostJSON(t *testing.T) {
+ // First call returns HTTP 500, second succeeds.
+ serverCalls := 0
+ ts := httptest.NewServer(handlerJSON(t, func(body io.Reader) interface{} {
+ serverCalls++
+ data := map[string]string{}
+ ut.ExpectEqual(t, nil, json.NewDecoder(body).Decode(&data))
+ ut.ExpectEqual(t, map[string]string{"in": "all"}, data)
+ if serverCalls == 1 {
+ return nil
+ }
+ return map[string]string{"success": "yeah"}
+ }))
+ defer ts.Close()
+
+ in := map[string]string{"in": "all"}
+ actual := map[string]string{}
+ ut.AssertEqual(t, nil, PostJSON(fast, http.DefaultClient, ts.URL, in, &actual))
+ ut.AssertEqual(t, map[string]string{"success": "yeah"}, actual)
+ ut.AssertEqual(t, 2, serverCalls)
+}
+
+// Private details.
+
+var fast = &retry.Config{
+ MaxTries: 3,
+ SleepMax: 0,
+}
+
+// slow is to be used when no retry should happen. The test will hang in that
+// case.
+var slow = &retry.Config{
+ MaxTries: 3,
+ SleepMax: time.Hour,
+ SleepBase: time.Hour,
+}
+
+type jsonAPI func(body io.Reader) interface{}
+
+// handlerJSON converts a jsonAPI http handler to a proper http.Handler.
+func handlerJSON(t *testing.T, handler jsonAPI) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ //ut.ExpectEqual(t, jsonContentType, r.Header.Get("Content-Type"))
+ defer r.Body.Close()
+ out := handler(r.Body)
+ if out == nil {
+ w.WriteHeader(500)
+ } else {
+ w.Header().Set("Content-Type", jsonContentType)
+ ut.ExpectEqual(t, nil, json.NewEncoder(w).Encode(out))
+ }
+ })
+}

Powered by Google App Engine
This is Rietveld 408576698