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 5package gin 6 7import ( 8 "fmt" 9 "io/ioutil" 10 "net/http" 11 "net/http/httptest" 12 "os" 13 "path/filepath" 14 "testing" 15 16 "github.com/stretchr/testify/assert" 17) 18 19func performRequest(r http.Handler, method, path string) *httptest.ResponseRecorder { 20 req, _ := http.NewRequest(method, path, nil) 21 w := httptest.NewRecorder() 22 r.ServeHTTP(w, req) 23 return w 24} 25 26func testRouteOK(method string, t *testing.T) { 27 passed := false 28 passedAny := false 29 r := New() 30 r.Any("/test2", func(c *Context) { 31 passedAny = true 32 }) 33 r.Handle(method, "/test", func(c *Context) { 34 passed = true 35 }) 36 37 w := performRequest(r, method, "/test") 38 assert.True(t, passed) 39 assert.Equal(t, w.Code, http.StatusOK) 40 41 performRequest(r, method, "/test2") 42 assert.True(t, passedAny) 43} 44 45// TestSingleRouteOK tests that POST route is correctly invoked. 46func testRouteNotOK(method string, t *testing.T) { 47 passed := false 48 router := New() 49 router.Handle(method, "/test_2", func(c *Context) { 50 passed = true 51 }) 52 53 w := performRequest(router, method, "/test") 54 55 assert.False(t, passed) 56 assert.Equal(t, w.Code, http.StatusNotFound) 57} 58 59// TestSingleRouteOK tests that POST route is correctly invoked. 60func testRouteNotOK2(method string, t *testing.T) { 61 passed := false 62 router := New() 63 router.HandleMethodNotAllowed = true 64 var methodRoute string 65 if method == "POST" { 66 methodRoute = "GET" 67 } else { 68 methodRoute = "POST" 69 } 70 router.Handle(methodRoute, "/test", func(c *Context) { 71 passed = true 72 }) 73 74 w := performRequest(router, method, "/test") 75 76 assert.False(t, passed) 77 assert.Equal(t, w.Code, http.StatusMethodNotAllowed) 78} 79 80func TestRouterMethod(t *testing.T) { 81 router := New() 82 router.PUT("/hey2", func(c *Context) { 83 c.String(200, "sup2") 84 }) 85 86 router.PUT("/hey", func(c *Context) { 87 c.String(200, "called") 88 }) 89 90 router.PUT("/hey3", func(c *Context) { 91 c.String(200, "sup3") 92 }) 93 94 w := performRequest(router, "PUT", "/hey") 95 96 assert.Equal(t, w.Code, 200) 97 assert.Equal(t, w.Body.String(), "called") 98} 99 100func TestRouterGroupRouteOK(t *testing.T) { 101 testRouteOK("GET", t) 102 testRouteOK("POST", t) 103 testRouteOK("PUT", t) 104 testRouteOK("PATCH", t) 105 testRouteOK("HEAD", t) 106 testRouteOK("OPTIONS", t) 107 testRouteOK("DELETE", t) 108 testRouteOK("CONNECT", t) 109 testRouteOK("TRACE", t) 110} 111 112func TestRouteNotOK(t *testing.T) { 113 testRouteNotOK("GET", t) 114 testRouteNotOK("POST", t) 115 testRouteNotOK("PUT", t) 116 testRouteNotOK("PATCH", t) 117 testRouteNotOK("HEAD", t) 118 testRouteNotOK("OPTIONS", t) 119 testRouteNotOK("DELETE", t) 120 testRouteNotOK("CONNECT", t) 121 testRouteNotOK("TRACE", t) 122} 123 124func TestRouteNotOK2(t *testing.T) { 125 testRouteNotOK2("GET", t) 126 testRouteNotOK2("POST", t) 127 testRouteNotOK2("PUT", t) 128 testRouteNotOK2("PATCH", t) 129 testRouteNotOK2("HEAD", t) 130 testRouteNotOK2("OPTIONS", t) 131 testRouteNotOK2("DELETE", t) 132 testRouteNotOK2("CONNECT", t) 133 testRouteNotOK2("TRACE", t) 134} 135 136func TestRouteRedirectTrailingSlash(t *testing.T) { 137 router := New() 138 router.RedirectFixedPath = false 139 router.RedirectTrailingSlash = true 140 router.GET("/path", func(c *Context) {}) 141 router.GET("/path2/", func(c *Context) {}) 142 router.POST("/path3", func(c *Context) {}) 143 router.PUT("/path4/", func(c *Context) {}) 144 145 w := performRequest(router, "GET", "/path/") 146 assert.Equal(t, w.Header().Get("Location"), "/path") 147 assert.Equal(t, w.Code, 301) 148 149 w = performRequest(router, "GET", "/path2") 150 assert.Equal(t, w.Header().Get("Location"), "/path2/") 151 assert.Equal(t, w.Code, 301) 152 153 w = performRequest(router, "POST", "/path3/") 154 assert.Equal(t, w.Header().Get("Location"), "/path3") 155 assert.Equal(t, w.Code, 307) 156 157 w = performRequest(router, "PUT", "/path4") 158 assert.Equal(t, w.Header().Get("Location"), "/path4/") 159 assert.Equal(t, w.Code, 307) 160 161 w = performRequest(router, "GET", "/path") 162 assert.Equal(t, w.Code, 200) 163 164 w = performRequest(router, "GET", "/path2/") 165 assert.Equal(t, w.Code, 200) 166 167 w = performRequest(router, "POST", "/path3") 168 assert.Equal(t, w.Code, 200) 169 170 w = performRequest(router, "PUT", "/path4/") 171 assert.Equal(t, w.Code, 200) 172 173 router.RedirectTrailingSlash = false 174 175 w = performRequest(router, "GET", "/path/") 176 assert.Equal(t, w.Code, 404) 177 w = performRequest(router, "GET", "/path2") 178 assert.Equal(t, w.Code, 404) 179 w = performRequest(router, "POST", "/path3/") 180 assert.Equal(t, w.Code, 404) 181 w = performRequest(router, "PUT", "/path4") 182 assert.Equal(t, w.Code, 404) 183} 184 185func TestRouteRedirectFixedPath(t *testing.T) { 186 router := New() 187 router.RedirectFixedPath = true 188 router.RedirectTrailingSlash = false 189 190 router.GET("/path", func(c *Context) {}) 191 router.GET("/Path2", func(c *Context) {}) 192 router.POST("/PATH3", func(c *Context) {}) 193 router.POST("/Path4/", func(c *Context) {}) 194 195 w := performRequest(router, "GET", "/PATH") 196 assert.Equal(t, w.Header().Get("Location"), "/path") 197 assert.Equal(t, w.Code, 301) 198 199 w = performRequest(router, "GET", "/path2") 200 assert.Equal(t, w.Header().Get("Location"), "/Path2") 201 assert.Equal(t, w.Code, 301) 202 203 w = performRequest(router, "POST", "/path3") 204 assert.Equal(t, w.Header().Get("Location"), "/PATH3") 205 assert.Equal(t, w.Code, 307) 206 207 w = performRequest(router, "POST", "/path4") 208 assert.Equal(t, w.Header().Get("Location"), "/Path4/") 209 assert.Equal(t, w.Code, 307) 210} 211 212// TestContextParamsGet tests that a parameter can be parsed from the URL. 213func TestRouteParamsByName(t *testing.T) { 214 name := "" 215 lastName := "" 216 wild := "" 217 router := New() 218 router.GET("/test/:name/:last_name/*wild", func(c *Context) { 219 name = c.Params.ByName("name") 220 lastName = c.Params.ByName("last_name") 221 var ok bool 222 wild, ok = c.Params.Get("wild") 223 224 assert.True(t, ok) 225 assert.Equal(t, name, c.Param("name")) 226 assert.Equal(t, name, c.Param("name")) 227 assert.Equal(t, lastName, c.Param("last_name")) 228 229 assert.Empty(t, c.Param("wtf")) 230 assert.Empty(t, c.Params.ByName("wtf")) 231 232 wtf, ok := c.Params.Get("wtf") 233 assert.Empty(t, wtf) 234 assert.False(t, ok) 235 }) 236 237 w := performRequest(router, "GET", "/test/john/smith/is/super/great") 238 239 assert.Equal(t, w.Code, 200) 240 assert.Equal(t, name, "john") 241 assert.Equal(t, lastName, "smith") 242 assert.Equal(t, wild, "/is/super/great") 243} 244 245// TestHandleStaticFile - ensure the static file handles properly 246func TestRouteStaticFile(t *testing.T) { 247 // SETUP file 248 testRoot, _ := os.Getwd() 249 f, err := ioutil.TempFile(testRoot, "") 250 if err != nil { 251 t.Error(err) 252 } 253 defer os.Remove(f.Name()) 254 f.WriteString("Gin Web Framework") 255 f.Close() 256 257 dir, filename := filepath.Split(f.Name()) 258 259 // SETUP gin 260 router := New() 261 router.Static("/using_static", dir) 262 router.StaticFile("/result", f.Name()) 263 264 w := performRequest(router, "GET", "/using_static/"+filename) 265 w2 := performRequest(router, "GET", "/result") 266 267 assert.Equal(t, w, w2) 268 assert.Equal(t, w.Code, 200) 269 assert.Equal(t, w.Body.String(), "Gin Web Framework") 270 assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") 271 272 w3 := performRequest(router, "HEAD", "/using_static/"+filename) 273 w4 := performRequest(router, "HEAD", "/result") 274 275 assert.Equal(t, w3, w4) 276 assert.Equal(t, w3.Code, 200) 277} 278 279// TestHandleStaticDir - ensure the root/sub dir handles properly 280func TestRouteStaticListingDir(t *testing.T) { 281 router := New() 282 router.StaticFS("/", Dir("./", true)) 283 284 w := performRequest(router, "GET", "/") 285 286 assert.Equal(t, w.Code, 200) 287 assert.Contains(t, w.Body.String(), "gin.go") 288 assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") 289} 290 291// TestHandleHeadToDir - ensure the root/sub dir handles properly 292func TestRouteStaticNoListing(t *testing.T) { 293 router := New() 294 router.Static("/", "./") 295 296 w := performRequest(router, "GET", "/") 297 298 assert.Equal(t, w.Code, 404) 299 assert.NotContains(t, w.Body.String(), "gin.go") 300} 301 302func TestRouterMiddlewareAndStatic(t *testing.T) { 303 router := New() 304 static := router.Group("/", func(c *Context) { 305 c.Writer.Header().Add("Last-Modified", "Mon, 02 Jan 2006 15:04:05 MST") 306 c.Writer.Header().Add("Expires", "Mon, 02 Jan 2006 15:04:05 MST") 307 c.Writer.Header().Add("X-GIN", "Gin Framework") 308 }) 309 static.Static("/", "./") 310 311 w := performRequest(router, "GET", "/gin.go") 312 313 assert.Equal(t, w.Code, 200) 314 assert.Contains(t, w.Body.String(), "package gin") 315 assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/plain; charset=utf-8") 316 assert.NotEqual(t, w.HeaderMap.Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") 317 assert.Equal(t, w.HeaderMap.Get("Expires"), "Mon, 02 Jan 2006 15:04:05 MST") 318 assert.Equal(t, w.HeaderMap.Get("x-GIN"), "Gin Framework") 319} 320 321func TestRouteNotAllowedEnabled(t *testing.T) { 322 router := New() 323 router.HandleMethodNotAllowed = true 324 router.POST("/path", func(c *Context) {}) 325 w := performRequest(router, "GET", "/path") 326 assert.Equal(t, w.Code, http.StatusMethodNotAllowed) 327 328 router.NoMethod(func(c *Context) { 329 c.String(http.StatusTeapot, "responseText") 330 }) 331 w = performRequest(router, "GET", "/path") 332 assert.Equal(t, w.Body.String(), "responseText") 333 assert.Equal(t, w.Code, http.StatusTeapot) 334} 335 336func TestRouteNotAllowedDisabled(t *testing.T) { 337 router := New() 338 router.HandleMethodNotAllowed = false 339 router.POST("/path", func(c *Context) {}) 340 w := performRequest(router, "GET", "/path") 341 assert.Equal(t, w.Code, 404) 342 343 router.NoMethod(func(c *Context) { 344 c.String(http.StatusTeapot, "responseText") 345 }) 346 w = performRequest(router, "GET", "/path") 347 assert.Equal(t, w.Body.String(), "404 page not found") 348 assert.Equal(t, w.Code, 404) 349} 350 351func TestRouterNotFound(t *testing.T) { 352 router := New() 353 router.RedirectFixedPath = true 354 router.GET("/path", func(c *Context) {}) 355 router.GET("/dir/", func(c *Context) {}) 356 router.GET("/", func(c *Context) {}) 357 358 testRoutes := []struct { 359 route string 360 code int 361 header string 362 }{ 363 {"/path/", 301, "map[Location:[/path]]"}, // TSR -/ 364 {"/dir", 301, "map[Location:[/dir/]]"}, // TSR +/ 365 {"", 301, "map[Location:[/]]"}, // TSR +/ 366 {"/PATH", 301, "map[Location:[/path]]"}, // Fixed Case 367 {"/DIR/", 301, "map[Location:[/dir/]]"}, // Fixed Case 368 {"/PATH/", 301, "map[Location:[/path]]"}, // Fixed Case -/ 369 {"/DIR", 301, "map[Location:[/dir/]]"}, // Fixed Case +/ 370 {"/../path", 301, "map[Location:[/path]]"}, // CleanPath 371 {"/nope", 404, ""}, // NotFound 372 } 373 for _, tr := range testRoutes { 374 w := performRequest(router, "GET", tr.route) 375 assert.Equal(t, w.Code, tr.code) 376 if w.Code != 404 { 377 assert.Equal(t, fmt.Sprint(w.Header()), tr.header) 378 } 379 } 380 381 // Test custom not found handler 382 var notFound bool 383 router.NoRoute(func(c *Context) { 384 c.AbortWithStatus(404) 385 notFound = true 386 }) 387 w := performRequest(router, "GET", "/nope") 388 assert.Equal(t, w.Code, 404) 389 assert.True(t, notFound) 390 391 // Test other method than GET (want 307 instead of 301) 392 router.PATCH("/path", func(c *Context) {}) 393 w = performRequest(router, "PATCH", "/path/") 394 assert.Equal(t, w.Code, 307) 395 assert.Equal(t, fmt.Sprint(w.Header()), "map[Location:[/path]]") 396 397 // Test special case where no node for the prefix "/" exists 398 router = New() 399 router.GET("/a", func(c *Context) {}) 400 w = performRequest(router, "GET", "/") 401 assert.Equal(t, w.Code, 404) 402} 403 404func TestRouteRawPath(t *testing.T) { 405 route := New() 406 route.UseRawPath = true 407 408 route.POST("/project/:name/build/:num", func(c *Context) { 409 name := c.Params.ByName("name") 410 num := c.Params.ByName("num") 411 412 assert.Equal(t, c.Param("name"), name) 413 assert.Equal(t, c.Param("num"), num) 414 415 assert.Equal(t, "Some/Other/Project", name) 416 assert.Equal(t, "222", num) 417 }) 418 419 w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/222") 420 assert.Equal(t, w.Code, 200) 421} 422 423func TestRouteRawPathNoUnescape(t *testing.T) { 424 route := New() 425 route.UseRawPath = true 426 route.UnescapePathValues = false 427 428 route.POST("/project/:name/build/:num", func(c *Context) { 429 name := c.Params.ByName("name") 430 num := c.Params.ByName("num") 431 432 assert.Equal(t, c.Param("name"), name) 433 assert.Equal(t, c.Param("num"), num) 434 435 assert.Equal(t, "Some%2FOther%2FProject", name) 436 assert.Equal(t, "333", num) 437 }) 438 439 w := performRequest(route, "POST", "/project/Some%2FOther%2FProject/build/333") 440 assert.Equal(t, w.Code, 200) 441} 442