github.com/ethersphere/bee/v2@v2.2.0/pkg/jsonhttp/jsonhttptest/jsonhttptest_test.go (about) 1 // Copyright 2020 The Swarm Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package jsonhttptest_test 6 7 import ( 8 "bytes" 9 "context" 10 "encoding/json" 11 "errors" 12 "fmt" 13 "io" 14 "mime" 15 "mime/multipart" 16 "net/http" 17 "net/http/httptest" 18 "strconv" 19 "strings" 20 "testing" 21 22 "github.com/ethersphere/bee/v2/pkg/jsonhttp" 23 "github.com/ethersphere/bee/v2/pkg/jsonhttp/jsonhttptest" 24 ) 25 26 func TestRequest_statusCode(t *testing.T) { 27 t.Parallel() 28 29 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 30 w.WriteHeader(http.StatusBadRequest) 31 })) 32 33 assert(t, testResult{}, func(m *mock) { 34 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusBadRequest) 35 }) 36 37 tr := testResult{ 38 errors: []string{`got response status 400 Bad Request, want 200 OK`}, 39 } 40 assert(t, tr, func(m *mock) { 41 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK) 42 }) 43 } 44 45 func TestRequest_method(t *testing.T) { 46 t.Parallel() 47 48 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 49 if r.Method != http.MethodGet { 50 w.WriteHeader(http.StatusMethodNotAllowed) 51 return 52 } 53 })) 54 55 assert(t, testResult{}, func(m *mock) { 56 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK) 57 }) 58 59 assert(t, testResult{}, func(m *mock) { 60 jsonhttptest.Request(m, c, http.MethodPost, endpoint, http.StatusMethodNotAllowed) 61 }) 62 } 63 64 func TestRequest_url(t *testing.T) { 65 t.Parallel() 66 67 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 68 if r.URL.Path != "/" { 69 w.WriteHeader(http.StatusNotFound) 70 } 71 })) 72 73 assert(t, testResult{}, func(m *mock) { 74 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK) 75 }) 76 77 assert(t, testResult{}, func(m *mock) { 78 jsonhttptest.Request(m, c, http.MethodPost, endpoint+"/bzz", http.StatusNotFound) 79 }) 80 } 81 82 func TestRequest_responseHeader(t *testing.T) { 83 t.Parallel() 84 85 headerName := "Swarm-Header" 86 headerValue := "Digital Freedom Now" 87 88 t.Run("with header", func(t *testing.T) { 89 t.Parallel() 90 91 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 92 w.Header().Set(headerName, headerValue) 93 })) 94 95 assert(t, testResult{}, func(m *mock) { 96 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 97 jsonhttptest.WithExpectedResponseHeader(headerName, headerValue), 98 jsonhttptest.WithNonEmptyResponseHeader(headerName), 99 ) 100 }) 101 }) 102 103 t.Run("without header", func(t *testing.T) { 104 t.Parallel() 105 106 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 107 108 tr := testResult{ 109 errors: []string{ 110 fmt.Sprintf(`header key=[%v] should be set`, headerName), 111 fmt.Sprintf(`header values for key=[%v] not as expected, got: [], want [%v]`, headerName, headerValue), 112 }, 113 } 114 115 assert(t, tr, func(m *mock) { 116 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 117 jsonhttptest.WithNonEmptyResponseHeader(headerName), 118 jsonhttptest.WithExpectedResponseHeader(headerName, headerValue), 119 ) 120 }) 121 }) 122 } 123 124 func TestWithContext(t *testing.T) { 125 t.Parallel() 126 127 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 128 129 ctx, cancel := context.WithCancel(context.Background()) 130 cancel() // cancel the context to detect the fatal error 131 132 tr := testResult{ 133 fatal: fmt.Sprintf("Get %q: context canceled", endpoint), 134 } 135 assert(t, tr, func(m *mock) { 136 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 137 jsonhttptest.WithContext(ctx), 138 ) 139 }) 140 } 141 142 func TestWithRequestBody(t *testing.T) { 143 t.Parallel() 144 145 wantBody := []byte("somebody") 146 var gotBody []byte 147 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 148 var err error 149 gotBody, err = io.ReadAll(r.Body) 150 if err != nil { 151 w.WriteHeader(http.StatusInternalServerError) 152 } 153 })) 154 155 assert(t, testResult{}, func(m *mock) { 156 jsonhttptest.Request(m, c, http.MethodPost, endpoint, http.StatusOK, 157 jsonhttptest.WithRequestBody(bytes.NewReader(wantBody)), 158 ) 159 }) 160 if !bytes.Equal(gotBody, wantBody) { 161 t.Errorf("got body %q, want %q", string(gotBody), string(wantBody)) 162 } 163 } 164 165 func TestWithJSONRequestBody(t *testing.T) { 166 t.Parallel() 167 168 type response struct { 169 Message string `json:"message"` 170 } 171 message := "text" 172 173 wantBody := response{ 174 Message: message, 175 } 176 var gotBody response 177 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 178 v, err := io.ReadAll(r.Body) 179 if err != nil { 180 jsonhttp.InternalServerError(w, err) 181 return 182 } 183 if err := json.Unmarshal(v, &gotBody); err != nil { 184 jsonhttp.BadRequest(w, err) 185 return 186 } 187 })) 188 189 assert(t, testResult{}, func(m *mock) { 190 jsonhttptest.Request(m, c, http.MethodPost, endpoint, http.StatusOK, 191 jsonhttptest.WithJSONRequestBody(wantBody), 192 ) 193 }) 194 if gotBody.Message != message { 195 t.Errorf("got message %q, want %q", gotBody.Message, message) 196 } 197 } 198 199 func TestWithMultipartRequest(t *testing.T) { 200 t.Parallel() 201 202 wantBody := []byte("somebody") 203 filename := "swarm.jpg" 204 contentType := "image/jpeg" 205 var gotBody []byte 206 var gotContentDisposition, gotContentType string 207 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 208 mediaType, params, err := mime.ParseMediaType(r.Header.Get("Content-Type")) 209 if err != nil { 210 jsonhttp.BadRequest(w, err) 211 return 212 } 213 if strings.HasPrefix(mediaType, "multipart/") { 214 mr := multipart.NewReader(r.Body, params["boundary"]) 215 216 p, err := mr.NextPart() 217 if err != nil { 218 if errors.Is(err, io.EOF) { 219 return 220 } 221 jsonhttp.BadRequest(w, err) 222 return 223 } 224 gotContentDisposition = p.Header.Get("Content-Disposition") 225 gotContentType = p.Header.Get("Content-Type") 226 gotBody, err = io.ReadAll(p) 227 if err != nil { 228 jsonhttp.BadRequest(w, err) 229 return 230 } 231 } 232 })) 233 234 assert(t, testResult{}, func(m *mock) { 235 jsonhttptest.Request(m, c, http.MethodPost, endpoint, http.StatusOK, 236 jsonhttptest.WithMultipartRequest(bytes.NewReader(wantBody), len(wantBody), filename, contentType), 237 ) 238 }) 239 if !bytes.Equal(gotBody, wantBody) { 240 t.Errorf("got body %q, want %q", string(gotBody), string(wantBody)) 241 } 242 if gotContentType != contentType { 243 t.Errorf("got content type %q, want %q", gotContentType, contentType) 244 } 245 if contentDisposition := fmt.Sprintf("form-data; name=%q", filename); gotContentDisposition != contentDisposition { 246 t.Errorf("got content disposition %q, want %q", gotContentDisposition, contentDisposition) 247 } 248 } 249 250 func TestWithRequestHeader(t *testing.T) { 251 t.Parallel() 252 253 headerName := "Swarm-Header" 254 headerValue := "somevalue" 255 var gotValue string 256 257 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 258 gotValue = r.Header.Get(headerName) 259 })) 260 261 assert(t, testResult{}, func(m *mock) { 262 jsonhttptest.Request(m, c, http.MethodPost, endpoint, http.StatusOK, 263 jsonhttptest.WithRequestHeader(headerName, headerValue), 264 ) 265 }) 266 if gotValue != headerValue { 267 t.Errorf("got header %q, want %q", gotValue, headerValue) 268 } 269 } 270 271 func TestWithExpectedContentLength(t *testing.T) { 272 t.Parallel() 273 274 body := []byte("Digital Freedom Now") 275 276 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 277 w.Header().Set("Content-Length", strconv.Itoa(len(body))) 278 _, err := w.Write(body) 279 if err != nil { 280 t.Error(err) 281 } 282 })) 283 284 assert(t, testResult{}, func(m *mock) { 285 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 286 jsonhttptest.WithExpectedContentLength(len(body)), 287 ) 288 }) 289 290 tr := testResult{ 291 errors: []string{ 292 "header values for key=[Content-Length] not as expected, got: [19], want [100]", 293 "http.Response.ContentLength not as expected, got 19, want 100", 294 }, 295 } 296 assert(t, tr, func(m *mock) { 297 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 298 jsonhttptest.WithExpectedContentLength(100), 299 ) 300 }) 301 } 302 303 func TestWithExpectedResponse(t *testing.T) { 304 t.Parallel() 305 306 body := []byte("something to want") 307 308 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 309 _, err := w.Write(body) 310 if err != nil { 311 jsonhttp.InternalServerError(w, err) 312 } 313 })) 314 315 assert(t, testResult{}, func(m *mock) { 316 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 317 jsonhttptest.WithExpectedResponse(body), 318 ) 319 }) 320 321 tr := testResult{ 322 errors: []string{`got response "something to want", want "invalid"`}, 323 } 324 assert(t, tr, func(m *mock) { 325 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 326 jsonhttptest.WithExpectedResponse([]byte("invalid")), 327 ) 328 }) 329 } 330 331 func TestWithExpectedJSONResponse(t *testing.T) { 332 t.Parallel() 333 334 type response struct { 335 Message string `json:"message"` 336 } 337 338 want := response{ 339 Message: "text", 340 } 341 342 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 343 jsonhttp.OK(w, want) 344 })) 345 346 assert(t, testResult{}, func(m *mock) { 347 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 348 jsonhttptest.WithExpectedJSONResponse(want), 349 ) 350 }) 351 352 tr := testResult{ 353 errors: []string{`got json response "{\"message\":\"text\"}", want "{\"message\":\"invalid\"}"`}, 354 } 355 assert(t, tr, func(m *mock) { 356 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 357 jsonhttptest.WithExpectedJSONResponse(response{ 358 Message: "invalid", 359 }), 360 ) 361 }) 362 } 363 364 func TestWithUnmarhalJSONResponse(t *testing.T) { 365 t.Parallel() 366 367 message := "text" 368 369 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 370 jsonhttp.OK(w, message) 371 })) 372 373 var r jsonhttp.StatusResponse 374 assert(t, testResult{}, func(m *mock) { 375 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 376 jsonhttptest.WithUnmarshalJSONResponse(&r), 377 ) 378 }) 379 if r.Message != message { 380 t.Errorf("got message %q, want %q", r.Message, message) 381 } 382 } 383 384 func TestWithPutResponseBody(t *testing.T) { 385 t.Parallel() 386 387 wantBody := []byte("somebody") 388 389 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 390 _, err := w.Write(wantBody) 391 if err != nil { 392 jsonhttp.InternalServerError(w, err) 393 } 394 })) 395 396 var gotBody []byte 397 assert(t, testResult{}, func(m *mock) { 398 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 399 jsonhttptest.WithPutResponseBody(&gotBody), 400 ) 401 }) 402 if !bytes.Equal(gotBody, wantBody) { 403 t.Errorf("got body %q, want %q", string(gotBody), string(wantBody)) 404 } 405 } 406 407 func TestWithNoResponseBody(t *testing.T) { 408 t.Parallel() 409 410 c, endpoint := newClient(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 411 if r.URL.Path != "/" { 412 fmt.Fprint(w, "not found") 413 } 414 })) 415 416 assert(t, testResult{}, func(m *mock) { 417 jsonhttptest.Request(m, c, http.MethodGet, endpoint, http.StatusOK, 418 jsonhttptest.WithNoResponseBody(), 419 ) 420 }) 421 422 tr := testResult{ 423 errors: []string{`got response body "not found", want none`}, 424 } 425 assert(t, tr, func(m *mock) { 426 jsonhttptest.Request(m, c, http.MethodGet, endpoint+"/bzz", http.StatusOK, 427 jsonhttptest.WithNoResponseBody(), 428 ) 429 }) 430 } 431 432 func newClient(t *testing.T, handler http.Handler) (c *http.Client, endpoint string) { 433 t.Helper() 434 435 s := httptest.NewServer(handler) 436 t.Cleanup(s.Close) 437 return s.Client(), s.URL 438 }