github.com/cloudwego/hertz@v0.9.3/pkg/route/tree_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 * 16 * Copyright 2013 Julien Schmidt. All rights reserved. 17 * Use of this source code is governed by a BSD-style license that can be found 18 * at https://github.com/julienschmidt/httprouter/blob/master/LICENSE 19 * 20 * This file may have been modified by CloudWeGo authors. All CloudWeGo 21 * Modifications are Copyright 2022 CloudWeGo Authors. 22 */ 23 24 package route 25 26 import ( 27 "context" 28 "fmt" 29 "strings" 30 "testing" 31 32 "github.com/cloudwego/hertz/pkg/app" 33 "github.com/cloudwego/hertz/pkg/route/param" 34 ) 35 36 // Used as a workaround since we can't compare functions or their addresses 37 var fakeHandlerValue string 38 39 func fakeHandler(val string) app.HandlersChain { 40 return app.HandlersChain{func(c context.Context, ctx *app.RequestContext) { 41 fakeHandlerValue = val 42 }} 43 } 44 45 type testRequests []struct { 46 path string 47 nilHandler bool 48 route string 49 ps param.Params 50 } 51 52 func getParams() *param.Params { 53 ps := make(param.Params, 0, 20) 54 return &ps 55 } 56 57 func checkRequests(t *testing.T, tree *router, requests testRequests, unescapes ...bool) { 58 unescape := false 59 if len(unescapes) >= 1 { 60 unescape = unescapes[0] 61 } 62 63 for _, request := range requests { 64 params := getParams() 65 value := tree.find(request.path, params, unescape) 66 67 if value.handlers == nil { 68 if !request.nilHandler { 69 t.Errorf("handle mismatch for route '%s': Expected non-nil handle", request.path) 70 } 71 } else if request.nilHandler { 72 t.Errorf("handle mismatch for route '%s': Expected nil handle", request.path) 73 } else { 74 value.handlers[0](context.Background(), nil) 75 if fakeHandlerValue != request.route { 76 t.Errorf("handle mismatch for route '%s': Wrong handle (%s != %s)", request.path, fakeHandlerValue, request.route) 77 } 78 } 79 for _, item := range request.ps { 80 if item.Value != (*params).ByName(item.Key) { 81 t.Errorf("mismatch params. path: %s, key: %s, expected value: %s, actual value: %s", request.path, item.Key, item.Value, (*params).ByName(item.Key)) 82 } 83 } 84 } 85 } 86 87 func TestCountParams(t *testing.T) { 88 if countParams("/path/:param1/static/*catch-all") != 2 { 89 t.Fail() 90 } 91 if countParams(strings.Repeat("/:param", 256)) != 256 { 92 t.Fail() 93 } 94 } 95 96 func TestEmptyPath(t *testing.T) { 97 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 98 99 routes := [...]string{ 100 "", 101 "user", 102 ":user", 103 "*user", 104 } 105 for _, route := range routes { 106 recv := catchPanic(func() { 107 tree.addRoute(route, nil) 108 }) 109 if recv == nil { 110 t.Fatalf("no panic while inserting route with empty path '%s", route) 111 } 112 } 113 } 114 115 func TestTreeAddAndGet(t *testing.T) { 116 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 117 118 routes := [...]string{ 119 "/hi", 120 "/contact", 121 "/co", 122 "/c", 123 "/a", 124 "/ab", 125 "/doc/", 126 "/doc/go_faq.html", 127 "/doc/go1.html", 128 "/α", 129 "/β", 130 } 131 for _, route := range routes { 132 tree.addRoute(route, fakeHandler(route)) 133 } 134 135 checkRequests(t, tree, testRequests{ 136 {"", true, "", nil}, 137 {"a", true, "", nil}, 138 {"/a", false, "/a", nil}, 139 {"/", true, "", nil}, 140 {"/hi", false, "/hi", nil}, 141 {"/contact", false, "/contact", nil}, 142 {"/co", false, "/co", nil}, 143 {"/con", true, "", nil}, // key mismatch 144 {"/cona", true, "", nil}, // key mismatch 145 {"/no", true, "", nil}, // no matching child 146 {"/ab", false, "/ab", nil}, 147 {"/α", false, "/α", nil}, 148 {"/β", false, "/β", nil}, 149 }) 150 } 151 152 func TestTreeWildcard(t *testing.T) { 153 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 154 155 routes := [...]string{ 156 "/", 157 "/cmd/:tool/:sub", 158 "/cmd/:tool/", 159 "/cmd/xxx/", 160 "/src/*filepath", 161 "/search/", 162 "/search/:query", 163 "/user_:name", 164 "/user_:name/about", 165 "/files/:dir/*filepath", 166 "/doc/", 167 "/doc/go_faq.html", 168 "/doc/go1.html", 169 "/info/:user/public", 170 "/info/:user/project/:project", 171 "/a/b/:c", 172 "/a/:b/c/d", 173 "/a/*b", 174 } 175 for _, route := range routes { 176 tree.addRoute(route, fakeHandler(route)) 177 } 178 179 checkRequests(t, tree, testRequests{ 180 {"/", false, "/", nil}, 181 {"/cmd/test/", false, "/cmd/:tool/", param.Params{param.Param{Key: "tool", Value: "test"}}}, 182 {"/cmd/test", true, "", nil}, 183 {"/cmd/test/3", false, "/cmd/:tool/:sub", param.Params{param.Param{Key: "tool", Value: "test"}, param.Param{Key: "sub", Value: "3"}}}, 184 {"/src/", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: ""}}}, 185 {"/src/some/file.png", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: "some/file.png"}}}, 186 {"/search/", false, "/search/", nil}, 187 {"/search/someth!ng+in+ünìcodé", false, "/search/:query", param.Params{param.Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}}, 188 {"/search/someth!ng+in+ünìcodé/", true, "", nil}, 189 {"/user_gopher", false, "/user_:name", param.Params{param.Param{Key: "name", Value: "gopher"}}}, 190 {"/user_gopher/about", false, "/user_:name/about", param.Params{param.Param{Key: "name", Value: "gopher"}}}, 191 {"/files/js/inc/framework.js", false, "/files/:dir/*filepath", param.Params{param.Param{Key: "dir", Value: "js"}, param.Param{Key: "filepath", Value: "inc/framework.js"}}}, 192 {"/info/gordon/public", false, "/info/:user/public", param.Params{param.Param{Key: "user", Value: "gordon"}}}, 193 {"/info/gordon/project/go", false, "/info/:user/project/:project", param.Params{param.Param{Key: "user", Value: "gordon"}, param.Param{Key: "project", Value: "go"}}}, 194 {"/a/b/c", false, "/a/b/:c", param.Params{param.Param{Key: "c", Value: "c"}}}, 195 {"/a/b/c/d", false, "/a/:b/c/d", param.Params{param.Param{Key: "b", Value: "b"}}}, 196 {"/a/b", false, "/a/*b", param.Params{param.Param{Key: "b", Value: "b"}}}, 197 }) 198 } 199 200 func TestUnescapeParameters(t *testing.T) { 201 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 202 203 routes := [...]string{ 204 "/", 205 "/cmd/:tool/:sub", 206 "/cmd/:tool/", 207 "/src/*filepath", 208 "/search/:query", 209 "/files/:dir/*filepath", 210 "/info/:user/project/:project", 211 "/info/:user", 212 } 213 for _, route := range routes { 214 tree.addRoute(route, fakeHandler(route)) 215 } 216 217 unescape := true 218 checkRequests(t, tree, testRequests{ 219 {"/", false, "/", nil}, 220 {"/cmd/test/", false, "/cmd/:tool/", param.Params{param.Param{Key: "tool", Value: "test"}}}, 221 {"/cmd/test", true, "", nil}, 222 {"/src/some/file.png", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: "some/file.png"}}}, 223 {"/src/some/file+test.png", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: "some/file test.png"}}}, 224 {"/src/some/file++++%%%%test.png", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: "some/file++++%%%%test.png"}}}, 225 {"/src/some/file%2Ftest.png", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: "some/file/test.png"}}}, 226 {"/search/someth!ng+in+ünìcodé", false, "/search/:query", param.Params{param.Param{Key: "query", Value: "someth!ng in ünìcodé"}}}, 227 {"/info/gordon/project/go", false, "/info/:user/project/:project", param.Params{param.Param{Key: "user", Value: "gordon"}, param.Param{Key: "project", Value: "go"}}}, 228 {"/info/slash%2Fgordon", false, "/info/:user", param.Params{param.Param{Key: "user", Value: "slash/gordon"}}}, 229 {"/info/slash%2Fgordon/project/Project%20%231", false, "/info/:user/project/:project", param.Params{param.Param{Key: "user", Value: "slash/gordon"}, param.Param{Key: "project", Value: "Project #1"}}}, 230 {"/info/slash%%%%", false, "/info/:user", param.Params{param.Param{Key: "user", Value: "slash%%%%"}}}, 231 {"/info/slash%%%%2Fgordon/project/Project%%%%20%231", false, "/info/:user/project/:project", param.Params{param.Param{Key: "user", Value: "slash%%%%2Fgordon"}, param.Param{Key: "project", Value: "Project%%%%20%231"}}}, 232 }, unescape) 233 } 234 235 func catchPanic(testFunc func()) (recv interface{}) { 236 defer func() { 237 recv = recover() 238 }() 239 240 testFunc() 241 return 242 } 243 244 type testRoute struct { 245 path string 246 conflict bool 247 } 248 249 func testRoutes(t *testing.T, routes []testRoute) { 250 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 251 252 for _, route := range routes { 253 recv := catchPanic(func() { 254 tree.addRoute(route.path, []app.HandlerFunc{ 255 func(c context.Context, ctx *app.RequestContext) { 256 fmt.Println("test") 257 }, 258 }) 259 }) 260 261 if route.conflict { 262 if recv == nil { 263 t.Errorf("no panic for conflicting route '%s'", route.path) 264 } 265 } else if recv != nil { 266 t.Errorf("unexpected panic for route '%s': %v", route.path, recv) 267 } 268 } 269 } 270 271 func TestTreeWildcardConflict(t *testing.T) { 272 routes := []testRoute{ 273 {"/cmd/vet", false}, 274 {"/cmd/:tool/:sub", false}, 275 {"/src/*filepath", false}, 276 {"/src/*filepathx", true}, 277 {"/src/", false}, 278 {"/src1/", false}, 279 {"/src1/*filepath", false}, 280 {"/src2*filepath", true}, 281 {"/search/:query", false}, 282 {"/search/invalid", false}, 283 {"/user_:name", false}, 284 {"/user_x", false}, 285 {"/user_:name", true}, 286 {"/id:id", false}, 287 {"/id/:id", false}, 288 } 289 testRoutes(t, routes) 290 } 291 292 func TestTreeChildConflict(t *testing.T) { 293 routes := []testRoute{ 294 {"/cmd/vet", false}, 295 {"/cmd/:tool/:sub", false}, 296 {"/src/AUTHORS", false}, 297 {"/src/*filepath", false}, 298 {"/user_x", false}, 299 {"/user_:name", false}, 300 {"/id/:id", false}, 301 {"/id:id", false}, 302 {"/:id", false}, 303 {"/*filepath", false}, 304 } 305 testRoutes(t, routes) 306 } 307 308 func TestTreeDuplicatePath(t *testing.T) { 309 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 310 311 routes := [...]string{ 312 "/", 313 "/doc/", 314 "/src/*filepath", 315 "/search/:query", 316 "/user_:name", 317 } 318 for _, route := range routes { 319 recv := catchPanic(func() { 320 tree.addRoute(route, fakeHandler(route)) 321 }) 322 if recv != nil { 323 t.Fatalf("panic inserting route '%s': %v", route, recv) 324 } 325 326 // Add again 327 recv = catchPanic(func() { 328 tree.addRoute(route, fakeHandler(route)) 329 }) 330 if recv == nil { 331 t.Fatalf("no panic while inserting duplicate route '%s", route) 332 } 333 } 334 335 checkRequests(t, tree, testRequests{ 336 {"/", false, "/", nil}, 337 {"/doc/", false, "/doc/", nil}, 338 {"/src/some/file.png", false, "/src/*filepath", param.Params{param.Param{Key: "filepath", Value: "some/file.png"}}}, 339 {"/search/someth!ng+in+ünìcodé", false, "/search/:query", param.Params{param.Param{Key: "query", Value: "someth!ng+in+ünìcodé"}}}, 340 {"/user_gopher", false, "/user_:name", param.Params{param.Param{Key: "name", Value: "gopher"}}}, 341 }) 342 } 343 344 func TestEmptyWildcardName(t *testing.T) { 345 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 346 347 routes := [...]string{ 348 "/user:", 349 "/user:/", 350 "/cmd/:/", 351 "/src/*", 352 } 353 for _, route := range routes { 354 recv := catchPanic(func() { 355 tree.addRoute(route, nil) 356 }) 357 if recv == nil { 358 t.Fatalf("no panic while inserting route with empty wildcard name '%s", route) 359 } 360 } 361 } 362 363 func TestTreeCatchAllConflict(t *testing.T) { 364 routes := []testRoute{ 365 {"/src/*filepath/x", true}, 366 {"/src2/", false}, 367 {"/src2/*filepath/x", true}, 368 {"/src3/*filepath", false}, 369 {"/src3/*filepath/x", true}, 370 } 371 testRoutes(t, routes) 372 } 373 374 func TestTreeCatchMaxParams(t *testing.T) { 375 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 376 route := "/cmd/*filepath" 377 tree.addRoute(route, fakeHandler(route)) 378 } 379 380 func TestTreeDoubleWildcard(t *testing.T) { 381 const panicMsg = "only one wildcard per path segment is allowed" 382 383 routes := [...]string{ 384 "/:foo:bar", 385 "/:foo:bar/", 386 "/:foo*bar", 387 } 388 389 for _, route := range routes { 390 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 391 recv := catchPanic(func() { 392 tree.addRoute(route, nil) 393 }) 394 395 if rs, ok := recv.(string); !ok || !strings.HasPrefix(rs, panicMsg) { 396 t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsg, route, recv) 397 } 398 } 399 } 400 401 func TestTreeTrailingSlashRedirect2(t *testing.T) { 402 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 403 404 routes := [...]string{ 405 "/api/:version/seller/locales/get", 406 "/api/v:version/seller/permissions/get", 407 "/api/v:version/seller/university/entrance_knowledge_list/get", 408 } 409 for _, route := range routes { 410 recv := catchPanic(func() { 411 tree.addRoute(route, fakeHandler(route)) 412 }) 413 if recv != nil { 414 t.Fatalf("panic inserting route '%s': %v", route, recv) 415 } 416 } 417 v := make(param.Params, 0, 1) 418 419 tsrRoutes := [...]string{ 420 "/api/v:version/seller/permissions/get/", 421 "/api/version/seller/permissions/get/", 422 } 423 424 for _, route := range tsrRoutes { 425 value := tree.find(route, &v, false) 426 if value.handlers != nil { 427 t.Fatalf("non-nil handler for TSR route '%s", route) 428 } else if !value.tsr { 429 t.Errorf("expected TSR recommendation for route '%s'", route) 430 } 431 } 432 433 noTsrRoutes := [...]string{ 434 "/api/v:version/seller/permissions/get/a", 435 } 436 for _, route := range noTsrRoutes { 437 value := tree.find(route, &v, false) 438 if value.handlers != nil { 439 t.Fatalf("non-nil handler for No-TSR route '%s", route) 440 } else if value.tsr { 441 t.Errorf("expected no TSR recommendation for route '%s'", route) 442 } 443 } 444 } 445 446 func TestTreeTrailingSlashRedirect(t *testing.T) { 447 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 448 449 routes := [...]string{ 450 "/hi", 451 "/b/", 452 "/search/:query", 453 "/cmd/:tool/", 454 "/src/*filepath", 455 "/x", 456 "/x/y", 457 "/y/", 458 "/y/z", 459 "/0/:id", 460 "/0/:id/1", 461 "/1/:id/", 462 "/1/:id/2", 463 "/aa", 464 "/a/", 465 "/admin", 466 "/admin/:category", 467 "/admin/:category/:page", 468 "/doc", 469 "/doc/go_faq.html", 470 "/doc/go1.html", 471 "/no/a", 472 "/no/b", 473 "/api/hello/:name", 474 "/user/:name/*id", 475 "/resource", 476 "/r/*id", 477 "/book/biz/:name", 478 "/book/biz/abc", 479 "/book/biz/abc/bar", 480 "/book/:page/:name", 481 "/book/hello/:name/biz/", 482 } 483 for _, route := range routes { 484 recv := catchPanic(func() { 485 tree.addRoute(route, fakeHandler(route)) 486 }) 487 if recv != nil { 488 t.Fatalf("panic inserting route '%s': %v", route, recv) 489 } 490 } 491 492 tsrRoutes := [...]string{ 493 "/hi/", 494 "/b", 495 "/search/gopher/", 496 "/cmd/vet", 497 "/src", 498 "/x/", 499 "/y", 500 "/0/go/", 501 "/1/go", 502 "/a", 503 "/admin/", 504 "/admin/config/", 505 "/admin/config/permissions/", 506 "/doc/", 507 "/user/name", 508 "/r", 509 "/book/hello/a/biz", 510 "/book/biz/foo/", 511 "/book/biz/abc/bar/", 512 } 513 v := make(param.Params, 0, 10) 514 for _, route := range tsrRoutes { 515 value := tree.find(route, &v, false) 516 if value.handlers != nil { 517 t.Fatalf("non-nil handler for TSR route '%s", route) 518 } else if !value.tsr { 519 t.Errorf("expected TSR recommendation for route '%s'", route) 520 } 521 } 522 523 noTsrRoutes := [...]string{ 524 "/", 525 "/no", 526 "/no/", 527 "/_", 528 "/_/", 529 "/api/world/abc", 530 "/book", 531 "/book/", 532 "/book/hello/a/abc", 533 "/book/biz/abc/biz", 534 } 535 for _, route := range noTsrRoutes { 536 value := tree.find(route, &v, false) 537 if value.handlers != nil { 538 t.Fatalf("non-nil handler for No-TSR route '%s", route) 539 } else if value.tsr { 540 t.Errorf("expected no TSR recommendation for route '%s'", route) 541 } 542 } 543 } 544 545 func TestTreeRootTrailingSlashRedirect(t *testing.T) { 546 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 547 548 recv := catchPanic(func() { 549 tree.addRoute("/:test", fakeHandler("/:test")) 550 }) 551 if recv != nil { 552 t.Fatalf("panic inserting test route: %v", recv) 553 } 554 555 value := tree.find("/", nil, false) 556 if value.handlers != nil { 557 t.Fatalf("non-nil handler") 558 } else if value.tsr { 559 t.Errorf("expected no TSR recommendation") 560 } 561 } 562 563 func TestTreeFindCaseInsensitivePath(t *testing.T) { 564 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 565 566 longPath := "/l" + strings.Repeat("o", 128) + "ng" 567 lOngPath := "/l" + strings.Repeat("O", 128) + "ng/" 568 569 routes := [...]string{ 570 "/hi", 571 "/b/", 572 "/ABC/", 573 "/search/:query", 574 "/cmd/:tool/", 575 "/src/*filepath", 576 "/x", 577 "/x/y", 578 "/y/", 579 "/y/z", 580 "/0/:id", 581 "/0/:id/1", 582 "/1/:id/", 583 "/1/:id/2", 584 "/aa", 585 "/a/", 586 "/doc", 587 "/doc/go_faq.html", 588 "/doc/go1.html", 589 "/doc/go/away", 590 "/no/a", 591 "/no/b", 592 "/z/:id/2", 593 "/z/:id/:age", 594 "/x/:id/3/", 595 "/x/:id/3/4", 596 "/x/:id/:age/5", 597 longPath, 598 } 599 600 for _, route := range routes { 601 recv := catchPanic(func() { 602 tree.addRoute(route, fakeHandler(route)) 603 }) 604 if recv != nil { 605 t.Fatalf("panic inserting route '%s': %v", route, recv) 606 } 607 } 608 609 // Check out == in for all registered routes 610 // With fixTrailingSlash = true 611 for _, route := range routes { 612 out, found := tree.root.findCaseInsensitivePath(route, true) 613 if !found { 614 t.Errorf("Route '%s' not found!", route) 615 } else if string(out) != route { 616 t.Errorf("Wrong result for route '%s': %s", route, string(out)) 617 } 618 } 619 // With fixTrailingSlash = false 620 for _, route := range routes { 621 out, found := tree.root.findCaseInsensitivePath(route, false) 622 if !found { 623 t.Errorf("Route '%s' not found!", route) 624 } else if string(out) != route { 625 t.Errorf("Wrong result for route '%s': %s", route, string(out)) 626 } 627 } 628 629 tests := []struct { 630 in string 631 out string 632 found bool 633 slash bool 634 }{ 635 {"/HI", "/hi", true, false}, 636 {"/HI/", "/hi", true, true}, 637 {"/B", "/b/", true, true}, 638 {"/B/", "/b/", true, false}, 639 {"/abc", "/ABC/", true, true}, 640 {"/abc/", "/ABC/", true, false}, 641 {"/aBc", "/ABC/", true, true}, 642 {"/aBc/", "/ABC/", true, false}, 643 {"/abC", "/ABC/", true, true}, 644 {"/abC/", "/ABC/", true, false}, 645 {"/SEARCH/QUERY", "/search/QUERY", true, false}, 646 {"/SEARCH/QUERY/", "/search/QUERY", true, true}, 647 {"/CMD/TOOL/", "/cmd/TOOL/", true, false}, 648 {"/CMD/TOOL", "/cmd/TOOL/", true, true}, 649 {"/SRC/FILE/PATH", "/src/FILE/PATH", true, false}, 650 {"/x/Y", "/x/y", true, false}, 651 {"/x/Y/", "/x/y", true, true}, 652 {"/X/y", "/x/y", true, false}, 653 {"/X/y/", "/x/y", true, true}, 654 {"/X/Y", "/x/y", true, false}, 655 {"/X/Y/", "/x/y", true, true}, 656 {"/Y/", "/y/", true, false}, 657 {"/Y", "/y/", true, true}, 658 {"/Y/z", "/y/z", true, false}, 659 {"/Y/z/", "/y/z", true, true}, 660 {"/Y/Z", "/y/z", true, false}, 661 {"/Y/Z/", "/y/z", true, true}, 662 {"/y/Z", "/y/z", true, false}, 663 {"/y/Z/", "/y/z", true, true}, 664 {"/Aa", "/aa", true, false}, 665 {"/Aa/", "/aa", true, true}, 666 {"/AA", "/aa", true, false}, 667 {"/AA/", "/aa", true, true}, 668 {"/aA", "/aa", true, false}, 669 {"/aA/", "/aa", true, true}, 670 {"/A/", "/a/", true, false}, 671 {"/A", "/a/", true, true}, 672 {"/DOC", "/doc", true, false}, 673 {"/DOC/", "/doc", true, true}, 674 {"/NO", "", false, true}, 675 {"/DOC/GO", "", false, true}, 676 {"/Z/1/2", "/z/1/2", true, false}, 677 {"/Z/1/3", "/z/1/3", true, false}, 678 {"/Z/1/2/", "/z/1/2", true, true}, 679 {"/Z/1/3/", "/z/1/3", true, true}, 680 {"/X/1/3", "/x/1/3/", true, true}, 681 {"/X/1/3/5", "/x/1/3/5", true, false}, 682 {lOngPath, longPath, true, true}, 683 } 684 // With fixTrailingSlash = true 685 for _, test := range tests { 686 out, found := tree.root.findCaseInsensitivePath(test.in, true) 687 if found != test.found || (found && (string(out) != test.out)) { 688 t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t", 689 test.in, string(out), found, test.out, test.found) 690 return 691 } 692 } 693 // With fixTrailingSlash = false 694 for _, test := range tests { 695 out, found := tree.root.findCaseInsensitivePath(test.in, false) 696 if test.slash { 697 if found { // test needs a trailingSlash fix. It must not be found! 698 t.Errorf("Found without fixTrailingSlash: %s; got %s", test.in, string(out)) 699 } 700 } else { 701 if found != test.found || (found && (string(out) != test.out)) { 702 t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t", 703 test.in, string(out), found, test.out, test.found) 704 return 705 } 706 } 707 } 708 } 709 710 func TestTreeParamNotOptimize(t *testing.T) { 711 tree := &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 712 routes := [...]string{ 713 "/:parama/start", 714 "/:paramb", 715 } 716 for _, route := range routes { 717 tree.addRoute(route, fakeHandler(route)) 718 } 719 checkRequests(t, tree, testRequests{ 720 {"/1", false, "/:paramb", param.Params{param.Param{Key: "paramb", Value: "1"}}}, 721 {"/1/start", false, "/:parama/start", param.Params{param.Param{Key: "parama", Value: "1"}}}, 722 }) 723 724 // other sequence 725 tree = &router{method: "GET", root: &node{}, hasTsrHandler: make(map[string]bool)} 726 routes = [...]string{ 727 "/:paramb", 728 "/:parama/start", 729 } 730 for _, route := range routes { 731 tree.addRoute(route, fakeHandler(route)) 732 } 733 checkRequests(t, tree, testRequests{ 734 {"/1/start", false, "/:parama/start", param.Params{param.Param{Key: "parama", Value: "1"}}}, 735 {"/1", false, "/:paramb", param.Params{param.Param{Key: "paramb", Value: "1"}}}, 736 }) 737 }