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 }