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  }