github.com/cloudwego/hertz@v0.9.3/pkg/route/routes_test.go (about) 1 /* 2 * Copyright 2022 CloudWeGo Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * The MIT License (MIT) 16 * 17 * Copyright (c) 2014 Manuel MartÃnez-Almeida 18 * 19 * Permission is hereby granted, free of charge, to any person obtaining a copy 20 * of this software and associated documentation files (the "Software"), to deal 21 * in the Software without restriction, including without limitation the rights 22 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 23 * copies of the Software, and to permit persons to whom the Software is 24 * furnished to do so, subject to the following conditions: 25 * 26 * The above copyright notice and this permission notice shall be included in 27 * all copies or substantial portions of the Software. 28 * 29 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 35 * THE SOFTWARE. 36 * 37 * This file may have been modified by CloudWeGo authors. All CloudWeGo 38 * Modifications are Copyright 2022 CloudWeGo Authors 39 */ 40 41 package route 42 43 import ( 44 "context" 45 "fmt" 46 "io/ioutil" 47 "net/http" 48 "net/http/httptest" 49 "os" 50 "path/filepath" 51 "strings" 52 "testing" 53 54 "github.com/cloudwego/hertz/pkg/app" 55 "github.com/cloudwego/hertz/pkg/common/config" 56 "github.com/cloudwego/hertz/pkg/common/test/assert" 57 "github.com/cloudwego/hertz/pkg/protocol" 58 "github.com/cloudwego/hertz/pkg/protocol/consts" 59 ) 60 61 type header struct { 62 Key string 63 Value string 64 } 65 66 func performRequest(e *Engine, method, path string, headers ...header) *httptest.ResponseRecorder { 67 ctx := e.ctxPool.Get().(*app.RequestContext) 68 ctx.HTMLRender = e.htmlRender 69 70 r := protocol.NewRequest(method, path, nil) 71 r.CopyTo(&ctx.Request) 72 for _, v := range headers { 73 ctx.Request.Header.Add(v.Key, v.Value) 74 } 75 76 e.ServeHTTP(context.Background(), ctx) 77 78 w := httptest.NewRecorder() 79 h := w.Header() 80 ctx.Response.Header.VisitAll(func(key, value []byte) { 81 h.Add(string(key), string(value)) 82 }) 83 w.WriteHeader(ctx.Response.StatusCode()) 84 if _, err := w.Write(ctx.Response.Body()); err != nil { 85 panic(err.Error()) 86 } 87 ctx.Reset() 88 e.ctxPool.Put(ctx) 89 90 return w 91 } 92 93 func testRouteOK(method string, t *testing.T) { 94 passed := false 95 passedAny := false 96 r := NewEngine(config.NewOptions(nil)) 97 r.Any("/test2", func(c context.Context, ctx *app.RequestContext) { 98 passedAny = true 99 }) 100 r.Handle(method, "/test", func(c context.Context, ctx *app.RequestContext) { 101 passed = true 102 }) 103 104 w := performRequest(r, method, "/test") 105 assert.DeepEqual(t, true, passed) 106 assert.DeepEqual(t, consts.StatusOK, w.Code) 107 108 performRequest(r, method, "/test2") 109 assert.DeepEqual(t, true, passedAny) 110 } 111 112 // TestSingleRouteOK tests that POST route is correctly invoked. 113 func testRouteNotOK(method string, t *testing.T) { 114 passed := false 115 router := NewEngine(config.NewOptions(nil)) 116 router.Handle(method, "/test_2", func(c context.Context, ctx *app.RequestContext) { 117 passed = true 118 }) 119 120 w := performRequest(router, method, "/test") 121 122 assert.DeepEqual(t, false, passed) 123 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 124 } 125 126 // TestSingleRouteOK tests that POST route is correctly invoked. 127 func testRouteNotOK2(method string, t *testing.T) { 128 passed := false 129 router := NewEngine(config.NewOptions(nil)) 130 router.options.HandleMethodNotAllowed = true 131 var methodRoute string 132 if method == consts.MethodPost { 133 methodRoute = consts.MethodGet 134 } else { 135 methodRoute = consts.MethodPost 136 } 137 router.Handle(methodRoute, "/test", func(c context.Context, ctx *app.RequestContext) { 138 passed = true 139 }) 140 141 w := performRequest(router, method, "/test") 142 143 assert.DeepEqual(t, false, passed) 144 assert.DeepEqual(t, consts.StatusMethodNotAllowed, w.Code) 145 } 146 147 func testRouteNotOK3(method string, t *testing.T) { 148 passed := false 149 router := NewEngine(config.NewOptions(nil)) 150 router.Handle("GET", "/api/v:version/product/local/products/list", func(c context.Context, ctx *app.RequestContext) { 151 passed = true 152 }) 153 router.Handle("GET", "/api/v:version/product/product_creation/preload_all_categories", func(c context.Context, ctx *app.RequestContext) { 154 passed = true 155 }) 156 157 w := performRequest(router, method, "/api/v1/product/products/activate") 158 159 assert.DeepEqual(t, false, passed) 160 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 161 } 162 163 func TestRouterMethod(t *testing.T) { 164 router := NewEngine(config.NewOptions(nil)) 165 router.PUT("/hey2", func(c context.Context, ctx *app.RequestContext) { 166 ctx.String(consts.StatusOK, "sup2") 167 }) 168 169 router.PUT("/hey", func(c context.Context, ctx *app.RequestContext) { 170 ctx.String(consts.StatusOK, "called") 171 }) 172 173 router.PUT("/hey3", func(c context.Context, ctx *app.RequestContext) { 174 ctx.String(consts.StatusOK, "sup3") 175 }) 176 177 w := performRequest(router, consts.MethodPut, "/hey") 178 179 assert.DeepEqual(t, consts.StatusOK, w.Code) 180 assert.DeepEqual(t, "called", w.Body.String()) 181 } 182 183 func TestRouterGroupRouteOK(t *testing.T) { 184 testRouteOK(consts.MethodGet, t) 185 testRouteOK(consts.MethodPost, t) 186 testRouteOK(consts.MethodPut, t) 187 testRouteOK(consts.MethodPatch, t) 188 testRouteOK(consts.MethodHead, t) 189 testRouteOK(consts.MethodOptions, t) 190 testRouteOK(consts.MethodDelete, t) 191 testRouteOK(consts.MethodConnect, t) 192 testRouteOK(consts.MethodTrace, t) 193 } 194 195 func TestRouteNotOK1(t *testing.T) { 196 testRouteNotOK(consts.MethodGet, t) 197 testRouteNotOK(consts.MethodPost, t) 198 testRouteNotOK(consts.MethodPut, t) 199 testRouteNotOK(consts.MethodPatch, t) 200 testRouteNotOK(consts.MethodHead, t) 201 testRouteNotOK(consts.MethodOptions, t) 202 testRouteNotOK(consts.MethodDelete, t) 203 testRouteNotOK(consts.MethodConnect, t) 204 testRouteNotOK(consts.MethodTrace, t) 205 } 206 207 func TestRouteNotOK2(t *testing.T) { 208 testRouteNotOK2(consts.MethodGet, t) 209 testRouteNotOK2(consts.MethodPost, t) 210 testRouteNotOK2(consts.MethodPut, t) 211 testRouteNotOK2(consts.MethodPatch, t) 212 testRouteNotOK2(consts.MethodHead, t) 213 testRouteNotOK2(consts.MethodOptions, t) 214 testRouteNotOK2(consts.MethodDelete, t) 215 testRouteNotOK2(consts.MethodConnect, t) 216 testRouteNotOK2(consts.MethodTrace, t) 217 } 218 219 func TestRouteNotOK3(t *testing.T) { 220 testRouteNotOK3(consts.MethodGet, t) 221 testRouteNotOK3(consts.MethodPost, t) 222 testRouteNotOK3(consts.MethodPut, t) 223 testRouteNotOK3(consts.MethodPatch, t) 224 testRouteNotOK3(consts.MethodHead, t) 225 testRouteNotOK3(consts.MethodOptions, t) 226 testRouteNotOK3(consts.MethodDelete, t) 227 testRouteNotOK3(consts.MethodConnect, t) 228 testRouteNotOK3(consts.MethodTrace, t) 229 } 230 231 func TestRouteRedirectTrailingSlash(t *testing.T) { 232 router := NewEngine(config.NewOptions(nil)) 233 router.options.RedirectFixedPath = false 234 router.options.RedirectTrailingSlash = true 235 router.GET("/path", func(c context.Context, ctx *app.RequestContext) {}) 236 router.GET("/path2/", func(c context.Context, ctx *app.RequestContext) {}) 237 router.POST("/path3", func(c context.Context, ctx *app.RequestContext) {}) 238 router.PUT("/path4/", func(c context.Context, ctx *app.RequestContext) {}) 239 240 w := performRequest(router, consts.MethodGet, "/path/") 241 assert.DeepEqual(t, "/path", w.Header().Get("Location")) 242 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 243 244 w = performRequest(router, consts.MethodGet, "/path2") 245 assert.DeepEqual(t, "/path2/", w.Header().Get("Location")) 246 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 247 248 w = performRequest(router, consts.MethodPost, "/path3/") 249 assert.DeepEqual(t, "/path3", w.Header().Get("Location")) 250 assert.DeepEqual(t, consts.StatusTemporaryRedirect, w.Code) 251 252 w = performRequest(router, consts.MethodPut, "/path4") 253 assert.DeepEqual(t, "/path4/", w.Header().Get("Location")) 254 assert.DeepEqual(t, consts.StatusTemporaryRedirect, w.Code) 255 256 w = performRequest(router, consts.MethodGet, "/path") 257 assert.DeepEqual(t, consts.StatusOK, w.Code) 258 259 w = performRequest(router, consts.MethodGet, "/path2/") 260 assert.DeepEqual(t, consts.StatusOK, w.Code) 261 262 w = performRequest(router, consts.MethodPost, "/path3") 263 assert.DeepEqual(t, consts.StatusOK, w.Code) 264 265 w = performRequest(router, consts.MethodPut, "/path4/") 266 assert.DeepEqual(t, consts.StatusOK, w.Code) 267 268 w = performRequest(router, consts.MethodGet, "/path2", header{Key: "X-Forwarded-Prefix", Value: "/api"}) 269 assert.DeepEqual(t, "/api/path2/", w.Header().Get("Location")) 270 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 271 272 w = performRequest(router, consts.MethodGet, "/path2/", header{Key: "X-Forwarded-Prefix", Value: "/api/"}) 273 assert.DeepEqual(t, consts.StatusOK, w.Code) 274 275 router.options.RedirectTrailingSlash = false 276 277 w = performRequest(router, consts.MethodGet, "/path/") 278 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 279 w = performRequest(router, consts.MethodGet, "/path2") 280 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 281 w = performRequest(router, consts.MethodPost, "/path3/") 282 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 283 w = performRequest(router, consts.MethodPut, "/path4") 284 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 285 } 286 287 func TestRouteRedirectTrailingSlashWithQuery(t *testing.T) { 288 router := NewEngine(config.NewOptions(nil)) 289 router.options.RedirectFixedPath = false 290 router.options.RedirectTrailingSlash = true 291 router.GET("/path", func(c context.Context, ctx *app.RequestContext) {}) 292 router.GET("/path2/", func(c context.Context, ctx *app.RequestContext) {}) 293 294 w := performRequest(router, consts.MethodGet, "/path/?offset=2") 295 assert.DeepEqual(t, "/path?offset=2", w.Header().Get("Location")) 296 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 297 298 w = performRequest(router, consts.MethodGet, "/path2?offset=2") 299 assert.DeepEqual(t, "/path2/?offset=2", w.Header().Get("Location")) 300 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 301 } 302 303 func TestRouteRedirectFixedPath(t *testing.T) { 304 router := NewEngine(config.NewOptions(nil)) 305 router.options.RedirectFixedPath = true 306 router.options.RedirectTrailingSlash = false 307 308 router.GET("/path", func(c context.Context, ctx *app.RequestContext) {}) 309 router.GET("/Path2", func(c context.Context, ctx *app.RequestContext) {}) 310 router.POST("/PATH3", func(c context.Context, ctx *app.RequestContext) {}) 311 router.POST("/Path4/", func(c context.Context, ctx *app.RequestContext) {}) 312 313 w := performRequest(router, consts.MethodGet, "/PATH") 314 assert.DeepEqual(t, "/path", w.Header().Get("Location")) 315 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 316 317 w = performRequest(router, consts.MethodGet, "/path2") 318 assert.DeepEqual(t, "/Path2", w.Header().Get("Location")) 319 assert.DeepEqual(t, consts.StatusMovedPermanently, w.Code) 320 321 w = performRequest(router, consts.MethodPost, "/path3") 322 assert.DeepEqual(t, "/PATH3", w.Header().Get("Location")) 323 assert.DeepEqual(t, consts.StatusTemporaryRedirect, w.Code) 324 325 w = performRequest(router, consts.MethodPost, "/path4") 326 assert.DeepEqual(t, "/Path4/", w.Header().Get("Location")) 327 assert.DeepEqual(t, consts.StatusTemporaryRedirect, w.Code) 328 } 329 330 // TestContextParamsGet tests that a parameter can be parsed from the URL. 331 func TestRouteParamsByName(t *testing.T) { 332 name := "" 333 lastName := "" 334 wild := "" 335 router := NewEngine(config.NewOptions(nil)) 336 router.GET("/test/:name/:last_name/*wild", func(c context.Context, ctx *app.RequestContext) { 337 name = ctx.Params.ByName("name") 338 lastName = ctx.Params.ByName("last_name") 339 var ok bool 340 wild, ok = ctx.Params.Get("wild") 341 342 assert.DeepEqual(t, true, ok) 343 assert.DeepEqual(t, name, ctx.Param("name")) 344 assert.DeepEqual(t, name, ctx.Param("name")) 345 assert.DeepEqual(t, lastName, ctx.Param("last_name")) 346 347 assert.DeepEqual(t, "", ctx.Param("wtf")) 348 assert.DeepEqual(t, "", ctx.Params.ByName("wtf")) 349 350 wtf, ok := ctx.Params.Get("wtf") 351 assert.DeepEqual(t, "", wtf) 352 assert.False(t, ok) 353 }) 354 355 w := performRequest(router, consts.MethodGet, "/test/john/smith/is/super/great") 356 357 assert.DeepEqual(t, consts.StatusOK, w.Code) 358 assert.DeepEqual(t, "john", name) 359 assert.DeepEqual(t, "smith", lastName) 360 assert.DeepEqual(t, "is/super/great", wild) 361 } 362 363 // TestContextParamsGet tests that a parameter can be parsed from the URL even with extra slashes. 364 func TestRouteParamsByNameWithExtraSlash(t *testing.T) { 365 name := "" 366 lastName := "" 367 wild := "" 368 router := NewEngine(config.NewOptions(nil)) 369 router.options.RemoveExtraSlash = true 370 router.GET("/test/:name/:last_name/*wild", func(c context.Context, ctx *app.RequestContext) { 371 name = ctx.Params.ByName("name") 372 lastName = ctx.Params.ByName("last_name") 373 var ok bool 374 wild, ok = ctx.Params.Get("wild") 375 376 assert.True(t, ok) 377 assert.DeepEqual(t, name, ctx.Param("name")) 378 assert.DeepEqual(t, name, ctx.Param("name")) 379 assert.DeepEqual(t, lastName, ctx.Param("last_name")) 380 381 assert.DeepEqual(t, "", ctx.Param("wtf")) 382 assert.DeepEqual(t, "", ctx.Params.ByName("wtf")) 383 384 wtf, ok := ctx.Params.Get("wtf") 385 assert.DeepEqual(t, "", wtf) 386 assert.False(t, ok) 387 }) 388 389 w := performRequest(router, consts.MethodGet, "/test//john//smith//is//super//great") 390 391 assert.DeepEqual(t, consts.StatusOK, w.Code) 392 assert.DeepEqual(t, "john", name) 393 assert.DeepEqual(t, "smith", lastName) 394 assert.DeepEqual(t, "is/super/great", wild) 395 } 396 397 // TestHandleStaticFile - ensure the static file handles properly 398 func TestRouteStaticFile(t *testing.T) { 399 // SETUP file 400 testRoot, _ := os.Getwd() 401 f, err := ioutil.TempFile(testRoot, "") 402 if err != nil { 403 t.Error(err) 404 } 405 defer os.Remove(f.Name()) 406 _, err = f.WriteString("Hertz Web Framework") 407 assert.Nil(t, err) 408 f.Close() 409 410 dir, filename := filepath.Split(f.Name()) 411 412 // SETUP engine 413 router := NewEngine(config.NewOptions(nil)) 414 router.StaticFS("/using_static", &app.FS{Root: dir, AcceptByteRange: true, PathRewrite: app.NewPathSlashesStripper(1)}) 415 router.StaticFile("/result", f.Name()) 416 417 w := performRequest(router, consts.MethodGet, "/using_static/"+filename) 418 w2 := performRequest(router, consts.MethodGet, "/result") 419 420 assert.DeepEqual(t, w, w2) 421 assert.DeepEqual(t, consts.StatusOK, w.Code) 422 assert.DeepEqual(t, "Hertz Web Framework", w.Body.String()) 423 assert.DeepEqual(t, "text/plain; charset=utf-8", w.Header().Get("Content-Type")) 424 425 w3 := performRequest(router, consts.MethodHead, "/using_static/"+filename) 426 w4 := performRequest(router, consts.MethodHead, "/result") 427 428 assert.DeepEqual(t, w3, w4) 429 assert.DeepEqual(t, consts.StatusOK, w3.Code) 430 } 431 432 // TestHandleStaticDir - ensure the root/sub dir handles properly 433 func TestRouteStaticListingDir(t *testing.T) { 434 router := NewEngine(config.NewOptions(nil)) 435 router.StaticFS("/", &app.FS{Root: "./", GenerateIndexPages: true}) 436 437 w := performRequest(router, consts.MethodGet, "/") 438 assert.DeepEqual(t, consts.StatusOK, w.Code) 439 440 assert.True(t, strings.Contains(w.Body.String(), "engine.go")) 441 assert.DeepEqual(t, "text/html; charset=utf-8", w.Header().Get("Content-Type")) 442 } 443 444 // TestHandleHeadToDir - ensure the root/sub dir handles properly 445 func TestRouteStaticNoListing(t *testing.T) { 446 router := NewEngine(config.NewOptions(nil)) 447 router.Static("/", "./") 448 449 w := performRequest(router, consts.MethodGet, "/") 450 451 assert.DeepEqual(t, http.StatusForbidden, w.Code) 452 assert.False(t, strings.Contains(w.Body.String(), "engine.go")) 453 } 454 455 func TestRouterMiddlewareAndStatic(t *testing.T) { 456 router := NewEngine(config.NewOptions(nil)) 457 static := router.Group("/", func(c context.Context, ctx *app.RequestContext) { 458 ctx.Response.Header.Set("Last-Modified", "Mon, 02 Jan 2006 15:04:05 MST") 459 ctx.Response.Header.Set("Last-Modified", "Mon, 02 Jan 2006 15:04:05 MST") 460 ctx.Response.Header.Set("Expires", "Mon, 02 Jan 2006 15:04:05 MST") 461 ctx.Response.Header.Set("X-Hertz", "Hertz Framework") 462 }) 463 static.Static("/", "./") 464 465 w := performRequest(router, consts.MethodGet, "/engine.go") 466 467 assert.DeepEqual(t, consts.StatusOK, w.Code) 468 assert.True(t, strings.Contains(w.Body.String(), "package route")) 469 // when Go version <= 1.16, mime.TypeByExtension will return Content-Type='text/plain; charset=utf-8', 470 // otherwise it will return Content-Type='text/x-go; charset=utf-8' 471 assert.NotEqual(t, "", w.Header().Get("Content-Type")) 472 assert.NotEqual(t, w.Header().Get("Last-Modified"), "Mon, 02 Jan 2006 15:04:05 MST") 473 assert.DeepEqual(t, "Mon, 02 Jan 2006 15:04:05 MST", w.Header().Get("Expires")) 474 assert.DeepEqual(t, "Hertz Framework", w.Header().Get("x-Hertz")) 475 } 476 477 func TestRouteNotAllowedEnabled(t *testing.T) { 478 router := NewEngine(config.NewOptions(nil)) 479 router.options.HandleMethodNotAllowed = true 480 router.POST("/path", func(c context.Context, ctx *app.RequestContext) {}) 481 w := performRequest(router, consts.MethodGet, "/path") 482 assert.DeepEqual(t, consts.StatusMethodNotAllowed, w.Code) 483 484 router.NoMethod(func(c context.Context, ctx *app.RequestContext) { 485 ctx.String(http.StatusTeapot, "responseText") 486 }) 487 w = performRequest(router, consts.MethodGet, "/path") 488 assert.DeepEqual(t, "responseText", w.Body.String()) 489 assert.DeepEqual(t, http.StatusTeapot, w.Code) 490 } 491 492 func TestRouteNotAllowedEnabled2(t *testing.T) { 493 router := NewEngine(config.NewOptions(nil)) 494 router.options.HandleMethodNotAllowed = true 495 // add one methodTree to trees 496 router.addRoute(consts.MethodPost, "/", app.HandlersChain{func(_ context.Context, _ *app.RequestContext) {}}) 497 router.GET("/path2", func(c context.Context, ctx *app.RequestContext) {}) 498 w := performRequest(router, consts.MethodPost, "/path2") 499 assert.DeepEqual(t, consts.StatusMethodNotAllowed, w.Code) 500 } 501 502 func TestRouteNotAllowedDisabled(t *testing.T) { 503 router := NewEngine(config.NewOptions(nil)) 504 router.options.HandleMethodNotAllowed = false 505 router.POST("/path", func(c context.Context, ctx *app.RequestContext) {}) 506 w := performRequest(router, consts.MethodGet, "/path") 507 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 508 509 router.NoMethod(func(c context.Context, ctx *app.RequestContext) { 510 ctx.String(http.StatusTeapot, "responseText") 511 }) 512 w = performRequest(router, consts.MethodGet, "/path") 513 assert.DeepEqual(t, "404 page not found", w.Body.String()) 514 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 515 } 516 517 func TestRouterNotFoundWithRemoveExtraSlash(t *testing.T) { 518 router := NewEngine(config.NewOptions(nil)) 519 router.options.RemoveExtraSlash = true 520 router.GET("/path", func(c context.Context, ctx *app.RequestContext) {}) 521 router.GET("/", func(c context.Context, ctx *app.RequestContext) {}) 522 523 testRoutes := []struct { 524 route string 525 code int 526 location string 527 }{ 528 {"/../path", consts.StatusOK, ""}, // CleanPath 529 {"/nope", consts.StatusNotFound, ""}, // NotFound 530 } 531 for _, tr := range testRoutes { 532 w := performRequest(router, "GET", tr.route) 533 assert.DeepEqual(t, tr.code, w.Code) 534 if w.Code != consts.StatusNotFound { 535 assert.DeepEqual(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) 536 } 537 } 538 } 539 540 func TestRouterNotFound(t *testing.T) { 541 router := NewEngine(config.NewOptions(nil)) 542 router.options.RedirectFixedPath = true 543 router.GET("/path", func(c context.Context, ctx *app.RequestContext) {}) 544 router.GET("/dir/", func(c context.Context, ctx *app.RequestContext) {}) 545 router.GET("/", func(c context.Context, ctx *app.RequestContext) {}) 546 547 testRoutes := []struct { 548 route string 549 code int 550 location string 551 }{ 552 {"/path/", consts.StatusMovedPermanently, "/path"}, // TSR -/ 553 {"/dir", consts.StatusMovedPermanently, "/dir/"}, // TSR +/ 554 {"/PATH", consts.StatusMovedPermanently, "/path"}, // Fixed Case 555 {"/DIR/", consts.StatusMovedPermanently, "/dir/"}, // Fixed Case 556 {"/PATH/", consts.StatusMovedPermanently, "/path"}, // Fixed Case -/ 557 {"/DIR", consts.StatusMovedPermanently, "/dir/"}, // Fixed Case +/ 558 {"/../path/", consts.StatusMovedPermanently, "/path"}, // Without CleanPath 559 {"/nope", consts.StatusNotFound, ""}, // NotFound 560 } 561 for _, tr := range testRoutes { 562 w := performRequest(router, consts.MethodGet, tr.route) 563 assert.DeepEqual(t, tr.code, w.Code) 564 if w.Code != consts.StatusNotFound { 565 assert.DeepEqual(t, tr.location, fmt.Sprint(w.Header().Get("Location"))) 566 } 567 } 568 569 // Test custom not found handler 570 var notFound bool 571 router.NoRoute(func(c context.Context, ctx *app.RequestContext) { 572 ctx.AbortWithStatus(consts.StatusNotFound) 573 notFound = true 574 }) 575 w := performRequest(router, consts.MethodGet, "/nope") 576 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 577 assert.True(t, notFound) 578 579 // Test other method than GET (want 307 instead of 301) 580 router.PATCH("/path", func(c context.Context, ctx *app.RequestContext) {}) 581 w = performRequest(router, consts.MethodPatch, "/path/") 582 assert.DeepEqual(t, consts.StatusTemporaryRedirect, w.Code) 583 assert.DeepEqual(t, "map[Content-Type:[text/plain; charset=utf-8] Location:[/path]]", fmt.Sprint(w.Header())) 584 585 // Test special case where no node for the prefix "/" exists 586 router = NewEngine(config.NewOptions(nil)) 587 router.GET("/a", func(c context.Context, ctx *app.RequestContext) {}) 588 w = performRequest(router, consts.MethodGet, "/") 589 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 590 } 591 592 func TestRouterStaticFSNotFound(t *testing.T) { 593 router := NewEngine(config.NewOptions(nil)) 594 router.StaticFS("/", &app.FS{Root: "/thisreallydoesntexist/"}) 595 router.NoRoute(func(c context.Context, ctx *app.RequestContext) { 596 ctx.String(consts.StatusNotFound, "non existent") 597 }) 598 599 w := performRequest(router, consts.MethodGet, "/nonexistent") 600 assert.DeepEqual(t, "Cannot open requested path", w.Body.String()) 601 602 w = performRequest(router, consts.MethodHead, "/nonexistent") 603 assert.DeepEqual(t, "Cannot open requested path", w.Body.String()) 604 } 605 606 func TestRouterStaticFSFileNotFound(t *testing.T) { 607 router := NewEngine(config.NewOptions(nil)) 608 609 router.StaticFS("/", &app.FS{Root: "."}) 610 611 assert.NotPanic(t, func() { 612 performRequest(router, consts.MethodGet, "/nonexistent") 613 }) 614 } 615 616 func TestMiddlewareCalledOnceByRouterStaticFSNotFound(t *testing.T) { 617 router := NewEngine(config.NewOptions(nil)) 618 619 // Middleware must be called just only once by per request. 620 middlewareCalledNum := 0 621 router.Use(func(c context.Context, ctx *app.RequestContext) { 622 middlewareCalledNum++ 623 }) 624 625 router.StaticFS("/", &app.FS{Root: "/thisreallydoesntexist/"}) 626 627 // First access 628 performRequest(router, consts.MethodGet, "/nonexistent") 629 assert.DeepEqual(t, 1, middlewareCalledNum) 630 631 // Second access 632 performRequest(router, consts.MethodHead, "/nonexistent") 633 assert.DeepEqual(t, 2, middlewareCalledNum) 634 } 635 636 func TestRouteRawPath(t *testing.T) { 637 route := NewEngine(config.NewOptions(nil)) 638 route.options.UseRawPath = true 639 640 route.POST("/project/:name/build/:num", func(c context.Context, ctx *app.RequestContext) { 641 name := ctx.Params.ByName("name") 642 num := ctx.Params.ByName("num") 643 644 assert.DeepEqual(t, name, ctx.Param("name")) 645 assert.DeepEqual(t, num, ctx.Param("num")) 646 647 assert.DeepEqual(t, "Some/Other/Project", name) 648 assert.DeepEqual(t, "222", num) 649 }) 650 651 w := performRequest(route, consts.MethodPost, "/project/Some%2FOther%2FProject/build/222") 652 assert.DeepEqual(t, consts.StatusOK, w.Code) 653 } 654 655 func TestRouteRawPathNoUnescape(t *testing.T) { 656 route := NewEngine(config.NewOptions(nil)) 657 route.options.UseRawPath = true 658 route.options.UnescapePathValues = false 659 660 route.POST("/project/:name/build/:num", func(c context.Context, ctx *app.RequestContext) { 661 name := ctx.Params.ByName("name") 662 num := ctx.Params.ByName("num") 663 664 assert.DeepEqual(t, name, ctx.Param("name")) 665 assert.DeepEqual(t, num, ctx.Param("num")) 666 667 assert.DeepEqual(t, "Some%2FOther%2FProject", name) 668 assert.DeepEqual(t, "333", num) 669 }) 670 671 w := performRequest(route, consts.MethodPost, "/project/Some%2FOther%2FProject/build/333") 672 assert.DeepEqual(t, consts.StatusOK, w.Code) 673 } 674 675 func TestRouteServeErrorWithWriteHeader(t *testing.T) { 676 route := NewEngine(config.NewOptions(nil)) 677 route.Use(func(c context.Context, ctx *app.RequestContext) { 678 ctx.SetStatusCode(421) 679 ctx.Next(c) 680 }) 681 682 w := performRequest(route, consts.MethodGet, "/NotFound") 683 assert.DeepEqual(t, 421, w.Code) 684 assert.DeepEqual(t, 0, w.Body.Len()) 685 } 686 687 func TestRouteContextHoldsFullPath(t *testing.T) { 688 router := NewEngine(config.NewOptions(nil)) 689 690 // Test routes 691 routes := []string{ 692 "/simple", 693 "/project/:name", 694 "/", 695 "/news/home", 696 "/news", 697 "/simple-two/one", 698 "/simple-two/one-two", 699 "/project/:name/build/*params", 700 "/project/:name/bui", 701 "/user/:id/status", 702 "/user/:id", 703 "/user/:id/profile", 704 } 705 706 for _, route := range routes { 707 actualRoute := route 708 router.GET(route, func(c context.Context, ctx *app.RequestContext) { 709 // For each defined route context should contain its full path 710 assert.DeepEqual(t, actualRoute, ctx.FullPath()) 711 ctx.AbortWithStatus(consts.StatusOK) 712 }) 713 } 714 715 for _, route := range routes { 716 w := performRequest(router, consts.MethodGet, route) 717 assert.DeepEqual(t, consts.StatusOK, w.Code) 718 } 719 720 // Test not found 721 router.Use(func(c context.Context, ctx *app.RequestContext) { 722 // For not found routes full path is empty 723 assert.DeepEqual(t, "", ctx.FullPath()) 724 }) 725 726 w := performRequest(router, consts.MethodGet, "/not-found") 727 assert.DeepEqual(t, consts.StatusNotFound, w.Code) 728 } 729 730 func checkUnusedParamValues(t *testing.T, ctx *app.RequestContext, expectParam map[string]string) { 731 for _, p := range ctx.Params { 732 if expectParam == nil { 733 t.Errorf("pValue '%+v' is set for param name '%v' but we are not expecting it", p.Value, p.Key) 734 } else if val, ok := expectParam[p.Key]; !ok || val != p.Value { 735 t.Errorf("'%+v' is set for param name '%v' but we are expecting it with expectParam '%+v'", p.Value, p.Key, val) 736 } 737 } 738 } 739 740 var handlerFunc = func(route string) app.HandlersChain { 741 return app.HandlersChain{func(c context.Context, ctx *app.RequestContext) { 742 ctx.Set("path", route) 743 }} 744 } 745 746 var handlerHelper = func(route, key string, value int) app.HandlersChain { 747 return app.HandlersChain{func(c context.Context, ctx *app.RequestContext) { 748 ctx.Set(key, value) 749 ctx.Set("path", route) 750 }} 751 } 752 753 func getHelper(c *app.RequestContext, key string) interface{} { 754 p, _ := c.Get(key) 755 return p 756 } 757 758 func TestRouterStatic(t *testing.T) { 759 e := NewEngine(config.NewOptions(nil)) 760 path := "/folders/a/files/hertz.gif" 761 e.addRoute(consts.MethodGet, path, handlerFunc(path)) 762 c := e.NewContext() 763 c.Request.SetRequestURI(path) 764 c.Request.Header.SetMethod(consts.MethodGet) 765 e.ServeHTTP(context.Background(), c) 766 assert.DeepEqual(t, path, getHelper(c, "path")) 767 } 768 769 func TestRouterParam(t *testing.T) { 770 e := NewEngine(config.NewOptions(nil)) 771 e.addRoute(consts.MethodGet, "/users/:id", handlerFunc("/users/:id")) 772 773 testCases := []struct { 774 name string 775 whenURL string 776 expectRoute interface{} 777 expectParam map[string]string 778 }{ 779 { 780 name: "route /users/1 to /users/:id", 781 whenURL: "/users/1", 782 expectRoute: "/users/:id", 783 expectParam: map[string]string{"id": "1"}, 784 }, 785 { 786 name: "route /users/1/ to /users/:id", 787 whenURL: "/users/1/", 788 expectRoute: nil, 789 expectParam: nil, 790 }, 791 } 792 793 for _, tc := range testCases { 794 t.Run(tc.name, func(t *testing.T) { 795 c := e.NewContext() 796 c.Request.SetRequestURI(tc.whenURL) 797 c.Request.Header.SetMethod(consts.MethodGet) 798 e.ServeHTTP(context.Background(), c) 799 assert.DeepEqual(t, tc.expectRoute, getHelper(c, "path")) 800 checkUnusedParamValues(t, c, tc.expectParam) 801 }) 802 } 803 } 804 805 func TestRouterTwoParam(t *testing.T) { 806 e := NewEngine(config.NewOptions(nil)) 807 e.addRoute(consts.MethodGet, "/users/:uid/files/:fid", handlerFunc("/users/:uid/files/:fid")) 808 ctx := e.NewContext() 809 ctx.Request.SetRequestURI("/users/1/files/1") 810 ctx.Request.Header.SetMethod(consts.MethodGet) 811 e.ServeHTTP(context.Background(), ctx) 812 813 assert.DeepEqual(t, "1", ctx.Param("uid")) 814 assert.DeepEqual(t, "1", ctx.Param("fid")) 815 } 816 817 func TestRouterParamWithSlash(t *testing.T) { 818 e := NewEngine(config.NewOptions(nil)) 819 820 e.addRoute(consts.MethodGet, "/a/:b/c/d/:e", handlerFunc("/a/:b/c/d/:e")) 821 e.addRoute(consts.MethodGet, "/a/:b/c/:d/:f", handlerFunc("/a/:b/c/:d/:f")) 822 823 ctx := e.NewContext() 824 ctx.Request.SetRequestURI("/a/1/c/d/2/3") 825 ctx.Request.Header.SetMethod(consts.MethodGet) 826 e.ServeHTTP(context.Background(), ctx) 827 assert.Nil(t, getHelper(ctx, "path")) 828 assert.DeepEqual(t, consts.StatusNotFound, ctx.Response.StatusCode()) 829 } 830 831 func TestRouteMultiLevelBacktracking(t *testing.T) { 832 testCases := []struct { 833 name string 834 whenURL string 835 expectRoute interface{} 836 expectParam map[string]string 837 }{ 838 { 839 name: "route /a/c/df to /a/c/df", 840 whenURL: "/a/c/df", 841 expectRoute: "/a/c/df", 842 }, 843 { 844 name: "route /a/x/df to /a/:b/c", 845 whenURL: "/a/x/c", 846 expectRoute: "/a/:b/c", 847 expectParam: map[string]string{"b": "x"}, 848 }, 849 // { 850 // name: "route /a/x/f to /a/*/f", 851 // whenURL: "/a/x/f", 852 // expectRoute: "/a/*/f", 853 // expectParam: map[string]string{"x": "x/f"}, // NOTE: `x` would be probably more suitable 854 // }, 855 { 856 name: "route /b/c/f to /:e/c/f", 857 whenURL: "/b/c/f", 858 expectRoute: "/:e/c/f", 859 expectParam: map[string]string{"e": "b"}, 860 }, 861 { 862 name: "route /b/c/c to /*x", 863 whenURL: "/b/c/c", 864 expectRoute: "/*x", 865 expectParam: map[string]string{"x": "b/c/c"}, 866 }, 867 } 868 869 e := NewEngine(config.NewOptions(nil)) 870 871 e.addRoute(consts.MethodGet, "/a/:b/c", handlerHelper("/a/:b/c", "case", 1)) 872 e.addRoute(consts.MethodGet, "/a/c/d", handlerHelper("/a/c/d", "case", 2)) 873 e.addRoute(consts.MethodGet, "/a/c/df", handlerHelper("/a/c/df", "case", 3)) 874 // e.addRoute(consts.MethodGet, "/a/*/f", handlerHelper("case", 4)) 875 e.addRoute(consts.MethodGet, "/:e/c/f", handlerHelper("/:e/c/f", "case", 5)) 876 e.addRoute(consts.MethodGet, "/*x", handlerHelper("/*x", "case", 6)) 877 878 for _, tc := range testCases { 879 t.Run(tc.name, func(t *testing.T) { 880 ctx := e.NewContext() 881 ctx.Request.SetRequestURI(tc.whenURL) 882 ctx.Request.Header.SetMethod(consts.MethodGet) 883 e.ServeHTTP(context.Background(), ctx) 884 885 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 886 for param, expectedValue := range tc.expectParam { 887 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 888 } 889 checkUnusedParamValues(t, ctx, tc.expectParam) 890 }) 891 } 892 } 893 894 func TestRouteMultiLevelBacktracking2(t *testing.T) { 895 e := NewEngine(config.NewOptions(nil)) 896 e.addRoute(consts.MethodGet, "/a/:b/c", handlerFunc("/a/:b/c")) 897 e.addRoute(consts.MethodGet, "/a/c/d", handlerFunc("/a/c/d")) 898 e.addRoute(consts.MethodGet, "/a/c/df", handlerFunc("/a/c/df")) 899 e.addRoute(consts.MethodGet, "/:e/c/f", handlerFunc("/:e/c/f")) 900 e.addRoute(consts.MethodGet, "/*x", handlerFunc("/*x")) 901 902 testCases := []struct { 903 name string 904 whenURL string 905 expectRoute string 906 expectParam map[string]string 907 }{ 908 { 909 name: "route /a/c/df to /a/c/df", 910 whenURL: "/a/c/df", 911 expectRoute: "/a/c/df", 912 }, 913 { 914 name: "route /a/x/df to /a/:b/c", 915 whenURL: "/a/x/c", 916 expectRoute: "/a/:b/c", 917 expectParam: map[string]string{"b": "x"}, 918 }, 919 { 920 name: "route /a/c/f to /:e/c/f", 921 whenURL: "/a/c/f", 922 expectRoute: "/:e/c/f", 923 expectParam: map[string]string{"e": "a"}, 924 }, 925 { 926 name: "route /b/c/f to /:e/c/f", 927 whenURL: "/b/c/f", 928 expectRoute: "/:e/c/f", 929 expectParam: map[string]string{"e": "b"}, 930 }, 931 { 932 name: "route /b/c/c to /*", 933 whenURL: "/b/c/c", 934 expectRoute: "/*x", 935 expectParam: map[string]string{"x": "b/c/c"}, 936 }, 937 { // this traverses `/a/:b/c` and `/:e/c/f` branches and eventually backtracks to `/*` 938 name: "route /a/c/cf to /*", 939 whenURL: "/a/c/cf", 940 expectRoute: "/*x", 941 expectParam: map[string]string{"x": "a/c/cf"}, 942 }, 943 { 944 name: "route /anyMatch to /*", 945 whenURL: "/anyMatch", 946 expectRoute: "/*x", 947 expectParam: map[string]string{"x": "anyMatch"}, 948 }, 949 { 950 name: "route /anyMatch/withSlash to /*", 951 whenURL: "/anyMatch/withSlash", 952 expectRoute: "/*x", 953 expectParam: map[string]string{"x": "anyMatch/withSlash"}, 954 }, 955 } 956 957 for _, tc := range testCases { 958 t.Run(tc.name, func(t *testing.T) { 959 ctx := e.NewContext() 960 ctx.Request.SetRequestURI(tc.whenURL) 961 ctx.Request.Header.SetMethod(consts.MethodGet) 962 e.ServeHTTP(context.Background(), ctx) 963 964 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 965 for param, expectedValue := range tc.expectParam { 966 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 967 } 968 checkUnusedParamValues(t, ctx, tc.expectParam) 969 }) 970 } 971 } 972 973 func TestRouterBacktrackingFromMultipleParamKinds(t *testing.T) { 974 e := NewEngine(config.NewOptions(nil)) 975 e.addRoute(consts.MethodGet, "/*x", handlerFunc("/*x")) // this can match only path that does not have slash in it 976 e.addRoute(consts.MethodGet, "/:1/second", handlerFunc("/:1/second")) 977 e.addRoute(consts.MethodGet, "/:1/:2", handlerFunc("/:1/:2")) // this acts as match ANY for all routes that have at least one slash 978 e.addRoute(consts.MethodGet, "/:1/:2/third", handlerFunc("/:1/:2/third")) 979 e.addRoute(consts.MethodGet, "/:1/:2/:3/fourth", handlerFunc("/:1/:2/:3/fourth")) 980 e.addRoute(consts.MethodGet, "/:1/:2/:3/:4/fifth", handlerFunc("/:1/:2/:3/:4/fifth")) 981 982 testCases := []struct { 983 name string 984 whenURL string 985 expectRoute string 986 expectParam map[string]string 987 }{ 988 { 989 name: "route /first to /*", 990 whenURL: "/first", 991 expectRoute: "/*x", 992 expectParam: map[string]string{"x": "first"}, 993 }, 994 { 995 name: "route /first/second to /:1/second", 996 whenURL: "/first/second", 997 expectRoute: "/:1/second", 998 expectParam: map[string]string{"1": "first"}, 999 }, 1000 { 1001 name: "route /first/second-new to /:1/:2", 1002 whenURL: "/first/second-new", 1003 expectRoute: "/:1/:2", 1004 expectParam: map[string]string{ 1005 "1": "first", 1006 "2": "second-new", 1007 }, 1008 }, 1009 { 1010 name: "route /first/second/ to /:1/:2", 1011 whenURL: "/first/second/", 1012 expectRoute: "/*x", // "/:1/:2", 1013 expectParam: map[string]string{"x": "first/second/"}, 1014 }, 1015 { 1016 name: "route /first/second/third/fourth/fifth/nope to /:1/:2", 1017 whenURL: "/first/second/third/fourth/fifth/nope", 1018 expectRoute: "/*x", 1019 expectParam: map[string]string{"x": "first/second/third/fourth/fifth/nope"}, 1020 }, 1021 } 1022 1023 for _, tc := range testCases { 1024 t.Run(tc.name, func(t *testing.T) { 1025 ctx := e.NewContext() 1026 ctx.Request.SetRequestURI(tc.whenURL) 1027 ctx.Request.Header.SetMethod(consts.MethodGet) 1028 e.ServeHTTP(context.Background(), ctx) 1029 1030 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1031 for param, expectedValue := range tc.expectParam { 1032 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1033 } 1034 checkUnusedParamValues(t, ctx, tc.expectParam) 1035 }) 1036 } 1037 } 1038 1039 func TestRouterParamStaticConflict(t *testing.T) { 1040 e := NewEngine(config.NewOptions(nil)) 1041 1042 g := e.Group("/g") 1043 g.GET("/skills", handlerFunc("/g/skills")...) 1044 g.GET("/status", handlerFunc("/g/status")...) 1045 g.GET("/:name", handlerFunc("/g/:name")...) 1046 1047 testCases := []struct { 1048 whenURL string 1049 expectRoute interface{} 1050 expectParam map[string]string 1051 }{ 1052 { 1053 whenURL: "/g/s", 1054 expectRoute: "/g/:name", 1055 expectParam: map[string]string{"name": "s"}, 1056 }, 1057 { 1058 whenURL: "/g/status", 1059 expectRoute: "/g/status", 1060 expectParam: map[string]string{"name": ""}, 1061 }, 1062 } 1063 for _, tc := range testCases { 1064 t.Run(tc.whenURL, func(t *testing.T) { 1065 ctx := e.NewContext() 1066 1067 ctx.Request.SetRequestURI(tc.whenURL) 1068 ctx.Request.Header.SetMethod(consts.MethodGet) 1069 e.ServeHTTP(context.Background(), ctx) 1070 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1071 for param, expectedValue := range tc.expectParam { 1072 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1073 } 1074 checkUnusedParamValues(t, ctx, tc.expectParam) 1075 }) 1076 } 1077 } 1078 1079 func TestRouterMatchAny(t *testing.T) { 1080 e := NewEngine(config.NewOptions(nil)) 1081 1082 // Routes 1083 e.addRoute(consts.MethodGet, "/", handlerFunc("/")) 1084 e.addRoute(consts.MethodGet, "/*x", handlerFunc("/*x")) 1085 e.addRoute(consts.MethodGet, "/users/*x", handlerFunc("/users/*x")) 1086 1087 testCases := []struct { 1088 whenURL string 1089 expectRoute interface{} 1090 expectParam map[string]string 1091 }{ 1092 { 1093 whenURL: "/", 1094 expectRoute: "/", 1095 expectParam: map[string]string{"x": ""}, 1096 }, 1097 { 1098 whenURL: "/download", 1099 expectRoute: "/*x", 1100 expectParam: map[string]string{"x": "download"}, 1101 }, 1102 { 1103 whenURL: "/users/joe", 1104 expectRoute: "/users/*x", 1105 expectParam: map[string]string{"x": "joe"}, 1106 }, 1107 } 1108 for _, tc := range testCases { 1109 t.Run(tc.whenURL, func(t *testing.T) { 1110 ctx := e.NewContext() 1111 1112 ctx.Request.SetRequestURI(tc.whenURL) 1113 ctx.Request.Header.SetMethod(consts.MethodGet) 1114 e.ServeHTTP(context.Background(), ctx) 1115 1116 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1117 for param, expectedValue := range tc.expectParam { 1118 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1119 } 1120 checkUnusedParamValues(t, ctx, tc.expectParam) 1121 }) 1122 } 1123 } 1124 1125 // NOTE: This is to document current implementation. Last added route with `*` asterisk is always the match and no 1126 // backtracking or more precise matching is done to find more suitable match. 1127 // 1128 // Current behaviour might not be correct or expected. 1129 // But this is where we are without well defined requirements/rules how (multiple) asterisks work in route 1130 func TestRouterAnyMatchesLastAddedAnyRoute(t *testing.T) { 1131 e := NewEngine(config.NewOptions(nil)) 1132 1133 e.addRoute(consts.MethodGet, "/users/*x", handlerHelper("/users/*x", "case", 1)) 1134 // e.addRoute(consts.MethodGet, "/users/*x/action*y", handlerHelper("/users/*x/action*y", "case", 2)) 1135 1136 ctx := e.NewContext() 1137 1138 ctx.Request.SetRequestURI("/users/xxx/action/sea") 1139 ctx.Request.Header.SetMethod(consts.MethodGet) 1140 e.ServeHTTP(context.Background(), ctx) 1141 assert.DeepEqual(t, "/users/*x", getHelper(ctx, "path")) 1142 assert.DeepEqual(t, "xxx/action/sea", ctx.Param("x")) 1143 1144 // if we add another route then it is the last added and so it is matched 1145 // e.addRoute(consts.MethodGet, "/users/*x/action/search", handlerHelper("/users/*x/action/search", "case", 3)) 1146 1147 // c.Request.SetRequestURI("/users/xxx/action/sea") 1148 // c.Request.Header.SetMethod(consts.MethodGet) 1149 // e.ServeHTTP(context.Background(), c) 1150 // test.DeepEqual(t, "/users/*x/action/search", getHelper(c, "path")) 1151 // test.DeepEqual(t, "xxx/action/sea", ctx.Param("x")) 1152 } 1153 1154 func TestRouterMatchAnyPrefixIssue(t *testing.T) { 1155 e := NewEngine(config.NewOptions(nil)) 1156 1157 // Routes 1158 e.addRoute(consts.MethodGet, "/*x", handlerFunc("/*x")) 1159 e.addRoute(consts.MethodGet, "/users/*x", handlerFunc("/users/*x")) 1160 1161 testCases := []struct { 1162 whenURL string 1163 expectRoute interface{} 1164 expectParam map[string]string 1165 }{ 1166 { 1167 whenURL: "/", 1168 expectRoute: "/*x", 1169 expectParam: map[string]string{"x": ""}, 1170 }, 1171 { 1172 whenURL: "/users", 1173 expectRoute: "/*x", 1174 expectParam: map[string]string{"x": "users"}, 1175 }, 1176 { 1177 whenURL: "/users/", 1178 expectRoute: "/users/*x", 1179 expectParam: map[string]string{"x": ""}, 1180 }, 1181 { 1182 whenURL: "/users_prefix", 1183 expectRoute: "/*x", 1184 expectParam: map[string]string{"x": "users_prefix"}, 1185 }, 1186 { 1187 whenURL: "/users_prefix/", 1188 expectRoute: "/*x", 1189 expectParam: map[string]string{"x": "users_prefix/"}, 1190 }, 1191 } 1192 for _, tc := range testCases { 1193 t.Run(tc.whenURL, func(t *testing.T) { 1194 ctx := e.NewContext() 1195 1196 ctx.Request.SetRequestURI(tc.whenURL) 1197 ctx.Request.Header.SetMethod(consts.MethodGet) 1198 e.ServeHTTP(context.Background(), ctx) 1199 1200 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1201 for param, expectedValue := range tc.expectParam { 1202 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1203 } 1204 checkUnusedParamValues(t, ctx, tc.expectParam) 1205 }) 1206 } 1207 } 1208 1209 // TestRouterMatchAnySlash shall verify finding the best route 1210 // for any routes with trailing slash requests 1211 func TestRouterMatchAnySlash(t *testing.T) { 1212 e := NewEngine(config.NewOptions(nil)) 1213 1214 // Routes 1215 e.addRoute(consts.MethodGet, "/users", handlerFunc("/users")) 1216 e.addRoute(consts.MethodGet, "/users/*x", handlerFunc("/users/*x")) 1217 e.addRoute(consts.MethodGet, "/img/*x", handlerFunc("/img/*x")) 1218 e.addRoute(consts.MethodGet, "/img/load", handlerFunc("/img/load")) 1219 e.addRoute(consts.MethodGet, "/img/load/*x", handlerFunc("/img/load/*x")) 1220 e.addRoute(consts.MethodGet, "/assets/*x", handlerFunc("/assets/*x")) 1221 1222 testCases := []struct { 1223 whenURL string 1224 expectRoute interface{} 1225 expectParam map[string]string 1226 expectError error 1227 }{ 1228 { 1229 whenURL: "/", 1230 expectRoute: nil, 1231 expectParam: map[string]string{"x": ""}, 1232 }, 1233 { // Test trailing slash request for simple any route (see #1526) 1234 whenURL: "/users/", 1235 expectRoute: "/users/*x", 1236 expectParam: map[string]string{"x": ""}, 1237 }, 1238 { 1239 whenURL: "/users/joe", 1240 expectRoute: "/users/*x", 1241 expectParam: map[string]string{"x": "joe"}, 1242 }, 1243 // Test trailing slash request for nested any route (see #1526) 1244 { 1245 whenURL: "/img/load", 1246 expectRoute: "/img/load", 1247 expectParam: map[string]string{"x": ""}, 1248 }, 1249 { 1250 whenURL: "/img/load/", 1251 expectRoute: "/img/load/*x", 1252 expectParam: map[string]string{"x": ""}, 1253 }, 1254 { 1255 whenURL: "/img/load/ben", 1256 expectRoute: "/img/load/*x", 1257 expectParam: map[string]string{"x": "ben"}, 1258 }, 1259 // Test /assets/*x any route 1260 { // ... without trailing slash must not match 1261 whenURL: "/assets", 1262 expectRoute: nil, 1263 expectParam: map[string]string{"x": ""}, 1264 }, 1265 1266 { // ... with trailing slash must match 1267 whenURL: "/assets/", 1268 expectRoute: "/assets/*x", 1269 expectParam: map[string]string{"x": ""}, 1270 }, 1271 } 1272 for _, tc := range testCases { 1273 t.Run(tc.whenURL, func(t *testing.T) { 1274 ctx := e.NewContext() 1275 1276 ctx.Request.SetRequestURI(tc.whenURL) 1277 ctx.Request.Header.SetMethod(consts.MethodGet) 1278 e.ServeHTTP(context.Background(), ctx) 1279 1280 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1281 for param, expectedValue := range tc.expectParam { 1282 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1283 } 1284 checkUnusedParamValues(t, ctx, tc.expectParam) 1285 }) 1286 } 1287 } 1288 1289 func TestRouterMatchAnyMultiLevel(t *testing.T) { 1290 e := NewEngine(config.NewOptions(nil)) 1291 1292 // Routes 1293 e.addRoute(consts.MethodGet, "/api/users/jack", handlerFunc("/api/users/jack")) 1294 e.addRoute(consts.MethodGet, "/api/users/jill", handlerFunc("/api/users/jill")) 1295 e.addRoute(consts.MethodGet, "/api/users/*x", handlerFunc("/api/users/*x")) 1296 e.addRoute(consts.MethodGet, "/api/*x", handlerFunc("/api/*x")) 1297 e.addRoute(consts.MethodGet, "/other/*x", handlerFunc("/other/*x")) 1298 e.addRoute(consts.MethodGet, "/*x", handlerFunc("/*x")) 1299 1300 testCases := []struct { 1301 whenURL string 1302 expectRoute interface{} 1303 expectParam map[string]string 1304 expectError error 1305 }{ 1306 { 1307 whenURL: "/api/users/jack", 1308 expectRoute: "/api/users/jack", 1309 expectParam: map[string]string{"x": ""}, 1310 }, 1311 { 1312 whenURL: "/api/users/jill", 1313 expectRoute: "/api/users/jill", 1314 expectParam: map[string]string{"x": ""}, 1315 }, 1316 { 1317 whenURL: "/api/users/joe", 1318 expectRoute: "/api/users/*x", 1319 expectParam: map[string]string{"x": "joe"}, 1320 }, 1321 { 1322 whenURL: "/api/nousers/joe", 1323 expectRoute: "/api/*x", 1324 expectParam: map[string]string{"x": "nousers/joe"}, 1325 }, 1326 { 1327 whenURL: "/api/none", 1328 expectRoute: "/api/*x", 1329 expectParam: map[string]string{"x": "none"}, 1330 }, 1331 { 1332 whenURL: "/api/none", 1333 expectRoute: "/api/*x", 1334 expectParam: map[string]string{"x": "none"}, 1335 }, 1336 { 1337 whenURL: "/noapi/users/jim", 1338 expectRoute: "/*x", 1339 expectParam: map[string]string{"x": "noapi/users/jim"}, 1340 }, 1341 } 1342 for _, tc := range testCases { 1343 t.Run(tc.whenURL, func(t *testing.T) { 1344 ctx := e.NewContext() 1345 1346 ctx.Request.SetRequestURI(tc.whenURL) 1347 ctx.Request.Header.SetMethod(consts.MethodGet) 1348 e.ServeHTTP(context.Background(), ctx) 1349 1350 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1351 for param, expectedValue := range tc.expectParam { 1352 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1353 } 1354 checkUnusedParamValues(t, ctx, tc.expectParam) 1355 }) 1356 } 1357 } 1358 1359 func TestRouterMatchAnyMultiLevelWithPost(t *testing.T) { 1360 e := NewEngine(config.NewOptions(nil)) 1361 1362 // Routes 1363 e.POST("/api/auth/login", handlerFunc("/api/auth/login")...) 1364 e.POST("/api/auth/forgotPassword", handlerFunc("/api/auth/forgotPassword")...) 1365 e.Any("/api/*x", handlerFunc("/api/*x")...) 1366 e.Any("/*x", handlerFunc("/*x")...) 1367 1368 testCases := []struct { 1369 whenMethod string 1370 whenURL string 1371 expectRoute interface{} 1372 expectParam map[string]string 1373 expectError error 1374 }{ 1375 { // POST /api/auth/login shall choose login method 1376 whenURL: "/api/auth/login", 1377 whenMethod: consts.MethodPost, 1378 expectRoute: "/api/auth/login", 1379 expectParam: map[string]string{"x": ""}, 1380 }, 1381 { // POST /api/auth/logout shall choose nearest any route 1382 whenURL: "/api/auth/logout", 1383 whenMethod: consts.MethodPost, 1384 expectRoute: "/api/*x", 1385 expectParam: map[string]string{"x": "auth/logout"}, 1386 }, 1387 { // POST to /api/other/test shall choose nearest any route 1388 whenURL: "/api/other/test", 1389 whenMethod: consts.MethodPost, 1390 expectRoute: "/api/*x", 1391 expectParam: map[string]string{"x": "other/test"}, 1392 }, 1393 { // GET to /api/other/test shall choose nearest any route 1394 whenURL: "/api/other/test", 1395 whenMethod: consts.MethodGet, 1396 expectRoute: "/api/*x", 1397 expectParam: map[string]string{"x": "other/test"}, 1398 }, 1399 } 1400 for _, tc := range testCases { 1401 t.Run(tc.whenURL, func(t *testing.T) { 1402 ctx := e.NewContext() 1403 ctx.Request.SetRequestURI(tc.whenURL) 1404 ctx.Request.Header.SetMethod(tc.whenMethod) 1405 e.ServeHTTP(context.Background(), ctx) 1406 1407 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1408 for param, expectedValue := range tc.expectParam { 1409 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1410 } 1411 checkUnusedParamValues(t, ctx, tc.expectParam) 1412 }) 1413 } 1414 } 1415 1416 func TestRouterMicroParam(t *testing.T) { 1417 e := NewEngine(config.NewOptions(nil)) 1418 e.addRoute(consts.MethodGet, "/:a/:b/:c", handlerFunc("/:a/:b/:c")) 1419 ctx := e.NewContext() 1420 ctx.Request.SetRequestURI("/1/2/3") 1421 ctx.Request.Header.SetMethod(consts.MethodGet) 1422 e.ServeHTTP(context.Background(), ctx) 1423 assert.DeepEqual(t, "1", ctx.Param("a")) 1424 assert.DeepEqual(t, "2", ctx.Param("b")) 1425 assert.DeepEqual(t, "3", ctx.Param("c")) 1426 } 1427 1428 func TestRouterMixParamMatchAny(t *testing.T) { 1429 e := NewEngine(config.NewOptions(nil)) 1430 1431 // Route 1432 e.addRoute(consts.MethodGet, "/users/:id/*x", handlerFunc("/users/:id/*x")) 1433 ctx := e.NewContext() 1434 1435 ctx.Request.SetRequestURI("/users/joe/comments") 1436 ctx.Request.Header.SetMethod(consts.MethodGet) 1437 e.ServeHTTP(context.Background(), ctx) 1438 assert.DeepEqual(t, "joe", ctx.Param("id")) 1439 } 1440 1441 func TestRouterMultiRoute(t *testing.T) { 1442 e := NewEngine(config.NewOptions(nil)) 1443 1444 // Routes 1445 e.addRoute(consts.MethodGet, "/users", handlerFunc("/users")) 1446 e.addRoute(consts.MethodGet, "/users/:id", handlerFunc("/users/:id")) 1447 1448 testCases := []struct { 1449 whenMethod string 1450 whenURL string 1451 expectRoute interface{} 1452 expectParam map[string]string 1453 expectError error 1454 }{ 1455 { 1456 whenURL: "/users", 1457 expectRoute: "/users", 1458 expectParam: map[string]string{"x": ""}, 1459 }, 1460 { 1461 whenURL: "/users/1", 1462 expectRoute: "/users/:id", 1463 expectParam: map[string]string{"id": "1"}, 1464 }, 1465 { 1466 whenURL: "/user", 1467 expectRoute: nil, 1468 expectParam: map[string]string{"x": ""}, 1469 }, 1470 } 1471 for _, tc := range testCases { 1472 t.Run(tc.whenURL, func(t *testing.T) { 1473 ctx := e.NewContext() 1474 ctx.Request.SetRequestURI(tc.whenURL) 1475 ctx.Request.Header.SetMethod(consts.MethodGet) 1476 e.ServeHTTP(context.Background(), ctx) 1477 1478 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1479 for param, expectedValue := range tc.expectParam { 1480 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1481 } 1482 checkUnusedParamValues(t, ctx, tc.expectParam) 1483 }) 1484 } 1485 } 1486 1487 func TestRouterPriority(t *testing.T) { 1488 e := NewEngine(config.NewOptions(nil)) 1489 1490 // Routes 1491 e.addRoute(consts.MethodGet, "/users", handlerFunc("/users")) 1492 e.addRoute(consts.MethodGet, "/users/new", handlerFunc("/users/new")) 1493 e.addRoute(consts.MethodGet, "/users/:id", handlerFunc("/users/:id")) 1494 e.addRoute(consts.MethodGet, "/users/dew", handlerFunc("/users/dew")) 1495 e.addRoute(consts.MethodGet, "/users/:id/files", handlerFunc("/users/:id/files")) 1496 e.addRoute(consts.MethodGet, "/users/newsee", handlerFunc("/users/newsee")) 1497 e.addRoute(consts.MethodGet, "/users/*x", handlerFunc("/users/*x")) 1498 e.addRoute(consts.MethodGet, "/users/new/*x", handlerFunc("/users/new/*x")) 1499 e.addRoute(consts.MethodGet, "/*x", handlerFunc("/*x")) 1500 1501 testCases := []struct { 1502 whenMethod string 1503 whenURL string 1504 expectRoute interface{} 1505 expectParam map[string]string 1506 expectError error 1507 }{ 1508 { 1509 whenURL: "/users", 1510 expectRoute: "/users", 1511 }, 1512 { 1513 whenURL: "/users/new", 1514 expectRoute: "/users/new", 1515 }, 1516 { 1517 whenURL: "/users/1", 1518 expectRoute: "/users/:id", 1519 expectParam: map[string]string{"id": "1"}, 1520 }, 1521 { 1522 whenURL: "/users/dew", 1523 expectRoute: "/users/dew", 1524 }, 1525 { 1526 whenURL: "/users/1/files", 1527 expectRoute: "/users/:id/files", 1528 expectParam: map[string]string{"id": "1"}, 1529 }, 1530 { 1531 whenURL: "/users/new", 1532 expectRoute: "/users/new", 1533 }, 1534 { 1535 whenURL: "/users/news", 1536 expectRoute: "/users/:id", 1537 expectParam: map[string]string{"id": "news"}, 1538 }, 1539 { 1540 whenURL: "/users/newsee", 1541 expectRoute: "/users/newsee", 1542 }, 1543 { 1544 whenURL: "/users/joe/books", 1545 expectRoute: "/users/*x", 1546 expectParam: map[string]string{"x": "joe/books"}, 1547 }, 1548 { 1549 whenURL: "/users/new/someone", 1550 expectRoute: "/users/new/*x", 1551 expectParam: map[string]string{"x": "someone"}, 1552 }, 1553 { 1554 whenURL: "/users/dew/someone", 1555 expectRoute: "/users/*x", 1556 expectParam: map[string]string{"x": "dew/someone"}, 1557 }, 1558 { // Route > /users/*x should be matched although /users/dew exists 1559 whenURL: "/users/notexists/someone", 1560 expectRoute: "/users/*x", 1561 expectParam: map[string]string{"x": "notexists/someone"}, 1562 }, 1563 { 1564 whenURL: "/nousers", 1565 expectRoute: "/*x", 1566 expectParam: map[string]string{"x": "nousers"}, 1567 }, 1568 { 1569 whenURL: "/nousers/new", 1570 expectRoute: "/*x", 1571 expectParam: map[string]string{"x": "nousers/new"}, 1572 }, 1573 } 1574 for _, tc := range testCases { 1575 t.Run(tc.whenURL, func(t *testing.T) { 1576 ctx := e.NewContext() 1577 ctx.Request.SetRequestURI(tc.whenURL) 1578 ctx.Request.Header.SetMethod(consts.MethodGet) 1579 e.ServeHTTP(context.Background(), ctx) 1580 1581 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1582 for param, expectedValue := range tc.expectParam { 1583 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1584 } 1585 checkUnusedParamValues(t, ctx, tc.expectParam) 1586 }) 1587 } 1588 } 1589 1590 func TestRouterIssue1348(t *testing.T) { 1591 e := NewEngine(config.NewOptions(nil)) 1592 1593 e.addRoute(consts.MethodGet, "/:lang/", handlerFunc("/:lang/")) 1594 e.addRoute(consts.MethodGet, "/:lang/dupa", handlerFunc("/:lang/dupa")) 1595 } 1596 1597 func TestRouterPriorityNotFound(t *testing.T) { 1598 e := NewEngine(config.NewOptions(nil)) 1599 1600 // Add 1601 e.addRoute(consts.MethodGet, "/a/foo", handlerFunc("/a/foo")) 1602 e.addRoute(consts.MethodGet, "/a/bar", handlerFunc("/a/bar")) 1603 1604 testCases := []struct { 1605 whenMethod string 1606 whenURL string 1607 expectRoute interface{} 1608 expectParam map[string]string 1609 expectError error 1610 }{ 1611 { 1612 whenURL: "/a/foo", 1613 expectRoute: "/a/foo", 1614 }, 1615 { 1616 whenURL: "/a/bar", 1617 expectRoute: "/a/bar", 1618 }, 1619 { 1620 whenURL: "/abc/def", 1621 expectRoute: nil, 1622 }, 1623 } 1624 for _, tc := range testCases { 1625 t.Run(tc.whenURL, func(t *testing.T) { 1626 ctx := e.NewContext() 1627 ctx.Request.SetRequestURI(tc.whenURL) 1628 ctx.Request.Header.SetMethod(consts.MethodGet) 1629 e.ServeHTTP(context.Background(), ctx) 1630 1631 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1632 for param, expectedValue := range tc.expectParam { 1633 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1634 } 1635 checkUnusedParamValues(t, ctx, tc.expectParam) 1636 }) 1637 } 1638 } 1639 1640 func TestRouterParamNames(t *testing.T) { 1641 e := NewEngine(config.NewOptions(nil)) 1642 1643 // Routes 1644 e.addRoute(consts.MethodGet, "/users", handlerFunc("/users")) 1645 e.addRoute(consts.MethodGet, "/users/:id", handlerFunc("/users/:id")) 1646 e.addRoute(consts.MethodGet, "/users/:uid/files/:fid", handlerFunc("/users/:uid/files/:fid")) 1647 1648 testCases := []struct { 1649 whenMethod string 1650 whenURL string 1651 expectRoute interface{} 1652 expectParam map[string]string 1653 expectError error 1654 }{ 1655 { 1656 whenURL: "/users", 1657 expectRoute: "/users", 1658 }, 1659 { 1660 whenURL: "/users/1", 1661 expectRoute: "/users/:id", 1662 expectParam: map[string]string{"id": "1"}, 1663 }, 1664 { 1665 whenURL: "/users/1/files/1", 1666 expectRoute: "/users/:uid/files/:fid", 1667 expectParam: map[string]string{ 1668 "uid": "1", 1669 "fid": "1", 1670 }, 1671 }, 1672 } 1673 for _, tc := range testCases { 1674 t.Run(tc.whenURL, func(t *testing.T) { 1675 ctx := e.NewContext() 1676 ctx.Request.SetRequestURI(tc.whenURL) 1677 ctx.Request.Header.SetMethod(consts.MethodGet) 1678 e.ServeHTTP(context.Background(), ctx) 1679 1680 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1681 for param, expectedValue := range tc.expectParam { 1682 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1683 } 1684 checkUnusedParamValues(t, ctx, tc.expectParam) 1685 }) 1686 } 1687 } 1688 1689 func TestRouterStaticDynamicConflict(t *testing.T) { 1690 e := NewEngine(config.NewOptions(nil)) 1691 1692 e.addRoute(consts.MethodGet, "/dictionary/skills", handlerHelper("/dictionary/skills", "a", 1)) 1693 e.addRoute(consts.MethodGet, "/dictionary/:name", handlerHelper("/dictionary/:name", "b", 2)) 1694 e.addRoute(consts.MethodGet, "/users/new", handlerHelper("/users/new", "d", 4)) 1695 e.addRoute(consts.MethodGet, "/users/:name", handlerHelper("/users/:name", "e", 5)) 1696 e.addRoute(consts.MethodGet, "/server", handlerHelper("/server", "c", 3)) 1697 e.addRoute(consts.MethodGet, "/", handlerHelper("/", "f", 6)) 1698 1699 testCases := []struct { 1700 whenMethod string 1701 whenURL string 1702 expectRoute interface{} 1703 expectParam map[string]string 1704 expectError error 1705 }{ 1706 { 1707 whenURL: "/dictionary/skills", 1708 expectRoute: "/dictionary/skills", 1709 expectParam: map[string]string{"x": ""}, 1710 }, 1711 { 1712 whenURL: "/dictionary/skillsnot", 1713 expectRoute: "/dictionary/:name", 1714 expectParam: map[string]string{"name": "skillsnot"}, 1715 }, 1716 { 1717 whenURL: "/dictionary/type", 1718 expectRoute: "/dictionary/:name", 1719 expectParam: map[string]string{"name": "type"}, 1720 }, 1721 { 1722 whenURL: "/server", 1723 expectRoute: "/server", 1724 }, 1725 { 1726 whenURL: "/users/new", 1727 expectRoute: "/users/new", 1728 }, 1729 { 1730 whenURL: "/users/new2", 1731 expectRoute: "/users/:name", 1732 expectParam: map[string]string{"name": "new2"}, 1733 }, 1734 { 1735 whenURL: "/", 1736 expectRoute: "/", 1737 }, 1738 } 1739 for _, tc := range testCases { 1740 t.Run(tc.whenURL, func(t *testing.T) { 1741 ctx := e.NewContext() 1742 ctx.Request.SetRequestURI(tc.whenURL) 1743 ctx.Request.Header.SetMethod(consts.MethodGet) 1744 e.ServeHTTP(context.Background(), ctx) 1745 1746 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1747 for param, expectedValue := range tc.expectParam { 1748 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1749 } 1750 checkUnusedParamValues(t, ctx, tc.expectParam) 1751 }) 1752 } 1753 } 1754 1755 func TestRouterParamBacktraceNotFound(t *testing.T) { 1756 e := NewEngine(config.NewOptions(nil)) 1757 1758 // Add 1759 e.addRoute(consts.MethodGet, "/:param1", handlerFunc("/:param1")) 1760 e.addRoute(consts.MethodGet, "/:param1/foo", handlerFunc("/:param1/foo")) 1761 e.addRoute(consts.MethodGet, "/:param1/bar", handlerFunc("/:param1/bar")) 1762 e.addRoute(consts.MethodGet, "/:param1/bar/:param2", handlerFunc("/:param1/bar/:param2")) 1763 1764 testCases := []struct { 1765 name string 1766 whenMethod string 1767 whenURL string 1768 expectRoute interface{} 1769 expectParam map[string]string 1770 expectError error 1771 }{ 1772 { 1773 name: "route /a to /:param1", 1774 whenURL: "/a", 1775 expectRoute: "/:param1", 1776 expectParam: map[string]string{"param1": "a"}, 1777 }, 1778 { 1779 name: "route /a/foo to /:param1/foo", 1780 whenURL: "/a/foo", 1781 expectRoute: "/:param1/foo", 1782 expectParam: map[string]string{"param1": "a"}, 1783 }, 1784 { 1785 name: "route /a/bar to /:param1/bar", 1786 whenURL: "/a/bar", 1787 expectRoute: "/:param1/bar", 1788 expectParam: map[string]string{"param1": "a"}, 1789 }, 1790 { 1791 name: "route /a/bar/b to /:param1/bar/:param2", 1792 whenURL: "/a/bar/b", 1793 expectRoute: "/:param1/bar/:param2", 1794 expectParam: map[string]string{ 1795 "param1": "a", 1796 "param2": "b", 1797 }, 1798 }, 1799 { 1800 name: "route /a/bbbbb should return 404", 1801 whenURL: "/a/bbbbb", 1802 expectRoute: nil, 1803 }, 1804 } 1805 for _, tc := range testCases { 1806 t.Run(tc.name, func(t *testing.T) { 1807 ctx := e.NewContext() 1808 ctx.Request.SetRequestURI(tc.whenURL) 1809 ctx.Request.Header.SetMethod(consts.MethodGet) 1810 e.ServeHTTP(context.Background(), ctx) 1811 1812 assert.DeepEqual(t, tc.expectRoute, getHelper(ctx, "path")) 1813 for param, expectedValue := range tc.expectParam { 1814 assert.DeepEqual(t, expectedValue, ctx.Param(param)) 1815 } 1816 checkUnusedParamValues(t, ctx, tc.expectParam) 1817 }) 1818 } 1819 }