github.com/System-Glitch/goyave/v3@v3.6.1-0.20210226143142-ac2fe42ee80e/router_test.go (about) 1 package goyave 2 3 import ( 4 "io/ioutil" 5 "net/http" 6 "net/http/httptest" 7 "strconv" 8 "testing" 9 10 "github.com/System-Glitch/goyave/v3/config" 11 "github.com/System-Glitch/goyave/v3/cors" 12 ) 13 14 type RouterTestSuite struct { 15 TestSuite 16 middlewareExecuted bool 17 } 18 19 func createRouterTestRequest(url string) (*Request, *Response) { 20 rawRequest := httptest.NewRequest("GET", url, nil) 21 request := &Request{ 22 httpRequest: rawRequest, 23 Params: map[string]string{"resource": url}, 24 } 25 response := newResponse(httptest.NewRecorder(), nil) 26 return request, response 27 } 28 29 func (suite *RouterTestSuite) routerTestMiddleware(handler Handler) Handler { 30 return func(response *Response, request *Request) { 31 suite.middlewareExecuted = true 32 handler(response, request) 33 } 34 } 35 36 func (suite *RouterTestSuite) createOrderedTestMiddleware(result *string, str string) Middleware { 37 return func(next Handler) Handler { 38 return func(response *Response, r *Request) { 39 *result += str 40 next(response, r) 41 } 42 } 43 } 44 45 func (suite *RouterTestSuite) TearDownTest() { 46 regexCache = nil 47 } 48 49 func (suite *RouterTestSuite) TestNewRouter() { 50 router := newRouter() 51 suite.NotNil(router) 52 suite.Equal(3, len(router.middleware)) 53 } 54 55 func (suite *RouterTestSuite) TestRouterRegisterRoute() { 56 router := newRouter() 57 route := router.Route("GET", "/uri", func(resp *Response, r *Request) {}) 58 suite.Contains(router.routes, route) 59 suite.Equal(router, route.parent) 60 61 route = router.Route("GET", "/", func(resp *Response, r *Request) {}) 62 suite.Equal("/", route.uri) 63 suite.Equal(router, route.parent) 64 65 route = router.Route("GET|POST", "/", func(resp *Response, r *Request) {}) 66 suite.Equal([]string{"GET", "POST", "HEAD"}, route.methods) 67 suite.Equal(router, route.parent) 68 69 subrouter := router.Subrouter("/sub") 70 route = subrouter.Route("GET", "/", func(resp *Response, r *Request) {}) 71 suite.Equal("", route.uri) 72 } 73 74 func (suite *RouterTestSuite) TestRouterMiddleware() { 75 router := newRouter() 76 router.Middleware(suite.routerTestMiddleware) 77 suite.Equal(4, len(router.middleware)) 78 } 79 80 func (suite *RouterTestSuite) TestSubRouter() { 81 router := newRouter() 82 router.Middleware(suite.routerTestMiddleware) 83 suite.Equal(4, len(router.middleware)) 84 85 subrouter := router.Subrouter("/sub") 86 suite.Contains(router.subrouters, subrouter) 87 suite.Equal(0, len(subrouter.middleware)) // Middleware inherited, not copied 88 suite.Equal(len(router.statusHandlers), len(subrouter.statusHandlers)) 89 90 router = newRouter() 91 subrouter = router.Subrouter("/") 92 suite.Equal("", subrouter.prefix) 93 } 94 95 func (suite *RouterTestSuite) TestCleanStaticPath() { 96 suite.Equal("config/index.html", cleanStaticPath("config", "index.html")) 97 suite.Equal("config/index.html", cleanStaticPath("config", "")) 98 suite.Equal("config/defaults.json", cleanStaticPath("config", "defaults.json")) 99 suite.Equal("resources/lang/en-US/locale.json", cleanStaticPath("resources", "lang/en-US/locale.json")) 100 suite.Equal("resources/lang/en-US/locale.json", cleanStaticPath("resources", "/lang/en-US/locale.json")) 101 suite.Equal("resources/img/logo/index.html", cleanStaticPath("resources", "img/logo")) 102 suite.Equal("resources/img/logo/index.html", cleanStaticPath("resources", "img/logo/")) 103 suite.Equal("resources/img/index.html", cleanStaticPath("resources", "img")) 104 suite.Equal("resources/img/index.html", cleanStaticPath("resources", "img/")) 105 } 106 107 func (suite *RouterTestSuite) TestStaticHandler() { 108 request, response := createRouterTestRequest("/config.test.json") 109 handler := staticHandler("config", false) 110 handler(response, request) 111 result := response.responseWriter.(*httptest.ResponseRecorder).Result() 112 suite.Equal(200, result.StatusCode) 113 suite.Equal("application/json", result.Header.Get("Content-Type")) 114 suite.Equal("inline", result.Header.Get("Content-Disposition")) 115 116 body, err := ioutil.ReadAll(result.Body) 117 if err != nil { 118 panic(err) 119 } 120 result.Body.Close() 121 122 suite.True(len(body) > 0) 123 124 request, response = createRouterTestRequest("/doesn'texist") 125 handler = staticHandler("config", false) 126 handler(response, request) 127 result = response.responseWriter.(*httptest.ResponseRecorder).Result() 128 suite.Equal(200, result.StatusCode) // Not written yet 129 suite.Equal(404, response.GetStatus()) 130 131 body, err = ioutil.ReadAll(result.Body) 132 if err != nil { 133 panic(err) 134 } 135 result.Body.Close() 136 137 suite.Equal(0, len(body)) 138 139 request, response = createRouterTestRequest("/config.test.json") 140 handler = staticHandler("config", true) 141 handler(response, request) 142 result = response.responseWriter.(*httptest.ResponseRecorder).Result() 143 suite.Equal(200, result.StatusCode) 144 suite.Equal("application/json", result.Header.Get("Content-Type")) 145 suite.Equal("attachment; filename=\"config.test.json\"", result.Header.Get("Content-Disposition")) 146 147 body, err = ioutil.ReadAll(result.Body) 148 if err != nil { 149 panic(err) 150 } 151 result.Body.Close() 152 153 suite.True(len(body) > 0) 154 } 155 156 func (suite *RouterTestSuite) TestRequestHandler() { 157 rawRequest := httptest.NewRequest("GET", "/uri", nil) 158 writer := httptest.NewRecorder() 159 router := newRouter() 160 161 route := &Route{} 162 var tmp *Route 163 route.handler = func(response *Response, request *Request) { 164 suite.NotNil(request.Extra) 165 tmp = request.Route() 166 response.String(200, "Hello world") 167 } 168 match := &routeMatch{route: route} 169 router.requestHandler(match, writer, rawRequest) 170 suite.Equal(route, tmp) 171 172 result := writer.Result() 173 body, err := ioutil.ReadAll(result.Body) 174 if err != nil { 175 panic(err) 176 } 177 result.Body.Close() 178 suite.Equal(200, result.StatusCode) 179 suite.Equal("Hello world", string(body)) 180 181 writer = httptest.NewRecorder() 182 router = newRouter() 183 router.Middleware(suite.routerTestMiddleware) 184 185 match = &routeMatch{ 186 route: &Route{ 187 handler: func(response *Response, request *Request) {}, 188 parent: router, 189 }, 190 } 191 router.requestHandler(match, writer, rawRequest) 192 193 result = writer.Result() 194 body, err = ioutil.ReadAll(result.Body) 195 if err != nil { 196 panic(err) 197 } 198 result.Body.Close() 199 suite.Equal(204, result.StatusCode) 200 suite.Equal(0, len(body)) 201 suite.True(suite.middlewareExecuted) 202 suite.middlewareExecuted = false 203 204 writer = httptest.NewRecorder() 205 router = newRouter() 206 match = &routeMatch{ 207 route: &Route{ 208 handler: func(response *Response, request *Request) { 209 response.Status(http.StatusNotFound) 210 }, 211 }, 212 } 213 router.requestHandler(match, writer, rawRequest) 214 215 result = writer.Result() 216 body, err = ioutil.ReadAll(result.Body) 217 if err != nil { 218 panic(err) 219 } 220 result.Body.Close() 221 suite.Equal(404, result.StatusCode) 222 suite.Equal("{\"error\":\""+http.StatusText(404)+"\"}\n", string(body)) 223 } 224 225 func (suite *RouterTestSuite) TestCORS() { 226 router := newRouter() 227 suite.Nil(router.corsOptions) 228 229 router.CORS(cors.Default()) 230 231 suite.NotNil(router.corsOptions) 232 suite.True(router.hasCORSMiddleware) 233 234 route := router.registerRoute("GET", "/cors", helloHandler) 235 suite.Equal([]string{"GET", "OPTIONS", "HEAD"}, route.methods) 236 237 match := routeMatch{currentPath: "/cors"} 238 suite.True(route.match(httptest.NewRequest("OPTIONS", "/cors", nil), &match)) 239 match = routeMatch{currentPath: "/cors"} 240 suite.True(route.match(httptest.NewRequest("GET", "/cors", nil), &match)) 241 242 writer := httptest.NewRecorder() 243 router.Middleware(func(handler Handler) Handler { 244 return func(response *Response, request *Request) { 245 suite.NotNil(request.corsOptions) 246 suite.NotNil(request.CORSOptions()) 247 handler(response, request) 248 } 249 }) 250 rawRequest := httptest.NewRequest("GET", "/cors", nil) 251 252 match = routeMatch{ 253 route: &Route{ 254 handler: func(response *Response, request *Request) {}, 255 }, 256 } 257 router.requestHandler(&match, writer, rawRequest) 258 } 259 260 func (suite *RouterTestSuite) TestPanicStatusHandler() { 261 request, response := createRouterTestRequest("/uri") 262 response.err = "random error" 263 PanicStatusHandler(response, request) 264 result := response.responseWriter.(*httptest.ResponseRecorder).Result() 265 suite.Equal(500, result.StatusCode) 266 result.Body.Close() 267 } 268 269 func (suite *RouterTestSuite) TestErrorStatusHandler() { 270 request, response := createRouterTestRequest("/uri") 271 response.Status(404) 272 ErrorStatusHandler(response, request) 273 result := response.responseWriter.(*httptest.ResponseRecorder).Result() 274 suite.Equal(404, result.StatusCode) 275 suite.Equal("application/json; charset=utf-8", result.Header.Get("Content-Type")) 276 277 body, err := ioutil.ReadAll(result.Body) 278 if err != nil { 279 panic(err) 280 } 281 result.Body.Close() 282 suite.Equal("{\"error\":\""+http.StatusText(404)+"\"}\n", string(body)) 283 } 284 285 func (suite *RouterTestSuite) TestStatusHandlers() { 286 rawRequest := httptest.NewRequest("GET", "/uri", nil) 287 writer := httptest.NewRecorder() 288 router := newRouter() 289 router.StatusHandler(func(response *Response, request *Request) { 290 response.String(http.StatusInternalServerError, "An unexpected panic occurred.") 291 }, http.StatusInternalServerError) 292 293 match := &routeMatch{ 294 route: &Route{ 295 handler: func(response *Response, request *Request) { 296 panic("Panic") 297 }, 298 parent: router, 299 }, 300 } 301 router.requestHandler(match, writer, rawRequest) 302 303 result := writer.Result() 304 body, err := ioutil.ReadAll(result.Body) 305 if err != nil { 306 panic(err) 307 } 308 result.Body.Close() 309 suite.Equal(500, result.StatusCode) 310 suite.Equal("An unexpected panic occurred.", string(body)) 311 312 // On subrouters 313 subrouter := router.Subrouter("/sub") 314 writer = httptest.NewRecorder() 315 316 subrouter.requestHandler(match, writer, rawRequest) 317 318 result = writer.Result() 319 body, err = ioutil.ReadAll(result.Body) 320 if err != nil { 321 panic(err) 322 } 323 result.Body.Close() 324 suite.Equal(500, result.StatusCode) 325 suite.Equal("An unexpected panic occurred.", string(body)) 326 327 // Multiple statuses 328 writer = httptest.NewRecorder() 329 subrouter.StatusHandler(func(response *Response, request *Request) { 330 response.String(response.GetStatus(), http.StatusText(response.GetStatus())) 331 }, 400, 404) 332 333 match = &routeMatch{ 334 route: &Route{ 335 handler: func(response *Response, request *Request) { 336 response.Status(400) 337 }, 338 }, 339 } 340 subrouter.requestHandler(match, writer, rawRequest) 341 342 result = writer.Result() 343 body, err = ioutil.ReadAll(result.Body) 344 if err != nil { 345 panic(err) 346 } 347 result.Body.Close() 348 suite.Equal(400, result.StatusCode) 349 suite.Equal(http.StatusText(400), string(body)) 350 351 writer = httptest.NewRecorder() 352 353 match = &routeMatch{ 354 route: &Route{ 355 handler: func(response *Response, request *Request) { 356 response.Status(404) 357 }, 358 }, 359 } 360 subrouter.requestHandler(match, writer, rawRequest) 361 362 result = writer.Result() 363 body, err = ioutil.ReadAll(result.Body) 364 if err != nil { 365 panic(err) 366 } 367 result.Body.Close() 368 suite.Equal(404, result.StatusCode) 369 suite.Equal(http.StatusText(404), string(body)) 370 } 371 372 func (suite *RouterTestSuite) TestRouteNoMatch() { 373 rawRequest := httptest.NewRequest("GET", "/uri", nil) 374 writer := httptest.NewRecorder() 375 router := newRouter() 376 377 match := &routeMatch{route: notFoundRoute} 378 router.requestHandler(match, writer, rawRequest) 379 result := writer.Result() 380 suite.Equal(http.StatusNotFound, result.StatusCode) 381 result.Body.Close() 382 383 writer = httptest.NewRecorder() 384 match = &routeMatch{route: methodNotAllowedRoute} 385 router.requestHandler(match, writer, rawRequest) 386 result = writer.Result() 387 suite.Equal(http.StatusMethodNotAllowed, result.StatusCode) 388 result.Body.Close() 389 } 390 391 func (suite *RouterTestSuite) TestNamedRoutes() { 392 r := newRouter() 393 route := r.Route("GET", "/uri", func(resp *Response, r *Request) {}) 394 route.Name("get-uri") 395 suite.Equal(route, r.namedRoutes["get-uri"]) 396 suite.Equal(route, r.GetRoute("get-uri")) 397 398 subrouter := r.Subrouter("/sub") 399 suite.Equal(route, subrouter.GetRoute("get-uri")) 400 401 route2 := r.Route("GET", "/other-route", func(resp *Response, r *Request) {}) 402 suite.Panics(func() { 403 route2.Name("get-uri") 404 }) 405 suite.Empty(route2.GetName()) 406 407 // Global router 408 router = r 409 suite.Equal(route, GetRoute("get-uri")) 410 router = nil 411 } 412 413 func (suite *RouterTestSuite) TestMiddleware() { 414 // Test the middleware execution order 415 result := "" 416 middleware := make([]Middleware, 0, 4) 417 for i := 0; i < 4; i++ { 418 middleware = append(middleware, suite.createOrderedTestMiddleware(&result, strconv.Itoa(i+1))) 419 } 420 router := newRouter() 421 router.Middleware(middleware[0]) 422 423 subrouter := router.Subrouter("/") 424 subrouter.Middleware(middleware[1]) 425 426 handler := func(response *Response, r *Request) { 427 result += "5" 428 } 429 route := subrouter.Route("GET", "/hello", handler).Middleware(middleware[2], middleware[3]) 430 431 rawRequest := httptest.NewRequest("GET", "/hello", nil) 432 match := routeMatch{ 433 route: route, 434 currentPath: rawRequest.URL.Path, 435 } 436 router.requestHandler(&match, httptest.NewRecorder(), rawRequest) 437 438 suite.Equal("12345", result) 439 } 440 441 func (suite *RouterTestSuite) TestCoreMiddleware() { 442 // Ensure core middleware is executed on Not Found and Method Not Allowed 443 router := newRouter() 444 445 match := &routeMatch{ 446 route: newRoute(func(response *Response, request *Request) { 447 panic("Test panic") // Test recover middleware is executed 448 }), 449 } 450 451 writer := httptest.NewRecorder() 452 prev := config.Get("app.debug") 453 config.Set("app.debug", false) 454 router.requestHandler(match, writer, httptest.NewRequest("GET", "/uri", nil)) 455 config.Set("app.debug", prev) 456 457 result := writer.Result() 458 body, err := ioutil.ReadAll(result.Body) 459 if err != nil { 460 panic(err) 461 } 462 result.Body.Close() 463 suite.Equal(500, result.StatusCode) 464 suite.Equal("{\"error\":\"Internal Server Error\"}\n", string(body)) 465 466 lang := "" 467 param := "" 468 match = &routeMatch{ 469 route: newRoute(func(response *Response, request *Request) { 470 // Test lang and parse request 471 lang = request.Lang 472 param = request.String("param") 473 }), 474 } 475 476 writer = httptest.NewRecorder() 477 router.requestHandler(match, writer, httptest.NewRequest("GET", "/uri?param=param", nil)) 478 suite.Equal("en-US", lang) 479 suite.Equal("param", param) 480 481 // Custom middleware shouldn't be executed 482 strResult := "" 483 testMiddleware := suite.createOrderedTestMiddleware(&strResult, "1") 484 router.Middleware(testMiddleware) 485 486 match = &routeMatch{route: notFoundRoute} 487 router.requestHandler(match, httptest.NewRecorder(), httptest.NewRequest("GET", "/uri", nil)) 488 suite.Empty(strResult) 489 490 match = &routeMatch{route: methodNotAllowedRoute} 491 router.requestHandler(match, httptest.NewRecorder(), httptest.NewRequest("GET", "/uri", nil)) 492 suite.Empty(strResult) 493 494 } 495 496 func (suite *RouterTestSuite) TestMiddlewareHolder() { 497 result := "" 498 testMiddleware := suite.createOrderedTestMiddleware(&result, "1") 499 secondTestMiddleware := suite.createOrderedTestMiddleware(&result, "2") 500 501 holder := &middlewareHolder{[]Middleware{testMiddleware, secondTestMiddleware}} 502 handler := holder.applyMiddleware(func(response *Response, r *Request) { 503 result += "3" 504 }) 505 handler(suite.CreateTestResponse(httptest.NewRecorder()), suite.CreateTestRequest(nil)) 506 suite.Equal("123", result) 507 } 508 509 func (suite *RouterTestSuite) TestTrimCurrentPath() { 510 routeMatch := routeMatch{currentPath: "/product/55"} 511 routeMatch.trimCurrentPath("/product") 512 suite.Equal("/55", routeMatch.currentPath) 513 } 514 515 func (suite *RouterTestSuite) TestMatch() { 516 handler := func(response *Response, r *Request) { 517 response.String(http.StatusOK, "Hello") 518 } 519 520 router := newRouter() 521 router.Route("GET", "/", handler).Name("root") 522 router.Route("GET|POST", "/hello", handler).Name("hello") 523 router.Route("PUT", "/hello", handler).Name("hello.put") 524 router.Route("GET", "/hello/sub", handler).Name("hello.sub") 525 526 productRouter := router.Subrouter("/product") 527 productRouter.Route("GET", "/", handler).Name("product.index") 528 productRouter.Route("GET", "/{id:[0-9]+}", handler).Name("product.show") 529 productRouter.Route("GET", "/{id:[0-9]+}/details", handler).Name("product.show.details") 530 531 userRouter := router.Subrouter("/user") 532 userRouter.Route("GET", "/", handler).Name("user.index") 533 userRouter.Route("GET", "/{id:[0-9]+}", handler).Name("user.show") 534 535 router.Subrouter("/empty") 536 537 match := routeMatch{currentPath: "/"} 538 suite.True(router.match(httptest.NewRequest("GET", "/", nil), &match)) 539 suite.Equal(router.GetRoute("root"), match.route) 540 541 match = routeMatch{currentPath: "/hello"} 542 suite.True(router.match(httptest.NewRequest("GET", "/hello", nil), &match)) 543 suite.Equal(router.GetRoute("hello"), match.route) 544 545 match = routeMatch{currentPath: "/hello/sub"} 546 suite.True(router.match(httptest.NewRequest("GET", "/hello/sub", nil), &match)) 547 suite.Equal(router.GetRoute("hello.sub"), match.route) 548 549 match = routeMatch{currentPath: "/product"} 550 suite.True(router.match(httptest.NewRequest("GET", "/product", nil), &match)) 551 suite.Equal(router.GetRoute("product.index"), match.route) 552 553 match = routeMatch{currentPath: "/product/5"} 554 suite.True(router.match(httptest.NewRequest("GET", "/product/5", nil), &match)) 555 suite.Equal(router.GetRoute("product.show"), match.route) 556 suite.Equal("5", match.parameters["id"]) 557 558 match = routeMatch{currentPath: "/product/5/details"} 559 suite.True(router.match(httptest.NewRequest("GET", "/product/5/details", nil), &match)) 560 suite.Equal(router.GetRoute("product.show.details"), match.route) 561 suite.Equal("5", match.parameters["id"]) 562 563 match = routeMatch{currentPath: "/user"} 564 suite.True(router.match(httptest.NewRequest("GET", "/user", nil), &match)) 565 suite.Equal(router.GetRoute("user.index"), match.route) 566 567 match = routeMatch{currentPath: "/user/42"} 568 suite.True(router.match(httptest.NewRequest("GET", "/user/42", nil), &match)) 569 suite.Equal(router.GetRoute("user.show"), match.route) 570 suite.Equal("42", match.parameters["id"]) 571 572 match = routeMatch{currentPath: "/product/notaroute"} 573 suite.False(router.match(httptest.NewRequest("GET", "/product/notaroute", nil), &match)) 574 suite.Equal(notFoundRoute, match.route) 575 576 match = routeMatch{currentPath: "/empty"} 577 suite.False(router.match(httptest.NewRequest("GET", "/empty", nil), &match)) 578 suite.Equal(notFoundRoute, match.route) 579 580 match = routeMatch{currentPath: "/product"} 581 suite.True(router.match(httptest.NewRequest("DELETE", "/product", nil), &match)) 582 suite.Equal(methodNotAllowedRoute, match.route) 583 584 // ------------ 585 586 paramSubrouter := router.Subrouter("/{param}") 587 route := paramSubrouter.Route("GET", "/{subparam}", handler).Name("param.name") 588 match = routeMatch{currentPath: "/name/surname"} 589 suite.True(router.match(httptest.NewRequest("GET", "/name/surname", nil), &match)) 590 suite.Equal(route, match.route) 591 suite.Equal("name", match.parameters["param"]) 592 suite.Equal("surname", match.parameters["subparam"]) 593 594 // ------------ 595 596 match = routeMatch{currentPath: "/user/42"} 597 suite.False(productRouter.match(httptest.NewRequest("GET", "/user/42", nil), &match)) 598 match = routeMatch{currentPath: "/product/42"} 599 suite.True(productRouter.match(httptest.NewRequest("GET", "/product/42", nil), &match)) 600 suite.Equal(router.GetRoute("product.show"), match.route) 601 suite.Equal("42", match.parameters["id"]) 602 603 match = routeMatch{currentPath: "/user/42/extra"} 604 suite.False(userRouter.match(httptest.NewRequest("GET", "/user/42/extra", nil), &match)) 605 } 606 607 func (suite *RouterTestSuite) TestScheme() { 608 // From HTTP to HTTPS 609 protocol = "https" 610 config.Set("server.protocol", "https") 611 612 router := newRouter() 613 614 recorder := httptest.NewRecorder() 615 router.ServeHTTP(recorder, httptest.NewRequest("GET", "http://localhost:443/test?param=1", nil)) 616 result := recorder.Result() 617 body, err := ioutil.ReadAll(result.Body) 618 suite.Nil(err) 619 result.Body.Close() 620 621 suite.Equal(http.StatusPermanentRedirect, result.StatusCode) 622 suite.Equal("<a href=\"https://127.0.0.1:1236/test?param=1\">Permanent Redirect</a>.\n\n", string(body)) 623 624 // From HTTPS to HTTP 625 config.Set("server.protocol", "http") 626 protocol = "http" 627 628 recorder = httptest.NewRecorder() 629 router.ServeHTTP(recorder, httptest.NewRequest("GET", "https://localhost:80/test?param=1", nil)) 630 result = recorder.Result() 631 body, err = ioutil.ReadAll(result.Body) 632 suite.Nil(err) 633 result.Body.Close() 634 635 suite.Equal(http.StatusPermanentRedirect, result.StatusCode) 636 suite.Equal("<a href=\"http://127.0.0.1:1235/test?param=1\">Permanent Redirect</a>.\n\n", string(body)) 637 638 // Only URI 639 recorder = httptest.NewRecorder() 640 router.ServeHTTP(recorder, httptest.NewRequest("GET", "/test?param=1", nil)) 641 result = recorder.Result() 642 body, err = ioutil.ReadAll(result.Body) 643 suite.Nil(err) 644 result.Body.Close() 645 646 suite.Equal(http.StatusNotFound, result.StatusCode) 647 suite.Equal("{\"error\":\"Not Found\"}\n", string(body)) 648 } 649 650 func (suite *RouterTestSuite) TestConflictingRoutes() { 651 // Test subrouter has priority over routes 652 handler := func(response *Response, request *Request) { 653 response.Status(200) 654 } 655 router := newRouter() 656 657 subrouter := router.Subrouter("/product") 658 routeSub := subrouter.Route("GET", "/{id:[0-9]+}", handler) 659 660 router.Route("GET", "/product/{id:[0-9]+}", handler) 661 662 req := httptest.NewRequest("GET", "/product/2", nil) 663 match := routeMatch{currentPath: req.URL.Path} 664 router.match(req, &match) 665 666 suite.Equal(routeSub, match.route) 667 668 // Test when route not in subrouter but first segment matches 669 // Should not match 670 router.Route("GET", "/product/test", handler) 671 672 req = httptest.NewRequest("GET", "/product/test", nil) 673 match = routeMatch{currentPath: req.URL.Path} 674 router.match(req, &match) 675 676 suite.Equal(notFoundRoute, match.route) 677 } 678 679 func (suite *RouterTestSuite) TestSubrouterEmptyPrefix() { 680 result := "" 681 handler := func(resp *Response, r *Request) {} 682 router := newRouter() 683 684 productRouter := router.Subrouter("/product") 685 productRouter.Route("GET", "/", handler).Name("product.index") 686 productRouter.Route("GET", "/{id:[0-9]+}", handler).Name("product.show") 687 productRouter.Route("POST", "/hardpath", handler).Name("product.hardpath.post") 688 productRouter.Route("GET", "/conflict", handler).Name("product.conflict") 689 690 // This route group has an empty prefix, the full path is identical to productRouter. 691 // However this group has a middleware and some conflicting routes with productRouter. 692 // Conflict should be resolved and both routes should be able to match. 693 groupProductRouter := productRouter.Subrouter("/") 694 groupProductRouter.Middleware(suite.createOrderedTestMiddleware(&result, "1")) 695 groupProductRouter.Route("POST", "/", handler).Name("product.store") 696 groupProductRouter.Route("GET", "/hardpath", handler).Name("product.hardpath.get") 697 groupProductRouter.Route("PUT", "/{id:[0-9]+}", handler).Name("product.update") 698 groupProductRouter.Route("GET", "/conflict", handler).Name("product.conflict.group") 699 groupProductRouter.Route("POST", "/method", handler).Name("product.method") 700 701 req := httptest.NewRequest("GET", "/product", nil) 702 match := routeMatch{currentPath: req.URL.Path} 703 router.match(req, &match) 704 suite.Equal("product.index", match.route.name) 705 router.requestHandler(&match, httptest.NewRecorder(), req) 706 suite.Empty(result) 707 result = "" 708 709 req = httptest.NewRequest("POST", "/product", nil) 710 match = routeMatch{currentPath: req.URL.Path} 711 router.match(req, &match) 712 suite.Equal("product.store", match.route.name) 713 router.requestHandler(&match, httptest.NewRecorder(), req) 714 suite.Equal("1", result) 715 result = "" 716 717 req = httptest.NewRequest("GET", "/product/hardpath", nil) 718 match = routeMatch{currentPath: req.URL.Path} 719 router.match(req, &match) 720 suite.Equal("product.hardpath.get", match.route.name) 721 router.requestHandler(&match, httptest.NewRecorder(), req) 722 suite.Equal("1", result) 723 result = "" 724 725 req = httptest.NewRequest("POST", "/product/hardpath", nil) 726 match = routeMatch{currentPath: req.URL.Path} 727 router.match(req, &match) 728 suite.Equal("product.hardpath.post", match.route.name) 729 router.requestHandler(&match, httptest.NewRecorder(), req) 730 suite.Empty(result) 731 result = "" 732 733 req = httptest.NewRequest("GET", "/product/42", nil) 734 match = routeMatch{currentPath: req.URL.Path} 735 router.match(req, &match) 736 suite.Equal("product.show", match.route.name) 737 router.requestHandler(&match, httptest.NewRecorder(), req) 738 suite.Empty(result) 739 result = "" 740 741 req = httptest.NewRequest("PUT", "/product/42", nil) 742 match = routeMatch{currentPath: req.URL.Path} 743 router.match(req, &match) 744 suite.Equal("product.update", match.route.name) 745 router.requestHandler(&match, httptest.NewRecorder(), req) 746 suite.Equal("1", result) 747 result = "" 748 749 req = httptest.NewRequest("GET", "/product/conflict", nil) 750 match = routeMatch{currentPath: req.URL.Path} 751 router.match(req, &match) 752 suite.Equal("product.conflict.group", match.route.name) 753 754 req = httptest.NewRequest("GET", "/product/method", nil) 755 match = routeMatch{currentPath: req.URL.Path} 756 router.match(req, &match) 757 suite.Equal(methodNotAllowedRoute, match.route) 758 } 759 760 func (suite *RouterTestSuite) TestChainedWriterCloseOnPanic() { 761 result := "" 762 testWr := &testWriter{&result, "0", nil, false} 763 764 suite.RunServer(func(router *Router) { 765 router.Middleware(func(next Handler) Handler { 766 return func(response *Response, r *Request) { 767 testWr.Writer = response.Writer() 768 response.SetWriter(testWr) 769 770 next(response, r) 771 } 772 }) 773 router.Route("GET", "/panic", func(response *Response, req *Request) { 774 panic("chained writer panic") 775 }) 776 }, func() { 777 resp, err := suite.Get("/panic", nil) 778 if err != nil { 779 panic(err) 780 } 781 defer resp.Body.Close() 782 783 suite.Equal(500, resp.StatusCode) 784 suite.True(testWr.closed) 785 }) 786 787 suite.True(testWr.closed) 788 } 789 790 func (suite *RouterTestSuite) TestMethodRouteRegistration() { 791 router := newRouter() 792 route := router.Get("/uri", func(resp *Response, r *Request) {}) 793 suite.Equal([]string{"GET", "HEAD"}, route.methods) 794 795 route = router.Post("/uri", func(resp *Response, r *Request) {}) 796 suite.Equal([]string{"POST"}, route.methods) 797 798 route = router.Put("/uri", func(resp *Response, r *Request) {}) 799 suite.Equal([]string{"PUT"}, route.methods) 800 801 route = router.Patch("/uri", func(resp *Response, r *Request) {}) 802 suite.Equal([]string{"PATCH"}, route.methods) 803 804 route = router.Delete("/uri", func(resp *Response, r *Request) {}) 805 suite.Equal([]string{"DELETE"}, route.methods) 806 807 route = router.Options("/uri", func(resp *Response, r *Request) {}) 808 suite.Equal([]string{"OPTIONS"}, route.methods) 809 } 810 811 func (suite *RouterTestSuite) TestFinalizeHijacked() { 812 recorder := &hijackableRecorder{httptest.NewRecorder()} 813 req := httptest.NewRequest(http.MethodGet, "/hijack", nil) 814 request := suite.CreateTestRequest(req) 815 resp := newResponse(recorder, req) 816 817 c, _, err := resp.Hijack() 818 if err != nil { 819 suite.Fail(err.Error()) 820 } 821 defer c.Close() 822 823 router := newRouter() 824 router.finalize(resp, request) 825 826 suite.False(resp.wroteHeader) 827 } 828 829 func TestRouterTestSuite(t *testing.T) { 830 RunTest(t, new(RouterTestSuite)) 831 }