go.sdls.io/sin@v0.0.9/pkg/sin/context_test.go (about) 1 // Copyright 2014 Manu Martinez-Almeida. All rights reserved. 2 // Use of this source code is governed by a MIT style 3 // license that can be found in the LICENSE file. 4 5 package sin 6 7 import ( 8 "bytes" 9 "context" 10 "errors" 11 "fmt" 12 "mime/multipart" 13 "net/http" 14 "net/http/httptest" 15 "reflect" 16 "strings" 17 "sync" 18 "testing" 19 "time" 20 21 "github.com/stretchr/testify/assert" 22 ) 23 24 var _ context.Context = &Context{} 25 26 var errTestPanicRender = errors.New("TestPanicRender") 27 28 // Unit tests TODO 29 // func (c *Context) File(filepath string) { 30 // func (c *Context) Negotiate(code int, config Negotiate) { 31 // BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) { 32 // test that information is not leaked when reusing Contexts (using the Pool) 33 34 func TestContextFormFileFailed(t *testing.T) { 35 buf := new(bytes.Buffer) 36 mw := multipart.NewWriter(buf) 37 _ = mw.Close() 38 c, _ := CreateTestContext(httptest.NewRecorder()) 39 c.Request, _ = http.NewRequest("POST", "/", nil) 40 c.Request.Header.Set("Content-Type", mw.FormDataContentType()) 41 c.engine.MaxMultipartMemory = 8 << 20 42 f, err := c.FormFile("file") 43 assert.Error(t, err) 44 assert.Nil(t, f) 45 } 46 47 func TestContextReset(t *testing.T) { 48 router := New() 49 c := router.allocateContext() 50 assert.Equal(t, c.engine, router) 51 52 c.index = 2 53 c.Writer = &responseWriter{ResponseWriter: httptest.NewRecorder()} 54 c.Params = Params{Param{}} 55 c.Error(errors.New("test")) // nolint: errcheck 56 c.Set("foo", "bar") 57 c.reset() 58 59 assert.False(t, c.IsAborted()) 60 assert.Nil(t, c.Keys) 61 assert.Len(t, c.Errors, 0) 62 assert.Empty(t, c.Errors.Errors()) 63 assert.Empty(t, c.Errors.ByType(ErrorTypeAny)) 64 assert.Len(t, c.Params, 0) 65 assert.EqualValues(t, c.index, -1) 66 assert.Equal(t, c.Writer.(*responseWriter), &c.writermem) 67 } 68 69 func TestContextHandlers(t *testing.T) { 70 c, _ := CreateTestContext(httptest.NewRecorder()) 71 assert.Nil(t, c.handlers) 72 assert.Nil(t, c.handlers.Last()) 73 74 c.handlers = HandlersChain{} 75 assert.NotNil(t, c.handlers) 76 assert.Nil(t, c.handlers.Last()) 77 78 f := func(c *Context) {} 79 g := func(c *Context) {} 80 81 c.handlers = HandlersChain{f} 82 compareFunc(t, f, c.handlers.Last()) 83 84 c.handlers = HandlersChain{f, g} 85 compareFunc(t, g, c.handlers.Last()) 86 } 87 88 // TestContextSetGet tests that a parameter is set correctly on the 89 // current context and can be retrieved using Get. 90 func TestContextSetGet(t *testing.T) { 91 c, _ := CreateTestContext(httptest.NewRecorder()) 92 c.Set("foo", "bar") 93 94 value, err := c.Get("foo") 95 assert.Equal(t, "bar", value) 96 assert.True(t, err) 97 98 value, err = c.Get("foo2") 99 assert.Nil(t, value) 100 assert.False(t, err) 101 102 assert.Equal(t, "bar", c.MustGet("foo")) 103 assert.Panics(t, func() { c.MustGet("no_exist") }) 104 } 105 106 func TestContextSetGetValues(t *testing.T) { 107 c, _ := CreateTestContext(httptest.NewRecorder()) 108 c.Set("string", "this is a string") 109 c.Set("int32", int32(-42)) 110 c.Set("int64", int64(42424242424242)) 111 c.Set("uint64", uint64(42)) 112 c.Set("float32", float32(4.2)) 113 c.Set("float64", 4.2) 114 var a interface{} = 1 115 c.Set("intInterface", a) 116 117 assert.Exactly(t, c.MustGet("string").(string), "this is a string") 118 assert.Exactly(t, c.MustGet("int32").(int32), int32(-42)) 119 assert.Exactly(t, c.MustGet("int64").(int64), int64(42424242424242)) 120 assert.Exactly(t, c.MustGet("uint64").(uint64), uint64(42)) 121 assert.Exactly(t, c.MustGet("float32").(float32), float32(4.2)) 122 assert.Exactly(t, c.MustGet("float64").(float64), 4.2) 123 assert.Exactly(t, c.MustGet("intInterface").(int), 1) 124 } 125 126 func TestContextGetString(t *testing.T) { 127 c, _ := CreateTestContext(httptest.NewRecorder()) 128 c.Set("string", "this is a string") 129 assert.Equal(t, "this is a string", c.GetString("string")) 130 } 131 132 func TestContextSetGetBool(t *testing.T) { 133 c, _ := CreateTestContext(httptest.NewRecorder()) 134 c.Set("bool", true) 135 assert.True(t, c.GetBool("bool")) 136 } 137 138 func TestContextGetInt(t *testing.T) { 139 c, _ := CreateTestContext(httptest.NewRecorder()) 140 c.Set("int", 1) 141 assert.Equal(t, 1, c.GetInt("int")) 142 } 143 144 func TestContextGetInt64(t *testing.T) { 145 c, _ := CreateTestContext(httptest.NewRecorder()) 146 c.Set("int64", int64(42424242424242)) 147 assert.Equal(t, int64(42424242424242), c.GetInt64("int64")) 148 } 149 150 func TestContextGetUint(t *testing.T) { 151 c, _ := CreateTestContext(httptest.NewRecorder()) 152 c.Set("uint", uint(1)) 153 assert.Equal(t, uint(1), c.GetUint("uint")) 154 } 155 156 func TestContextGetUint64(t *testing.T) { 157 c, _ := CreateTestContext(httptest.NewRecorder()) 158 c.Set("uint64", uint64(18446744073709551615)) 159 assert.Equal(t, uint64(18446744073709551615), c.GetUint64("uint64")) 160 } 161 162 func TestContextGetFloat64(t *testing.T) { 163 c, _ := CreateTestContext(httptest.NewRecorder()) 164 c.Set("float64", 4.2) 165 assert.Equal(t, 4.2, c.GetFloat64("float64")) 166 } 167 168 func TestContextGetTime(t *testing.T) { 169 c, _ := CreateTestContext(httptest.NewRecorder()) 170 t1, _ := time.Parse("1/2/2006 15:04:05", "01/01/2017 12:00:00") 171 c.Set("time", t1) 172 assert.Equal(t, t1, c.GetTime("time")) 173 } 174 175 func TestContextGetDuration(t *testing.T) { 176 c, _ := CreateTestContext(httptest.NewRecorder()) 177 c.Set("duration", time.Second) 178 assert.Equal(t, time.Second, c.GetDuration("duration")) 179 } 180 181 func TestContextGetStringSlice(t *testing.T) { 182 c, _ := CreateTestContext(httptest.NewRecorder()) 183 c.Set("slice", []string{"foo"}) 184 assert.Equal(t, []string{"foo"}, c.GetStringSlice("slice")) 185 } 186 187 func TestContextGetStringMap(t *testing.T) { 188 c, _ := CreateTestContext(httptest.NewRecorder()) 189 m := make(map[string]interface{}) 190 m["foo"] = 1 191 c.Set("map", m) 192 193 assert.Equal(t, m, c.GetStringMap("map")) 194 assert.Equal(t, 1, c.GetStringMap("map")["foo"]) 195 } 196 197 func TestContextGetStringMapString(t *testing.T) { 198 c, _ := CreateTestContext(httptest.NewRecorder()) 199 m := make(map[string]string) 200 m["foo"] = "bar" 201 c.Set("map", m) 202 203 assert.Equal(t, m, c.GetStringMapString("map")) 204 assert.Equal(t, "bar", c.GetStringMapString("map")["foo"]) 205 } 206 207 func TestContextGetStringMapStringSlice(t *testing.T) { 208 c, _ := CreateTestContext(httptest.NewRecorder()) 209 m := make(map[string][]string) 210 m["foo"] = []string{"foo"} 211 c.Set("map", m) 212 213 assert.Equal(t, m, c.GetStringMapStringSlice("map")) 214 assert.Equal(t, []string{"foo"}, c.GetStringMapStringSlice("map")["foo"]) 215 } 216 217 func TestContextCopy(t *testing.T) { 218 c, _ := CreateTestContext(httptest.NewRecorder()) 219 c.index = 2 220 c.Request, _ = http.NewRequest("POST", "/hola", nil) 221 c.handlers = HandlersChain{func(c *Context) {}} 222 c.Params = Params{Param{Key: "foo", Value: "bar"}} 223 c.Set("foo", "bar") 224 225 cp := c.Copy() 226 assert.Nil(t, cp.handlers) 227 assert.Nil(t, cp.writermem.ResponseWriter) 228 assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter)) 229 assert.Equal(t, cp.Request, c.Request) 230 assert.Equal(t, cp.index, abortIndex) 231 assert.Equal(t, cp.Keys, c.Keys) 232 assert.Equal(t, cp.engine, c.engine) 233 assert.Equal(t, cp.Params, c.Params) 234 cp.Set("foo", "notBar") 235 assert.False(t, cp.Keys["foo"] == c.Keys["foo"]) 236 } 237 238 func TestContextHandlerName(t *testing.T) { 239 c, _ := CreateTestContext(httptest.NewRecorder()) 240 c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest} 241 242 assert.Regexp(t, "^(.*/vendor/)?go.sdls.io/sin/pkg/sin.handlerNameTest$", c.HandlerName()) 243 } 244 245 func TestContextHandlerNames(t *testing.T) { 246 c, _ := CreateTestContext(httptest.NewRecorder()) 247 c.handlers = HandlersChain{func(c *Context) {}, handlerNameTest, func(c *Context) {}, handlerNameTest2} 248 249 names := c.HandlerNames() 250 251 assert.True(t, len(names) == 4) 252 for _, name := range names { 253 assert.Regexp(t, `^(.*/vendor/)?(go\.sdls\.io/sin/pkg/sin\.){1}(TestContextHandlerNames\.func.*){0,1}(handlerNameTest.*){0,1}`, name) 254 } 255 } 256 257 func handlerNameTest(_ *Context) { 258 } 259 260 func handlerNameTest2(_ *Context) { 261 } 262 263 var handlerTest HandlerFunc = func(c *Context) { 264 } 265 266 func TestContextHandler(t *testing.T) { 267 c, _ := CreateTestContext(httptest.NewRecorder()) 268 c.handlers = HandlersChain{func(c *Context) {}, handlerTest} 269 270 assert.Equal(t, reflect.ValueOf(handlerTest).Pointer(), reflect.ValueOf(c.Handler()).Pointer()) 271 } 272 273 func TestContextQuery(t *testing.T) { 274 c, _ := CreateTestContext(httptest.NewRecorder()) 275 c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10&id=", nil) 276 277 value, ok := c.GetQuery("foo") 278 assert.True(t, ok) 279 assert.Equal(t, "bar", value) 280 assert.Equal(t, "bar", c.DefaultQuery("foo", "none")) 281 assert.Equal(t, "bar", c.Query("foo")) 282 283 value, ok = c.GetQuery("page") 284 assert.True(t, ok) 285 assert.Equal(t, "10", value) 286 assert.Equal(t, "10", c.DefaultQuery("page", "0")) 287 assert.Equal(t, "10", c.Query("page")) 288 289 value, ok = c.GetQuery("id") 290 assert.True(t, ok) 291 assert.Empty(t, value) 292 assert.Empty(t, c.DefaultQuery("id", "nada")) 293 assert.Empty(t, c.Query("id")) 294 295 value, ok = c.GetQuery("NoKey") 296 assert.False(t, ok) 297 assert.Empty(t, value) 298 assert.Equal(t, "nada", c.DefaultQuery("NoKey", "nada")) 299 assert.Empty(t, c.Query("NoKey")) 300 } 301 302 func TestContextDefaultQueryOnEmptyRequest(t *testing.T) { 303 c, _ := CreateTestContext(httptest.NewRecorder()) // here c.Request == nil 304 assert.NotPanics(t, func() { 305 value, ok := c.GetQuery("NoKey") 306 assert.False(t, ok) 307 assert.Empty(t, value) 308 }) 309 assert.NotPanics(t, func() { 310 assert.Equal(t, "nada", c.DefaultQuery("NoKey", "nada")) 311 }) 312 assert.NotPanics(t, func() { 313 assert.Empty(t, c.Query("NoKey")) 314 }) 315 } 316 317 func TestContextSetCookie(t *testing.T) { 318 c, _ := CreateTestContext(httptest.NewRecorder()) 319 c.SetSameSite(http.SameSiteLaxMode) 320 c.SetCookie("user", "gin", 1, "/", "localhost", true, true) 321 assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) 322 } 323 324 func TestContextSetCookiePathEmpty(t *testing.T) { 325 c, _ := CreateTestContext(httptest.NewRecorder()) 326 c.SetSameSite(http.SameSiteLaxMode) 327 c.SetCookie("user", "gin", 1, "", "localhost", true, true) 328 assert.Equal(t, "user=gin; Path=/; Domain=localhost; Max-Age=1; HttpOnly; Secure; SameSite=Lax", c.Writer.Header().Get("Set-Cookie")) 329 } 330 331 func TestContextGetCookie(t *testing.T) { 332 c, _ := CreateTestContext(httptest.NewRecorder()) 333 c.Request, _ = http.NewRequest("GET", "/get", nil) 334 c.Request.Header.Set("Cookie", "user=gin") 335 cookie, _ := c.Cookie("user") 336 assert.Equal(t, "gin", cookie) 337 338 _, err := c.Cookie("nokey") 339 assert.Error(t, err) 340 } 341 342 func TestContextBodyAllowedForStatus(t *testing.T) { 343 assert.False(t, false, bodyAllowedForStatus(http.StatusProcessing)) 344 assert.False(t, false, bodyAllowedForStatus(http.StatusNoContent)) 345 assert.False(t, false, bodyAllowedForStatus(http.StatusNotModified)) 346 assert.True(t, true, bodyAllowedForStatus(http.StatusInternalServerError)) 347 } 348 349 type TestPanicRender struct{} 350 351 func (*TestPanicRender) Render(http.ResponseWriter) error { 352 return errTestPanicRender 353 } 354 355 func (*TestPanicRender) WriteContentType(http.ResponseWriter) {} 356 357 func TestContextRenderIfErr(t *testing.T) { 358 w := httptest.NewRecorder() 359 c, _ := CreateTestContext(w) 360 361 c.Render(http.StatusOK, &TestPanicRender{}) 362 363 assert.Equal(t, errorMsgs{&Error{Err: errTestPanicRender, Type: 1}}, c.Errors) 364 } 365 366 // Tests that the response is serialized as JSON 367 // and Content-Type is set to application/json 368 // and special HTML characters are escaped 369 func TestContextRenderJSON(t *testing.T) { 370 w := httptest.NewRecorder() 371 c, _ := CreateTestContext(w) 372 373 c.JSON(http.StatusCreated, H{"foo": "bar", "html": "<b>"}) 374 375 assert.Equal(t, http.StatusCreated, w.Code) 376 assert.Equal(t, "{\"foo\":\"bar\",\"html\":\"\\u003cb\\u003e\"}", w.Body.String()) 377 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) 378 } 379 380 // Tests that no JSON is rendered if code is 204 381 func TestContextRenderNoContentJSON(t *testing.T) { 382 w := httptest.NewRecorder() 383 c, _ := CreateTestContext(w) 384 385 c.JSON(http.StatusNoContent, H{"foo": "bar"}) 386 387 assert.Equal(t, http.StatusNoContent, w.Code) 388 assert.Empty(t, w.Body.String()) 389 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) 390 } 391 392 // Tests that the response is serialized as JSON 393 // we change the content-type before 394 func TestContextRenderAPIJSON(t *testing.T) { 395 w := httptest.NewRecorder() 396 c, _ := CreateTestContext(w) 397 398 c.Header("Content-Type", "application/vnd.api+json") 399 c.JSON(http.StatusCreated, H{"foo": "bar"}) 400 401 assert.Equal(t, http.StatusCreated, w.Code) 402 assert.Equal(t, "{\"foo\":\"bar\"}", w.Body.String()) 403 assert.Equal(t, "application/vnd.api+json", w.Header().Get("Content-Type")) 404 } 405 406 // Tests that no Custom JSON is rendered if code is 204 407 func TestContextRenderNoContentAPIJSON(t *testing.T) { 408 w := httptest.NewRecorder() 409 c, _ := CreateTestContext(w) 410 411 c.Header("Content-Type", "application/vnd.api+json") 412 c.JSON(http.StatusNoContent, H{"foo": "bar"}) 413 414 assert.Equal(t, http.StatusNoContent, w.Code) 415 assert.Empty(t, w.Body.String()) 416 assert.Equal(t, w.Header().Get("Content-Type"), "application/vnd.api+json") 417 } 418 419 // Tests that the response is serialized as JSON 420 // and Content-Type is set to application/json 421 func TestContextRenderIndentedJSON(t *testing.T) { 422 w := httptest.NewRecorder() 423 c, _ := CreateTestContext(w) 424 425 c.IndentedJSON(http.StatusCreated, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) 426 427 assert.Equal(t, http.StatusCreated, w.Code) 428 assert.Equal(t, "{\n \"bar\": \"foo\",\n \"foo\": \"bar\",\n \"nested\": {\n \"foo\": \"bar\"\n }\n}", w.Body.String()) 429 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) 430 } 431 432 // Tests that no Custom JSON is rendered if code is 204 433 func TestContextRenderNoContentIndentedJSON(t *testing.T) { 434 w := httptest.NewRecorder() 435 c, _ := CreateTestContext(w) 436 437 c.IndentedJSON(http.StatusNoContent, H{"foo": "bar", "bar": "foo", "nested": H{"foo": "bar"}}) 438 439 assert.Equal(t, http.StatusNoContent, w.Code) 440 assert.Empty(t, w.Body.String()) 441 assert.Equal(t, "application/json; charset=utf-8", w.Header().Get("Content-Type")) 442 } 443 444 // TestContextXML tests that the response is serialized as XML 445 // and Content-Type is set to application/xml 446 func TestContextRenderXML(t *testing.T) { 447 w := httptest.NewRecorder() 448 c, _ := CreateTestContext(w) 449 450 c.XML(http.StatusCreated, H{"foo": "bar"}) 451 452 assert.Equal(t, http.StatusCreated, w.Code) 453 assert.Equal(t, "<map><foo>bar</foo></map>", w.Body.String()) 454 assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) 455 } 456 457 // Tests that no XML is rendered if code is 204 458 func TestContextRenderNoContentXML(t *testing.T) { 459 w := httptest.NewRecorder() 460 c, _ := CreateTestContext(w) 461 462 c.XML(http.StatusNoContent, H{"foo": "bar"}) 463 464 assert.Equal(t, http.StatusNoContent, w.Code) 465 assert.Empty(t, w.Body.String()) 466 assert.Equal(t, "application/xml; charset=utf-8", w.Header().Get("Content-Type")) 467 } 468 469 // TestContextString tests that the response is returned 470 // with Content-Type set to text/plain 471 func TestContextRenderString(t *testing.T) { 472 w := httptest.NewRecorder() 473 c, _ := CreateTestContext(w) 474 475 c.String(http.StatusCreated, "test %s %d", "string", 2) 476 477 assert.Equal(t, http.StatusCreated, w.Code) 478 assert.Equal(t, "test string 2", w.Body.String()) 479 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) 480 } 481 482 // Tests that no String is rendered if code is 204 483 func TestContextRenderNoContentString(t *testing.T) { 484 w := httptest.NewRecorder() 485 c, _ := CreateTestContext(w) 486 487 c.String(http.StatusNoContent, "test %s %d", "string", 2) 488 489 assert.Equal(t, http.StatusNoContent, w.Code) 490 assert.Empty(t, w.Body.String()) 491 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) 492 } 493 494 // TestContextString tests that the response is returned 495 // with Content-Type set to text/html 496 func TestContextRenderHTMLString(t *testing.T) { 497 w := httptest.NewRecorder() 498 c, _ := CreateTestContext(w) 499 500 c.Header("Content-Type", "text/html; charset=utf-8") 501 c.String(http.StatusCreated, "<html>%s %d</html>", "string", 3) 502 503 assert.Equal(t, http.StatusCreated, w.Code) 504 assert.Equal(t, "<html>string 3</html>", w.Body.String()) 505 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) 506 } 507 508 // Tests that no HTML String is rendered if code is 204 509 func TestContextRenderNoContentHTMLString(t *testing.T) { 510 w := httptest.NewRecorder() 511 c, _ := CreateTestContext(w) 512 513 c.Header("Content-Type", "text/html; charset=utf-8") 514 c.String(http.StatusNoContent, "<html>%s %d</html>", "string", 3) 515 516 assert.Equal(t, http.StatusNoContent, w.Code) 517 assert.Empty(t, w.Body.String()) 518 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) 519 } 520 521 // TestContextData tests that the response can be written from `bytestring` 522 // with specified MIME type 523 func TestContextRenderData(t *testing.T) { 524 w := httptest.NewRecorder() 525 c, _ := CreateTestContext(w) 526 527 c.Data(http.StatusCreated, "text/csv", []byte(`foo,bar`)) 528 529 assert.Equal(t, http.StatusCreated, w.Code) 530 assert.Equal(t, "foo,bar", w.Body.String()) 531 assert.Equal(t, "text/csv", w.Header().Get("Content-Type")) 532 } 533 534 // Tests that no Custom Data is rendered if code is 204 535 func TestContextRenderNoContentData(t *testing.T) { 536 w := httptest.NewRecorder() 537 c, _ := CreateTestContext(w) 538 539 c.Data(http.StatusNoContent, "text/csv", []byte(`foo,bar`)) 540 541 assert.Equal(t, http.StatusNoContent, w.Code) 542 assert.Empty(t, w.Body.String()) 543 assert.Equal(t, "text/csv", w.Header().Get("Content-Type")) 544 } 545 546 func TestContextHeaders(t *testing.T) { 547 c, _ := CreateTestContext(httptest.NewRecorder()) 548 c.Header("Content-Type", "text/plain") 549 c.Header("X-Custom", "value") 550 551 assert.Equal(t, "text/plain", c.Writer.Header().Get("Content-Type")) 552 assert.Equal(t, "value", c.Writer.Header().Get("X-Custom")) 553 554 c.Header("Content-Type", "text/html") 555 c.Header("X-Custom", "") 556 557 assert.Equal(t, "text/html", c.Writer.Header().Get("Content-Type")) 558 _, exist := c.Writer.Header()["X-Custom"] 559 assert.False(t, exist) 560 } 561 562 // TODO 563 func TestContextRenderRedirectWithRelativePath(t *testing.T) { 564 w := httptest.NewRecorder() 565 c, _ := CreateTestContext(w) 566 567 c.Request, _ = http.NewRequest("POST", "http://example.com", nil) 568 assert.Panics(t, func() { c.Redirect(299, "/new_path") }) 569 assert.Panics(t, func() { c.Redirect(309, "/new_path") }) 570 571 c.Redirect(http.StatusMovedPermanently, "/path") 572 c.Writer.WriteHeaderNow() 573 assert.Equal(t, http.StatusMovedPermanently, w.Code) 574 assert.Equal(t, "/path", w.Header().Get("Location")) 575 } 576 577 func TestContextRenderRedirectWithAbsolutePath(t *testing.T) { 578 w := httptest.NewRecorder() 579 c, _ := CreateTestContext(w) 580 581 c.Request, _ = http.NewRequest("POST", "http://example.com", nil) 582 c.Redirect(http.StatusFound, "http://google.com") 583 c.Writer.WriteHeaderNow() 584 585 assert.Equal(t, http.StatusFound, w.Code) 586 assert.Equal(t, "http://google.com", w.Header().Get("Location")) 587 } 588 589 func TestContextRenderRedirectWith201(t *testing.T) { 590 w := httptest.NewRecorder() 591 c, _ := CreateTestContext(w) 592 593 c.Request, _ = http.NewRequest("POST", "http://example.com", nil) 594 c.Redirect(http.StatusCreated, "/resource") 595 c.Writer.WriteHeaderNow() 596 597 assert.Equal(t, http.StatusCreated, w.Code) 598 assert.Equal(t, "/resource", w.Header().Get("Location")) 599 } 600 601 func TestContextRenderRedirectAll(t *testing.T) { 602 c, _ := CreateTestContext(httptest.NewRecorder()) 603 c.Request, _ = http.NewRequest("POST", "http://example.com", nil) 604 assert.Panics(t, func() { c.Redirect(http.StatusOK, "/resource") }) 605 assert.Panics(t, func() { c.Redirect(http.StatusAccepted, "/resource") }) 606 assert.Panics(t, func() { c.Redirect(299, "/resource") }) 607 assert.Panics(t, func() { c.Redirect(309, "/resource") }) 608 assert.NotPanics(t, func() { c.Redirect(http.StatusMultipleChoices, "/resource") }) 609 assert.NotPanics(t, func() { c.Redirect(http.StatusPermanentRedirect, "/resource") }) 610 } 611 612 func TestContextIsAborted(t *testing.T) { 613 c, _ := CreateTestContext(httptest.NewRecorder()) 614 assert.False(t, c.IsAborted()) 615 616 c.Abort() 617 assert.True(t, c.IsAborted()) 618 619 c.Next() 620 assert.True(t, c.IsAborted()) 621 622 c.index++ 623 assert.True(t, c.IsAborted()) 624 } 625 626 // TestContextData tests that the response can be written from `bytestring` 627 // with specified MIME type 628 func TestContextAbortWithStatus(t *testing.T) { 629 w := httptest.NewRecorder() 630 c, _ := CreateTestContext(w) 631 632 c.index = 4 633 c.AbortWithStatus(http.StatusUnauthorized) 634 635 assert.Equal(t, abortIndex, c.index) 636 assert.Equal(t, http.StatusUnauthorized, c.Writer.Status()) 637 assert.Equal(t, http.StatusUnauthorized, w.Code) 638 assert.True(t, c.IsAborted()) 639 } 640 641 type testJSONAbortMsg struct { 642 Foo string `json:"foo"` 643 Bar string `json:"bar"` 644 } 645 646 func TestContextAbortWithStatusJSON(t *testing.T) { 647 w := httptest.NewRecorder() 648 c, _ := CreateTestContext(w) 649 c.index = 4 650 651 in := new(testJSONAbortMsg) 652 in.Bar = "barValue" 653 in.Foo = "fooValue" 654 655 c.AbortWithStatusJSON(http.StatusUnsupportedMediaType, in) 656 657 assert.Equal(t, abortIndex, c.index) 658 assert.Equal(t, http.StatusUnsupportedMediaType, c.Writer.Status()) 659 assert.Equal(t, http.StatusUnsupportedMediaType, w.Code) 660 assert.True(t, c.IsAborted()) 661 662 contentType := w.Header().Get("Content-Type") 663 assert.Equal(t, "application/json; charset=utf-8", contentType) 664 665 buf := new(bytes.Buffer) 666 _, err := buf.ReadFrom(w.Body) 667 assert.NoError(t, err) 668 jsonStringBody := buf.String() 669 assert.Equal(t, "{\"foo\":\"fooValue\",\"bar\":\"barValue\"}", jsonStringBody) 670 } 671 672 func TestContextError(t *testing.T) { 673 c, _ := CreateTestContext(httptest.NewRecorder()) 674 assert.Empty(t, c.Errors) 675 676 firstErr := errors.New("first error") 677 c.Error(firstErr) // nolint: errcheck 678 assert.Len(t, c.Errors, 1) 679 assert.Equal(t, "Error #01: first error\n", c.Errors.String()) 680 681 secondErr := errors.New("second error") 682 c.Error(&Error{ // nolint: errcheck 683 Err: secondErr, 684 Meta: "some data 2", 685 Type: ErrorTypePublic, 686 }) 687 assert.Len(t, c.Errors, 2) 688 689 assert.Equal(t, firstErr, c.Errors[0].Err) 690 assert.Nil(t, c.Errors[0].Meta) 691 assert.Equal(t, ErrorTypePrivate, c.Errors[0].Type) 692 693 assert.Equal(t, secondErr, c.Errors[1].Err) 694 assert.Equal(t, "some data 2", c.Errors[1].Meta) 695 assert.Equal(t, ErrorTypePublic, c.Errors[1].Type) 696 697 assert.Equal(t, c.Errors.Last(), c.Errors[1]) 698 699 defer func() { 700 if recover() == nil { 701 t.Error("didn't panic") 702 } 703 }() 704 c.Error(nil) // nolint: errcheck 705 } 706 707 func TestContextTypedError(t *testing.T) { 708 c, _ := CreateTestContext(httptest.NewRecorder()) 709 c.Error(errors.New("externo 0")).SetType(ErrorTypePublic) // nolint: errcheck 710 c.Error(errors.New("interno 0")).SetType(ErrorTypePrivate) // nolint: errcheck 711 712 for _, err := range c.Errors.ByType(ErrorTypePublic) { 713 assert.Equal(t, ErrorTypePublic, err.Type) 714 } 715 for _, err := range c.Errors.ByType(ErrorTypePrivate) { 716 assert.Equal(t, ErrorTypePrivate, err.Type) 717 } 718 assert.Equal(t, []string{"externo 0", "interno 0"}, c.Errors.Errors()) 719 } 720 721 func TestContextAbortWithError(t *testing.T) { 722 w := httptest.NewRecorder() 723 c, _ := CreateTestContext(w) 724 725 c.AbortWithError(http.StatusUnauthorized, errors.New("bad input")).SetMeta("some input") // nolint: errcheck 726 727 assert.Equal(t, http.StatusUnauthorized, w.Code) 728 assert.Equal(t, abortIndex, c.index) 729 assert.True(t, c.IsAborted()) 730 } 731 732 func TestContextContentType(t *testing.T) { 733 c, _ := CreateTestContext(httptest.NewRecorder()) 734 c.Request, _ = http.NewRequest("POST", "/", nil) 735 c.Request.Header.Set("Content-Type", "application/json; charset=utf-8") 736 737 assert.Equal(t, "application/json", c.ContentType()) 738 } 739 740 func TestContextGolangContext(t *testing.T) { 741 c, _ := CreateTestContext(httptest.NewRecorder()) 742 c.Request, _ = http.NewRequest("POST", "/", bytes.NewBufferString("{\"foo\":\"bar\", \"bar\":\"foo\"}")) 743 assert.NoError(t, c.Err()) 744 assert.Nil(t, c.Done()) 745 ti, ok := c.Deadline() 746 assert.Equal(t, ti, time.Time{}) 747 assert.False(t, ok) 748 assert.Equal(t, c.Value(0), c.Request) 749 assert.Nil(t, c.Value("foo")) 750 751 c.Set("foo", "bar") 752 assert.Equal(t, "bar", c.Value("foo")) 753 assert.Nil(t, c.Value(1)) 754 } 755 756 func TestWebsocketsRequired(t *testing.T) { 757 // Example request from spec: https://tools.ietf.org/html/rfc6455#section-1.2 758 c, _ := CreateTestContext(httptest.NewRecorder()) 759 c.Request, _ = http.NewRequest("GET", "/chat", nil) 760 c.Request.Header.Set("Host", "server.example.com") 761 c.Request.Header.Set("Upgrade", "websocket") 762 c.Request.Header.Set("Connection", "Upgrade") 763 c.Request.Header.Set("Sec-WebSocket-Key", "dGhlIHNhbXBsZSBub25jZQ==") 764 c.Request.Header.Set("Origin", "http://example.com") 765 c.Request.Header.Set("Sec-WebSocket-Protocol", "chat, superchat") 766 c.Request.Header.Set("Sec-WebSocket-Version", "13") 767 768 assert.True(t, c.IsWebsocket()) 769 770 // Normal request, no websocket required. 771 c, _ = CreateTestContext(httptest.NewRecorder()) 772 c.Request, _ = http.NewRequest("GET", "/chat", nil) 773 c.Request.Header.Set("Host", "server.example.com") 774 775 assert.False(t, c.IsWebsocket()) 776 } 777 778 func TestGetRequestHeaderValue(t *testing.T) { 779 c, _ := CreateTestContext(httptest.NewRecorder()) 780 c.Request, _ = http.NewRequest("GET", "/chat", nil) 781 c.Request.Header.Set("Gin-Version", "1.0.0") 782 783 assert.Equal(t, "1.0.0", c.GetHeader("Gin-Version")) 784 assert.Empty(t, c.GetHeader("Connection")) 785 } 786 787 func TestContextRenderDataFromReader(t *testing.T) { 788 w := httptest.NewRecorder() 789 c, _ := CreateTestContext(w) 790 791 body := "#!PNG some raw data" 792 reader := strings.NewReader(body) 793 contentLength := int64(len(body)) 794 contentType := "image/png" 795 extraHeaders := map[string]string{"Content-Disposition": `attachment; filename="gopher.png"`} 796 797 c.DataFromReader(http.StatusOK, contentLength, contentType, reader, extraHeaders) 798 799 assert.Equal(t, http.StatusOK, w.Code) 800 assert.Equal(t, body, w.Body.String()) 801 assert.Equal(t, contentType, w.Header().Get("Content-Type")) 802 assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) 803 assert.Equal(t, extraHeaders["Content-Disposition"], w.Header().Get("Content-Disposition")) 804 } 805 806 func TestContextRenderDataFromReaderNoHeaders(t *testing.T) { 807 w := httptest.NewRecorder() 808 c, _ := CreateTestContext(w) 809 810 body := "#!PNG some raw data" 811 reader := strings.NewReader(body) 812 contentLength := int64(len(body)) 813 contentType := "image/png" 814 815 c.DataFromReader(http.StatusOK, contentLength, contentType, reader, nil) 816 817 assert.Equal(t, http.StatusOK, w.Code) 818 assert.Equal(t, body, w.Body.String()) 819 assert.Equal(t, contentType, w.Header().Get("Content-Type")) 820 assert.Equal(t, fmt.Sprintf("%d", contentLength), w.Header().Get("Content-Length")) 821 } 822 823 type TestResponseRecorder struct { 824 *httptest.ResponseRecorder 825 closeChannel chan bool 826 } 827 828 func (r *TestResponseRecorder) CloseNotify() <-chan bool { 829 return r.closeChannel 830 } 831 832 func CreateTestResponseRecorder() *TestResponseRecorder { 833 return &TestResponseRecorder{ 834 httptest.NewRecorder(), 835 make(chan bool, 1), 836 } 837 } 838 839 func TestContextResetInHandler(t *testing.T) { 840 w := CreateTestResponseRecorder() 841 c, _ := CreateTestContext(w) 842 843 c.handlers = []HandlerFunc{ 844 func(c *Context) { c.reset() }, 845 } 846 assert.NotPanics(t, func() { 847 c.Next() 848 }) 849 } 850 851 func TestRaceParamsContextCopy(t *testing.T) { 852 router := New() 853 nameGroup := router.Group("/:name") 854 var wg sync.WaitGroup 855 wg.Add(2) 856 { 857 nameGroup.GET("/api", func(c *Context) { 858 go func(c *Context, param string) { 859 defer wg.Done() 860 // First assert must be executed after the second request 861 time.Sleep(50 * time.Millisecond) 862 assert.Equal(t, c.Param("name"), param) 863 }(c.Copy(), c.Param("name")) 864 }) 865 } 866 performRequest(router, "GET", "/name1/api") 867 performRequest(router, "GET", "/name2/api") 868 wg.Wait() 869 } 870 871 func TestContextWithKeysMutex(t *testing.T) { 872 c := &Context{} 873 c.Set("foo", "bar") 874 875 value, err := c.Get("foo") 876 assert.Equal(t, "bar", value) 877 assert.True(t, err) 878 879 value, err = c.Get("foo2") 880 assert.Nil(t, value) 881 assert.False(t, err) 882 } 883 884 func TestContextWithFallbackValueFromRequestContext(t *testing.T) { 885 var key struct{} 886 c := &Context{} 887 c.Request, _ = http.NewRequest("POST", "/", nil) 888 c.Request = c.Request.WithContext(context.WithValue(context.TODO(), key, "value")) 889 890 assert.Equal(t, "value", c.Value(key)) 891 892 c2 := &Context{} 893 c2.Request, _ = http.NewRequest("POST", "/", nil) 894 // nolint:staticcheck 895 c2.Request = c2.Request.WithContext(context.WithValue(context.TODO(), "key", "value2")) 896 assert.Equal(t, "value2", c2.Value("key")) 897 } 898 899 func TestContextWithFallbackDeadlineFromRequestContext(t *testing.T) { 900 c := &Context{} 901 deadline, ok := c.Deadline() 902 assert.Zero(t, deadline) 903 assert.False(t, ok) 904 905 c2 := &Context{} 906 c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) 907 d := time.Now().Add(time.Second) 908 ctx, cancel := context.WithDeadline(context.Background(), d) 909 defer cancel() 910 c2.Request = c2.Request.WithContext(ctx) 911 deadline, ok = c2.Deadline() 912 assert.Equal(t, d, deadline) 913 assert.True(t, ok) 914 } 915 916 func TestContextWithFallbackDoneFromRequestContext(t *testing.T) { 917 c := &Context{} 918 assert.Nil(t, c.Done()) 919 920 c2 := &Context{} 921 c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) 922 ctx, cancel := context.WithCancel(context.Background()) 923 c2.Request = c2.Request.WithContext(ctx) 924 cancel() 925 assert.NotNil(t, <-c2.Done()) 926 } 927 928 func TestContextWithFallbackErrFromRequestContext(t *testing.T) { 929 c := &Context{} 930 assert.Nil(t, c.Err()) 931 932 c2 := &Context{} 933 c2.Request, _ = http.NewRequest(http.MethodGet, "/", nil) 934 ctx, cancel := context.WithCancel(context.Background()) 935 c2.Request = c2.Request.WithContext(ctx) 936 cancel() 937 938 assert.EqualError(t, c2.Err(), context.Canceled.Error()) 939 }