github.com/hellobchain/third_party@v0.0.0-20230331131523-deb0478a2e52/gin/routes_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 gin 6 7 import ( 8 "fmt" 9 "github.com/hellobchain/newcryptosm/http" 10 "github.com/hellobchain/newcryptosm/http/httptest" 11 "io/ioutil" 12 "os" 13 "path/filepath" 14 "testing" 15 16 "github.com/stretchr/testify/assert" 17 ) 18 19 type header struct { 20 Key string 21 Value string 22 } 23 24 func performRequest(r http.Handler, method, path string, headers ...header) *httptest.ResponseRecorder { 25 req := httptest.NewRequest(method, path, nil) 26 for _, h := range headers { 27 req.Header.Add(h.Key, h.Value) 28 } 29 w := httptest.NewRecorder() 30 r.ServeHTTP(w, req) 31 return w 32 } 33 34 func testRouteOK(method string, t *testing.T) { 35 passed := false 36 passedAny := false 37 r := New() 38 r.Any("/test2", func(c *Context) { 39 passedAny = true 40 }) 41 r.Handle(method, "/test", func(c *Context) { 42 passed = true 43 }) 44 45 w := performRequest(r, method, "/test") 46 assert.True(t, passed) 47 assert.Equal(t, http.StatusOK, w.Code) 48 49 performRequest(r, method, "/test2") 50 assert.True(t, passedAny) 51 } 52 53 // TestSingleRouteOK tests that POST route is correctly invoked. 54 func testRouteNotOK(method string, t *testing.T) { 55 passed := false 56 router := New() 57 router.Handle(method, "/test_2", func(c *Context) { 58 passed = true 59 }) 60 61 w := performRequest(router, method, "/test") 62 63 assert.False(t, passed) 64 assert.Equal(t, http.StatusNotFound, w.Code) 65 } 66 67 // TestSingleRouteOK tests that POST route is correctly invoked. 68 func testRouteNotOK2(method string, t *testing.T) { 69 passed := false 70 router := New() 71 router.HandleMethodNotAllowed = true 72 var methodRoute string 73 if method == http.MethodPost { 74 methodRoute = http.MethodGet 75 } else { 76 methodRoute = http.MethodPost 77 } 78 router.Handle(methodRoute, "/test", func(c *Context) { 79 passed = true 80 }) 81 82 w := performRequest(router, method, "/test") 83 84 assert.False(t, passed) 85 assert.Equal(t, http.StatusMethodNotAllowed, w.Code) 86 } 87 88 func TestRouterMethod(t *testing.T) { 89 router := New() 90 router.PUT("/hey2", func(c *Context) { 91 c.String(http.StatusOK, "sup2") 92 }) 93 94 router.PUT("/hey", func(c *Context) { 95 c.String(http.StatusOK, "called") 96 }) 97 98 router.PUT("/hey3", func(c *Context) { 99 c.String(http.StatusOK, "sup3") 100 }) 101 102 w := performRequest(router, http.MethodPut, "/hey") 103 104 assert.Equal(t, http.StatusOK, w.Code) 105 assert.Equal(t, "called", w.Body.String()) 106 } 107 108 func TestRouterGroupRouteOK(t *testing.T) { 109 testRouteOK(http.MethodGet, t) 110 testRouteOK(http.MethodPost, t) 111 testRouteOK(http.MethodPut, t) 112 testRouteOK(http.MethodPatch, t) 113 testRouteOK(http.MethodHead, t) 114 testRouteOK(http.MethodOptions, t) 115 testRouteOK(http.MethodDelete, t) 116 testRouteOK(http.MethodConnect, t) 117 testRouteOK(http.MethodTrace, t) 118 } 119 120 func TestRouteNotOK(t *testing.T) { 121 testRouteNotOK(http.MethodGet, t) 122 testRouteNotOK(http.MethodPost, t) 123 testRouteNotOK(http.MethodPut, t) 124 testRouteNotOK(http.MethodPatch, t) 125 testRouteNotOK(http.MethodHead, t) 126 testRouteNotOK(http.MethodOptions, t) 127 testRouteNotOK(http.MethodDelete, t) 128 testRouteNotOK(http.MethodConnect, t) 129 testRouteNotOK(http.MethodTrace, t) 130 } 131 132 func TestRouteNotOK2(t *testing.T) { 133 testRouteNotOK2(http.MethodGet, t) 134 testRouteNotOK2(http.MethodPost, t) 135 testRouteNotOK2(http.MethodPut, t) 136 testRouteNotOK2(http.MethodPatch, t) 137 testRouteNotOK2(http.MethodHead, t) 138 testRouteNotOK2(http.MethodOptions, t) 139 testRouteNotOK2(http.MethodDelete, t) 140 testRouteNotOK2(http.MethodConnect, t) 141 testRouteNotOK2(http.MethodTrace, t) 142 } 143 144 func TestRouteRedirectTrailingSlash(t *testing.T) { 145 router := New() 146 router.RedirectFixedPath = false 147 router.RedirectTrailingSlash = true 148 router.GET("/path", func(c *Context) {}) 149 router.GET("/path2/", func(c *Context) {}) 150 router.POST("/path3", func(c *Context) {}) 151 router.PUT("/path4/", func(c *Context) {}) 152 153 w := performRequest(router, http.MethodGet, "/path/") 154 assert.Equal(t, "/path", w.Header().Get("Location")) 155 assert.Equal(t, http.StatusMovedPermanently, w.Code) 156 157 w = performRequest(router, http.MethodGet, "/path2") 158 assert.Equal(t, "/path2/", w.Header().Get("Location")) 159 assert.Equal(t, http.StatusMovedPermanently, w.Code) 160 161 w = performRequest(router, http.MethodPost, "/path3/") 162 assert.Equal(t, "/path3", w.Header().Get("Location")) 163 assert.Equal(t, http.StatusTemporaryRedirect, w.Code) 164 165 w = performRequest(router, http.MethodPut, "/path4") 166 assert.Equal(t, "/path4/", w.Header().Get("Location")) 167 assert.Equal(t, http.StatusTemporaryRedirect, w.Code) 168 169 w = performRequest(router, http.MethodGet, "/path") 170 assert.Equal(t, http.StatusOK, w.Code) 171 172 w = performRequest(router, http.MethodGet, "/path2/") 173 assert.Equal(t, http.StatusOK, w.Code) 174 175 w = performRequest(router, http.MethodPost, "/path3") 176 assert.Equal(t, http.StatusOK, w.Code) 177 178 w = performRequest(router, http.MethodPut, "/path4/") 179 assert.Equal(t, http.StatusOK, w.Code) 180 181 w = performRequest(router, http.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) 182 assert.Equal(t, "/api/path2/", w.Header().Get("Location")) 183 assert.Equal(t, 301, w.Code) 184 185 w = performRequest(router, http.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) 186 assert.Equal(t, 200, w.Code) 187 188 router.RedirectTrailingSlash = false 189 190 w = performRequest(router, http.MethodGet, "/path/") 191 assert.Equal(t, http.StatusNotFound, w.Code) 192 w = performRequest(router, http.MethodGet, "/path2") 193 assert.Equal(t, http.StatusNotFound, w.Code) 194 w = performRequest(router, http.MethodPost, "/path3/") 195 assert.Equal(t, http.StatusNotFound, w.Code) 196 w = performRequest(router, http.MethodPut, "/path4") 197 assert.Equal(t, http.StatusNotFound, w.Code) 198 } 199 200 func TestRouteRedirectFixedPath(t *testing.T) { 201 router := New() 202 router.RedirectFixedPath = true 203 router.RedirectTrailingSlash = false 204 205 router.GET("/path", func(c *Context) {}) 206 router.GET("/Path2", func(c *Context) {}) 207 router.POST("/PATH3", func(c *Context) {}) 208 router.POST("/Path4/", func(c *Context) {}) 209 210 w := performRequest(router, http.MethodGet, "/PATH") 211 assert.Equal(t, "/path", w.Header().Get("Location")) 212 assert.Equal(t, http.StatusMovedPermanently, w.Code) 213 214 w = performRequest(router, http.MethodGet, "/path2") 215 assert.Equal(t, "/Path2", w.Header().Get("Location")) 216 assert.Equal(t, http.StatusMovedPermanently, w.Code) 217 218 w = performRequest(router, http.MethodPost, "/path3") 219 assert.Equal(t, "/PATH3", w.Header().Get("Location")) 220 assert.Equal(t, http.StatusTemporaryRedirect, w.Code) 221 222 w = performRequest(router, http.MethodPost, "/path4") 223 assert.Equal(t, "/Path4/", w.Header().Get("Location")) 224 assert.Equal(t, http.StatusTemporaryRedirect, w.Code) 225 } 226 227 // TestContextParamsGet tests that a parameter can be parsed from the URL. 228 func TestRouteParamsByName(t *testing.T) { 229 name := "" 230 lastName := "" 231 wild := "" 232 router := New() 233 router.GET("/test/:name/:last_name/*wild", func(c *Context) { 234 name = c.Params.ByName("name") 235 lastName = c.Params.ByName("last_name") 236 var ok bool 237 wild, ok = c.Params.Get("wild") 238 239 assert.True(t, ok) 240 assert.Equal(t, name, c.Param("name")) 241 assert.Equal(t, lastName, c.Param("last_name")) 242 243 assert.Empty(t, c.Param("wtf")) 244 assert.Empty(t, c.Params.ByName("wtf")) 245 246 wtf, ok := c.Params.Get("wtf") 247 assert.Empty(t, wtf) 248 assert.False(t, ok) 249 }) 250 251 w := performRequest(router, http.MethodGet, "/test/john/smith/is/super/great") 252 253 assert.Equal(t, http.StatusOK, w.Code) 254 assert.Equal(t, "john", name) 255 assert.Equal(t, "smith", lastName) 256 assert.Equal(t, "/is/super/great", wild) 257 } 258 259 // TestContextParamsGet tests that a parameter can be parsed from the URL even with extra slashes. 260 func TestRouteParamsByNameWithExtraSlash(t *testing.T) { 261 name := "" 262 lastName := "" 263 wild := "" 264 router := New() 265 router.RemoveExtraSlash = true 266 router.GET("/test/:name/:last_name/*wild", func(c *Context) { 267 name = c.Params.ByName("name") 268 lastName = c.Params.ByName("last_name") 269 var ok bool 270 wild, ok = c.Params.Get("wild") 271 272 assert.True(t, ok) 273 assert.Equal(t, name, c.Param("name")) 274 assert.Equal(t, lastName, c.Param("last_name")) 275 276 assert.Empty(t, c.Param("wtf")) 277 assert.Empty(t, c.Params.ByName("wtf")) 278 279 wtf, ok := c.Params.Get("wtf") 280 assert.Empty(t, wtf) 281 assert.False(t, ok) 282 }) 283 284 w := performRequest(router, http.MethodGet, "//test//john//smith//is//super//great") 285 286 assert.Equal(t, http.StatusOK, w.Code) 287 assert.Equal(t, "john", name) 288 assert.Equal(t, "smith", lastName) 289 assert.Equal(t, "/is/super/great", wild) 290 } 291 292 // TestHandleStaticFile - ensure the static file handles properly 293 func TestRouteStaticFile(t *testing.T) { 294 // SETUP file 295 testRoot, _ := os.Getwd() 296 f, err := ioutil.TempFile(testRoot, "") 297 if err != nil { 298 t.Error(err) 299 } 300 defer os.Remove(f.Name()) 301 _, err = f.WriteString("Gin Web Framework") 302 assert.NoError(t, err) 303 f.Close() 304 305 dir, filename := filepath.Split(f.Name()) 306 307 // SETUP gin 308 router := New() 309 router.Static("/using_static", dir) 310 router.StaticFile("/result", f.Name()) 311 312 w := performRequest(router, http.MethodGet, "/using_static/"+filename) 313 w2 := performRequest(router, http.MethodGet, "/result") 314 315 assert.Equal(t, w, w2) 316 assert.Equal(t, http.StatusOK, w.Code) 317 assert.Equal(t, "Gin Web Framework", w.Body.String()) 318 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) 319 320 w3 := performRequest(router, http.MethodHead, "/using_static/"+filename) 321 w4 := performRequest(router, http.MethodHead, "/result") 322 323 assert.Equal(t, w3, w4) 324 assert.Equal(t, http.StatusOK, w3.Code) 325 } 326 327 // TestHandleStaticDir - ensure the root/sub dir handles properly 328 func TestRouteStaticListingDir(t *testing.T) { 329 router := New() 330 router.StaticFS("/", Dir("./", true)) 331 332 w := performRequest(router, http.MethodGet, "/") 333 334 assert.Equal(t, http.StatusOK, w.Code) 335 assert.Contains(t, w.Body.String(), "gin.go") 336 assert.Equal(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) 337 } 338 339 // TestHandleHeadToDir - ensure the root/sub dir handles properly 340 func TestRouteStaticNoListing(t *testing.T) { 341 router := New() 342 router.Static("/", "./") 343 344 w := performRequest(router, http.MethodGet, "/") 345 346 assert.Equal(t, http.StatusNotFound, w.Code) 347 assert.NotContains(t, w.Body.String(), "gin.go") 348 } 349 350 func TestRouterMiddlewareAndStatic(t *testing.T) { 351 router := New() 352 static := router.Group("/", func(c *Context) { 353 c.Writer.Header().Add("Last-Modified", "Mon, 02 Jan 2006 15:04:05 MST") 354 c.Writer.Header().Add("Expires", "Mon, 02 Jan 2006 15:04:05 MST") 355 c.Writer.Header().Add("X-GIN", "Gin Framework") 356 }) 357 static.Static("/", "./") 358 359 w := performRequest(router, http.MethodGet, "/gin.go") 360 361 assert.Equal(t, http.StatusOK, w.Code) 362 assert.Contains(t, w.Body.String(), "package gin") 363 assert.Equal(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) 364 assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") 365 assert.Equal(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires")) 366 assert.Equal(t, "Gin Framework", w.Header().Get("x-GIN")) 367 } 368 369 func TestRouteNotAllowedEnabled(t *testing.T) { 370 router := New() 371 router.HandleMethodNotAllowed = true 372 router.POST("/path", func(c *Context) {}) 373 w := performRequest(router, http.MethodGet, "/path") 374 assert.Equal(t, http.StatusMethodNotAllowed, w.Code) 375 376 router.NoMethod(func(c *Context) { 377 c.String(http.StatusTeapot, "responseText") 378 }) 379 w = performRequest(router, http.MethodGet, "/path") 380 assert.Equal(t, "responseText", w.Body.String()) 381 assert.Equal(t, http.StatusTeapot, w.Code) 382 } 383 384 func TestRouteNotAllowedEnabled2(t *testing.T) { 385 router := New() 386 router.HandleMethodNotAllowed = true 387 // add one methodTree to trees 388 router.addRoute(http.MethodPost, "/", HandlersChain{func(_ *Context) {}}) 389 router.GET("/path2", func(c *Context) {}) 390 w := performRequest(router, http.MethodPost, "/path2") 391 assert.Equal(t, http.StatusMethodNotAllowed, w.Code) 392 } 393 394 func TestRouteNotAllowedDisabled(t *testing.T) { 395 router := New() 396 router.HandleMethodNotAllowed = false 397 router.POST("/path", func(c *Context) {}) 398 w := performRequest(router, http.MethodGet, "/path") 399 assert.Equal(t, http.StatusNotFound, w.Code) 400 401 router.NoMethod(func(c *Context) { 402 c.String(http.StatusTeapot, "responseText") 403 }) 404 w = performRequest(router, http.MethodGet, "/path") 405 assert.Equal(t, "404 page not found", w.Body.String()) 406 assert.Equal(t, http.StatusNotFound, w.Code) 407 } 408 409 func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) { 410 router := New() 411 router.RemoveExtraSlash = true 412 router.GET("/path", func(c *Context) {}) 413 router.GET("/", func(c *Context) {}) 414 415 testRoutes := []struct { 416 route string 417 code int 418 location string 419 }{ 420 {"/../path", http.StatusOK, ""}, // CleanPath 421 {"/nope", http.StatusNotFound, ""}, // NotFound 422 } 423 for _, tr := range testRoutes { 424 w := performRequest(router, "GET", tr.route) 425 assert.Equal(t, tr.code, w.Code) 426 if w.Code != http.StatusNotFound { 427 assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) 428 } 429 } 430 } 431 432 func TestRouterNotFound(t *testing.T) { 433 router := New() 434 router.RedirectFixedPath = true 435 router.GET("/path", func(c *Context) {}) 436 router.GET("/dir/", func(c *Context) {}) 437 router.GET("/", func(c *Context) {}) 438 439 testRoutes := []struct { 440 route string 441 code int 442 location string 443 }{ 444 {"/path/", http.StatusMovedPermanently, "/path"}, // TSR -/ 445 {"/dir", http.StatusMovedPermanently, "/dir/"}, // TSR +/ 446 {"/PATH", http.StatusMovedPermanently, "/path"}, // Fixed Case 447 {"/DIR/", http.StatusMovedPermanently, "/dir/"}, // Fixed Case 448 {"/PATH/", http.StatusMovedPermanently, "/path"}, // Fixed Case -/ 449 {"/DIR", http.StatusMovedPermanently, "/dir/"}, // Fixed Case +/ 450 {"/../path", http.StatusMovedPermanently, "/path"}, // Without CleanPath 451 {"/nope", http.StatusNotFound, ""}, // NotFound 452 } 453 for _, tr := range testRoutes { 454 w := performRequest(router, http.MethodGet, tr.route) 455 assert.Equal(t, tr.code, w.Code) 456 if w.Code != http.StatusNotFound { 457 assert.Equal(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) 458 } 459 } 460 461 // Test custom not found handler 462 var notFound bool 463 router.NoRoute(func(c *Context) { 464 c.AbortWithStatus(http.StatusNotFound) 465 notFound = true 466 }) 467 w := performRequest(router, http.MethodGet, "/nope") 468 assert.Equal(t, http.StatusNotFound, w.Code) 469 assert.True(t, notFound) 470 471 // Test other method than GET (want 307 instead of 301) 472 router.PATCH("/path", func(c *Context) {}) 473 w = performRequest(router, http.MethodPatch, "/path/") 474 assert.Equal(t, http.StatusTemporaryRedirect, w.Code) 475 assert.Equal(t, "map[Location:[/path]]", fmt.Sprint(w.Header())) 476 477 // Test special case where no node for the prefix "/" exists 478 router = New() 479 router.GET("/a", func(c *Context) {}) 480 w = performRequest(router, http.MethodGet, "/") 481 assert.Equal(t, http.StatusNotFound, w.Code) 482 } 483 484 func TestRouterStaticFSNotFound(t *testing.T) { 485 router := New() 486 router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/"))) 487 router.NoRoute(func(c *Context) { 488 c.String(404, "non existent") 489 }) 490 491 w := performRequest(router, http.MethodGet, "/nonexistent") 492 assert.Equal(t, "non existent", w.Body.String()) 493 494 w = performRequest(router, http.MethodHead, "/nonexistent") 495 assert.Equal(t, "non existent", w.Body.String()) 496 } 497 498 func TestRouterStaticFSFileNotFound(t *testing.T) { 499 router := New() 500 501 router.StaticFS("/", http.FileSystem(http.Dir("."))) 502 503 assert.NotPanics(t, func() { 504 performRequest(router, http.MethodGet, "/nonexistent") 505 }) 506 } 507 508 // Reproduction test for the bug of issue #1805 509 func TestMiddlewareCalledOnceByRouterStaticFSNotFound(t *testing.T) { 510 router := New() 511 512 // Middleware must be called just only once by per request. 513 middlewareCalledNum := 0 514 router.Use(func(c *Context) { 515 middlewareCalledNum++ 516 }) 517 518 router.StaticFS("/", http.FileSystem(http.Dir("/thisreallydoesntexist/"))) 519 520 // First access 521 performRequest(router, http.MethodGet, "/nonexistent") 522 assert.Equal(t, 1, middlewareCalledNum) 523 524 // Second access 525 performRequest(router, http.MethodHead, "/nonexistent") 526 assert.Equal(t, 2, middlewareCalledNum) 527 } 528 529 func TestRouteRawPath(t *testing.T) { 530 route := New() 531 route.UseRawPath = true 532 533 route.POST("/project/:name/build/:num", func(c *Context) { 534 name := c.Params.ByName("name") 535 num := c.Params.ByName("num") 536 537 assert.Equal(t, name, c.Param("name")) 538 assert.Equal(t, num, c.Param("num")) 539 540 assert.Equal(t, "Some/Other/Project", name) 541 assert.Equal(t, "222", num) 542 }) 543 544 w := performRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/222") 545 assert.Equal(t, http.StatusOK, w.Code) 546 } 547 548 func TestRouteRawPathNoUnescape(t *testing.T) { 549 route := New() 550 route.UseRawPath = true 551 route.UnescapePathValues = false 552 553 route.POST("/project/:name/build/:num", func(c *Context) { 554 name := c.Params.ByName("name") 555 num := c.Params.ByName("num") 556 557 assert.Equal(t, name, c.Param("name")) 558 assert.Equal(t, num, c.Param("num")) 559 560 assert.Equal(t, "Some%2FOther%2FProject", name) 561 assert.Equal(t, "333", num) 562 }) 563 564 w := performRequest(route, http.MethodPost, "/project/Some%2FOther%2FProject/build/333") 565 assert.Equal(t, http.StatusOK, w.Code) 566 } 567 568 func TestRouteServeErrorWithWriteHeader(t *testing.T) { 569 route := New() 570 route.Use(func(c *Context) { 571 c.Status(421) 572 c.Next() 573 }) 574 575 w := performRequest(route, http.MethodGet, "/NotFound") 576 assert.Equal(t, 421, w.Code) 577 assert.Equal(t, 0, w.Body.Len()) 578 } 579 580 func TestRouteContextHoldsFullPath(t *testing.T) { 581 router := New() 582 583 // Test routes 584 routes := []string{ 585 "/simple", 586 "/project/:name", 587 "/", 588 "/news/home", 589 "/news", 590 "/simple-two/one", 591 "/simple-two/one-two", 592 "/project/:name/build/*params", 593 "/project/:name/bui", 594 "/user/:id/status", 595 "/user/:id", 596 "/user/:id/profile", 597 } 598 599 for _, route := range routes { 600 actualRoute := route 601 router.GET(route, func(c *Context) { 602 // For each defined route context should contain its full path 603 assert.Equal(t, actualRoute, c.FullPath()) 604 c.AbortWithStatus(http.StatusOK) 605 }) 606 } 607 608 for _, route := range routes { 609 w := performRequest(router, http.MethodGet, route) 610 assert.Equal(t, http.StatusOK, w.Code) 611 } 612 613 // Test not found 614 router.Use(func(c *Context) { 615 // For not found routes full path is empty 616 assert.Equal(t, "", c.FullPath()) 617 }) 618 619 w := performRequest(router, http.MethodGet, "/not-found") 620 assert.Equal(t, http.StatusNotFound, w.Code) 621 }