github.com/apcera/util@v0.0.0-20180322191801-7a50bc84ee48/restclient/restclient_test.go (about)

     1  // Copyright 2012-2014 Apcera Inc. All rights reserved.
     2  
     3  package restclient
     4  
     5  import (
     6  	"encoding/json"
     7  	"fmt"
     8  	"io"
     9  	"io/ioutil"
    10  	"net/http"
    11  	"net/http/httptest"
    12  	"net/url"
    13  	"strings"
    14  	"testing"
    15  
    16  	tt "github.com/apcera/util/testtool"
    17  )
    18  
    19  type person struct {
    20  	Name string
    21  	Age  int
    22  }
    23  
    24  func TestResourceURL(t *testing.T) {
    25  	testHelper := tt.StartTest(t)
    26  	defer testHelper.FinishTest()
    27  
    28  	base := "http://example.com:8080/v1/resources"
    29  	baseURL, err := url.Parse(base)
    30  	tt.TestExpectSuccess(t, err)
    31  
    32  	examples := []struct{ in, out string }{
    33  		// Returns base when path == ""
    34  		{"", "http://example.com:8080/v1/resources"},
    35  		// Adds relative paths to end
    36  		{"items/1234", "http://example.com:8080/v1/resources/items/1234"},
    37  		{"/items/1234", "http://example.com:8080/v1/resources/items/1234"},
    38  	}
    39  
    40  	for i, ex := range examples {
    41  		u := resourceURL(baseURL, ex.in)
    42  		if u.String() != ex.out {
    43  			t.Errorf("%d. resourceURL(..., %q) resolved incorrectly.\nhave: %s\nwant: %s",
    44  				i, ex.in, u.String(), ex.out)
    45  		}
    46  	}
    47  }
    48  
    49  func TestHelper_newRequest(t *testing.T) {
    50  	testHelper := tt.StartTest(t)
    51  	defer testHelper.FinishTest()
    52  
    53  	client, err := New("http://example.com/resources")
    54  	tt.TestExpectSuccess(t, err)
    55  	req := client.newRequest(GET, "/foos")
    56  
    57  	tt.TestEqual(t, req.Method, GET)
    58  	tt.TestEqual(t, req.URL.String(), "http://example.com/resources/foos")
    59  	tt.TestEqual(t, req.Headers, http.Header(map[string][]string{}))
    60  }
    61  
    62  func TestHelper_newRequestWithParams(t *testing.T) {
    63  	testHelper := tt.StartTest(t)
    64  	defer testHelper.FinishTest()
    65  
    66  	client, err := New("http://example.com/resources")
    67  	tt.TestExpectSuccess(t, err)
    68  	req := client.newRequest(GET, "/excellence?yeah=stuff&more=params")
    69  
    70  	tt.TestEqual(t, req.Method, GET)
    71  	tt.TestEqual(t, req.URL.String(), "http://example.com/resources/excellence?yeah=stuff&more=params")
    72  	tt.TestEqual(t, req.Headers, http.Header(map[string][]string{}))
    73  }
    74  
    75  func TestNewRequest(t *testing.T) {
    76  	testHelper := tt.StartTest(t)
    77  	defer testHelper.FinishTest()
    78  
    79  	// create a test server
    80  	method, path, body := "", "", ""
    81  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
    82  		method = req.Method
    83  		path = req.URL.Path
    84  
    85  		defer req.Body.Close()
    86  		b, err := ioutil.ReadAll(req.Body)
    87  		if err != nil {
    88  			t.Errorf("Error reading request: %v", err)
    89  			w.WriteHeader(500)
    90  			return
    91  		}
    92  		body = string(b)
    93  		w.WriteHeader(200)
    94  	}))
    95  	defer server.Close()
    96  
    97  	client, err := New(server.URL)
    98  	tt.TestExpectSuccess(t, err)
    99  	req := client.NewRequest("POST", "/blobs", "text/plain", strings.NewReader("I am a giant blob of bytes!"))
   100  	err = client.Result(req, nil)
   101  	tt.TestExpectSuccess(t, err)
   102  
   103  	// Verify request as received by server
   104  	tt.TestEqual(t, method, "POST")
   105  	tt.TestEqual(t, path, "/blobs")
   106  	tt.TestEqual(t, body, "I am a giant blob of bytes!")
   107  }
   108  
   109  func TestNewRequestHeaders(t *testing.T) {
   110  	testHelper := tt.StartTest(t)
   111  	defer testHelper.FinishTest()
   112  
   113  	// create a test server
   114  	headerValue := ""
   115  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   116  		defer req.Body.Close()
   117  		headerValue = req.Header.Get("Sample")
   118  		w.WriteHeader(200)
   119  	}))
   120  	defer server.Close()
   121  
   122  	client, err := New(server.URL)
   123  	tt.TestExpectSuccess(t, err)
   124  	client.Headers.Set("Sample", "applesauce")
   125  	req := client.NewRequest("POST", "/blobs", "text/plain", strings.NewReader("I am a giant blob of bytes!"))
   126  	err = client.Result(req, nil)
   127  	tt.TestExpectSuccess(t, err)
   128  
   129  	// Verify request to the server had the correct header value
   130  	tt.TestEqual(t, headerValue, "applesauce")
   131  }
   132  
   133  func TestBasicJsonRequest(t *testing.T) {
   134  	testHelper := tt.StartTest(t)
   135  	defer testHelper.FinishTest()
   136  
   137  	// create a test server
   138  	method, path, body := "", "", ""
   139  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   140  		method = req.Method
   141  		path = req.URL.Path
   142  
   143  		defer req.Body.Close()
   144  		b, err := ioutil.ReadAll(req.Body)
   145  		if err != nil {
   146  			t.Errorf("Error reading request: %v", err)
   147  			w.WriteHeader(500)
   148  			return
   149  		}
   150  		body = string(b)
   151  		w.Header().Set("Content-Type", "application/json")
   152  		w.WriteHeader(200)
   153  		io.WriteString(w, `{"foo":"bar"}`)
   154  	}))
   155  	defer server.Close()
   156  
   157  	client, err := New(server.URL)
   158  	tt.TestExpectSuccess(t, err)
   159  	req := client.NewJsonRequest("POST", "/foo", map[string]string{"bar": "baz"})
   160  
   161  	var res map[string]string
   162  	err = client.Result(req, &res)
   163  	tt.TestExpectSuccess(t, err)
   164  
   165  	// Verify request as received by server
   166  	tt.TestEqual(t, method, "POST")
   167  	tt.TestEqual(t, path, "/foo")
   168  	tt.TestEqual(t, body, `{"bar":"baz"}`+"\n")
   169  
   170  	// Verify response was parsed by client
   171  	tt.TestEqual(t, len(res), 1)
   172  	tt.TestEqual(t, res["foo"], "bar")
   173  }
   174  
   175  func TestJsonStructRequest(t *testing.T) {
   176  	testHelper := tt.StartTest(t)
   177  	defer testHelper.FinishTest()
   178  
   179  	var receivedPerson *person
   180  
   181  	// create a test server
   182  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   183  		defer req.Body.Close()
   184  		decoder := json.NewDecoder(req.Body)
   185  		err := decoder.Decode(&receivedPerson)
   186  		if err != nil {
   187  			t.Errorf("Error reading request: %v", err)
   188  			w.WriteHeader(500)
   189  			return
   190  		}
   191  		w.Header().Set("Content-Type", "application/json")
   192  		w.WriteHeader(200)
   193  		io.WriteString(w, `{"Name":"Molly","Age":45}`)
   194  	}))
   195  	defer server.Close()
   196  
   197  	client, err := New(server.URL)
   198  	tt.TestExpectSuccess(t, err)
   199  	req := client.NewJsonRequest("POST", "/", person{Name: "John", Age: 56})
   200  
   201  	var responsePerson person
   202  	err = client.Result(req, &responsePerson)
   203  	tt.TestExpectSuccess(t, err)
   204  
   205  	tt.TestEqual(t, receivedPerson.Name, "John")
   206  	tt.TestEqual(t, receivedPerson.Age, 56)
   207  
   208  	tt.TestEqual(t, responsePerson.Name, "Molly")
   209  	tt.TestEqual(t, responsePerson.Age, 45)
   210  }
   211  
   212  func TestFormRequest(t *testing.T) {
   213  	testHelper := tt.StartTest(t)
   214  	defer testHelper.FinishTest()
   215  
   216  	// create a test server
   217  	var form url.Values
   218  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   219  		if err := req.ParseForm(); err != nil {
   220  			t.Errorf("Error reading request: %v", err)
   221  			w.WriteHeader(500)
   222  			return
   223  		}
   224  		form = req.Form
   225  		w.Header().Set("Content-Type", "application/json")
   226  		w.WriteHeader(200)
   227  		io.WriteString(w, `{"foo":"bar"}`)
   228  	}))
   229  	defer server.Close()
   230  
   231  	client, err := New(server.URL)
   232  	tt.TestExpectSuccess(t, err)
   233  	req := client.NewFormRequest("POST", "/", map[string]string{"name": "Tim"})
   234  	err = client.Result(req, nil)
   235  	tt.TestExpectSuccess(t, err)
   236  
   237  	// Verify form data as received by server
   238  	tt.TestEqual(t, form.Get("name"), "Tim")
   239  }
   240  
   241  func TestErrorResult(t *testing.T) {
   242  	testHelper := tt.StartTest(t)
   243  	defer testHelper.FinishTest()
   244  
   245  	// create a test server
   246  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   247  		w.Header().Set("Content-Type", "text/plain")
   248  		w.WriteHeader(500)
   249  		io.WriteString(w, "Didn't work")
   250  	}))
   251  	defer server.Close()
   252  
   253  	client, err := New(server.URL)
   254  	tt.TestExpectSuccess(t, err)
   255  	req := client.NewFormRequest("GET", "/", nil)
   256  	err = client.Result(req, nil)
   257  	tt.TestExpectError(t, err)
   258  
   259  	rerr, ok := err.(*RestError)
   260  	tt.TestEqual(t, ok, true, "Error should be of type *RestError")
   261  	tt.TestEqual(t, rerr.Error(), "error in response: 500 Internal Server Error - Didn't work")
   262  	tt.TestEqual(t, rerr.Body(), "Didn't work")
   263  
   264  	rerr2 := new(RestError)
   265  	rerr2.err = fmt.Errorf("foo bar baz wibble")
   266  	tt.TestEqual(t, rerr2.Error(), "foo bar baz wibble")
   267  
   268  	rerr2 = new(RestError)
   269  	rerr2.Resp = &http.Response{
   270  		StatusCode: 404,
   271  	}
   272  	rerr2.err = fmt.Errorf("foo bar baz wibble")
   273  	tt.TestEqual(t, rerr2.Error(), "foo bar baz wibble")
   274  }
   275  
   276  func TestErrorResponse(t *testing.T) {
   277  	testHelper := tt.StartTest(t)
   278  	defer testHelper.FinishTest()
   279  
   280  	// create a test server
   281  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   282  		w.Header().Set("Content-Type", "text/plain")
   283  		w.WriteHeader(500)
   284  		io.WriteString(w, "Didn't work")
   285  	}))
   286  	defer server.Close()
   287  
   288  	client, err := New(server.URL)
   289  	tt.TestExpectSuccess(t, err)
   290  	req := client.NewFormRequest("GET", "/", nil)
   291  	resp, err := client.Do(req)
   292  	tt.TestExpectError(t, err)
   293  
   294  	rerr, ok := err.(*RestError)
   295  	tt.TestEqual(t, ok, true, "Error should be of type *RestError")
   296  	tt.TestEqual(t, rerr.Error(), "error in response: 500 Internal Server Error - Didn't work")
   297  	tt.TestEqual(t, rerr.Body(), "Didn't work")
   298  
   299  	body, err := ioutil.ReadAll(resp.Body)
   300  	defer resp.Body.Close()
   301  	tt.TestExpectSuccess(t, err)
   302  	tt.TestEqual(t, string(body), "Didn't work")
   303  }
   304  
   305  func TestErrorResponseWithJson(t *testing.T) {
   306  	testHelper := tt.StartTest(t)
   307  	defer testHelper.FinishTest()
   308  
   309  	// create a test server
   310  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   311  		w.Header().Set("Content-Type", "text/plain")
   312  		w.WriteHeader(500)
   313  		io.WriteString(w, `{"error": "some error"}`)
   314  	}))
   315  	defer server.Close()
   316  
   317  	client, err := New(server.URL)
   318  	tt.TestExpectSuccess(t, err)
   319  	req := client.NewFormRequest("GET", "/", nil)
   320  	resp, err := client.Do(req)
   321  	tt.TestExpectError(t, err)
   322  
   323  	rerr, ok := err.(*RestError)
   324  	tt.TestEqual(t, ok, true, "Error should be of type *RestError")
   325  	tt.TestEqual(t, rerr.Error(), "error in response: 500 Internal Server Error - some error")
   326  	tt.TestEqual(t, rerr.Body(), `{"error": "some error"}`)
   327  
   328  	body, err := ioutil.ReadAll(resp.Body)
   329  	defer resp.Body.Close()
   330  	tt.TestExpectSuccess(t, err)
   331  	tt.TestEqual(t, string(body), `{"error": "some error"}`)
   332  }
   333  
   334  func TestErrorResponseNoBody(t *testing.T) {
   335  	testHelper := tt.StartTest(t)
   336  	defer testHelper.FinishTest()
   337  
   338  	// create a test server
   339  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   340  		w.Header().Set("Content-Type", "text/plain")
   341  		w.WriteHeader(500)
   342  	}))
   343  	defer server.Close()
   344  
   345  	client, err := New(server.URL)
   346  	tt.TestExpectSuccess(t, err)
   347  	req := client.NewFormRequest("GET", "/", nil)
   348  	resp, err := client.Do(req)
   349  	tt.TestExpectError(t, err)
   350  
   351  	rerr, ok := err.(*RestError)
   352  	tt.TestEqual(t, ok, true, "Error should be of type *RestError")
   353  	tt.TestEqual(t, rerr.Error(), "error in response: 500 Internal Server Error")
   354  
   355  	body, err := ioutil.ReadAll(resp.Body)
   356  	defer resp.Body.Close()
   357  	tt.TestExpectSuccess(t, err)
   358  	tt.TestEqual(t, string(body), "")
   359  }
   360  
   361  func TestInvalidJsonResponse(t *testing.T) {
   362  	testHelper := tt.StartTest(t)
   363  	defer testHelper.FinishTest()
   364  
   365  	// create a test server
   366  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   367  		w.Header().Set("Content-Type", "application/json")
   368  		w.WriteHeader(200)
   369  		io.WriteString(w, `"Name":"Molly","Age":45}`)
   370  	}))
   371  	defer server.Close()
   372  
   373  	client, err := New(server.URL)
   374  	tt.TestExpectSuccess(t, err)
   375  	req := client.NewJsonRequest("GET", "/", nil)
   376  
   377  	var responsePerson person
   378  	err = client.Result(req, &responsePerson)
   379  	tt.TestExpectError(t, err)
   380  	tt.TestNotEqual(t, err.(*json.UnmarshalTypeError), nil, "Should have been a json unmarshal error")
   381  }
   382  
   383  // TestParseMimetype ensures that client.Result handles the expected media types
   384  // correctly.
   385  func TestParseMimetype(t *testing.T) {
   386  	testHelper := tt.StartTest(t)
   387  	defer testHelper.FinishTest()
   388  
   389  	testCases := []struct {
   390  		desc           string
   391  		handler        http.Handler
   392  		expectedPerson person
   393  		expectedErr    error
   394  	}{
   395  		{
   396  			desc: "Response with application/json should work",
   397  			handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   398  				w.Header().Set("Content-Type", "application/json; charset=utf-8")
   399  				w.WriteHeader(200)
   400  				io.WriteString(w, `{"Name":"Molly","Age":45}`)
   401  			}),
   402  			expectedPerson: person{Name: "Molly", Age: 45},
   403  		},
   404  		{
   405  			desc: "Response with application/vnd type should work",
   406  			handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   407  				w.Header().Set("Content-Type", "application/vnd.Foo; charset=utf-8")
   408  				w.WriteHeader(200)
   409  				io.WriteString(w, `{"Name":"Molly","Age":45}`)
   410  			}),
   411  			expectedPerson: person{Name: "Molly", Age: 45},
   412  		},
   413  		{
   414  			desc: "Response with +json suffix should work",
   415  			handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   416  				w.Header().Set("Content-Type", "application/foo+json; charset=utf-8")
   417  				w.WriteHeader(200)
   418  				io.WriteString(w, `{"Name":"Molly","Age":45}`)
   419  			}),
   420  			expectedPerson: person{Name: "Molly", Age: 45},
   421  		},
   422  		{
   423  			desc: "Response with any other type should error",
   424  			handler: http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   425  				w.Header().Set("Content-Type", "foo")
   426  				w.WriteHeader(200)
   427  				io.WriteString(w, `{"Name":"Molly","Age":45}`)
   428  			}),
   429  			expectedErr: fmt.Errorf("unexpected response: 200 OK foo"),
   430  		},
   431  	}
   432  
   433  	for _, tc := range testCases {
   434  		// Create test server; defer close will leave them open for the duration
   435  		// longer than one loop but will ensure they all get shutdown even when
   436  		// testing fails.
   437  		server := httptest.NewServer(tc.handler)
   438  		defer server.Close()
   439  		client, err := New(server.URL)
   440  		tt.TestExpectSuccess(t, err)
   441  
   442  		req := client.NewJsonRequest("GET", "/", nil)
   443  		var responsePerson person
   444  		err = client.Result(req, &responsePerson)
   445  		tt.TestEqual(t, err, tc.expectedErr, tc.desc)
   446  		tt.TestEqual(t, responsePerson, tc.expectedPerson, tc.desc)
   447  	}
   448  }
   449  
   450  func TestEmptyPostRequest(t *testing.T) {
   451  	testHelper := tt.StartTest(t)
   452  	defer testHelper.FinishTest()
   453  
   454  	// create a test server
   455  	body := ""
   456  	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
   457  		defer req.Body.Close()
   458  		b, err := ioutil.ReadAll(req.Body)
   459  		if err != nil {
   460  			t.Errorf("Error reading request: %v", err)
   461  			w.WriteHeader(500)
   462  			return
   463  		}
   464  		body = string(b)
   465  		w.WriteHeader(200)
   466  	}))
   467  	defer server.Close()
   468  
   469  	client, err := New(server.URL)
   470  	tt.TestExpectSuccess(t, err)
   471  	req := client.NewJsonRequest("POST", "/", nil)
   472  
   473  	err = client.Result(req, nil)
   474  	tt.TestExpectSuccess(t, err)
   475  	tt.TestEqual(t, body, "")
   476  }