1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
15 // Data-driven tests that check that a given routes-file line translates into
16 // the expected Route object.
17 var routeTestCases = map[string]*Route{
18 "get / Application.Index": {
21 Action: "Application.Index",
22 FixedParams: []string{},
25 "post /app/:id Application.SaveApp": {
28 Action: "Application.SaveApp",
29 FixedParams: []string{},
32 "get /app/ Application.List": {
35 Action: "Application.List",
36 FixedParams: []string{},
39 `get /app/:appId/ Application.Show`: {
42 Action: "Application.Show",
43 FixedParams: []string{},
46 `get /app-wild/*appId/ Application.WildShow`: {
48 Path: `/app-wild/*appId/`,
49 Action: "Application.WildShow",
50 FixedParams: []string{},
53 `GET /public/:filepath Static.Serve("public")`: {
55 Path: "/public/:filepath",
56 Action: "Static.Serve",
57 FixedParams: []string{
62 `GET /javascript/:filepath Static.Serve("public/js")`: {
64 Path: "/javascript/:filepath",
65 Action: "Static.Serve",
66 FixedParams: []string{
71 "* /apps/:id/:action Application.:action": {
73 Path: "/apps/:id/:action",
74 Action: "Application.:action",
75 FixedParams: []string{},
78 "* /:controller/:action :controller.:action": {
80 Path: "/:controller/:action",
81 Action: ":controller.:action",
82 FixedParams: []string{},
85 `GET / Application.Index("Test", "Test2")`: {
88 Action: "Application.Index",
89 FixedParams: []string{
96 // Run the test cases above.
97 func TestComputeRoute(t *testing.T) {
98 for routeLine, expected := range routeTestCases {
99 method, path, action, fixedArgs, found := parseRouteLine(routeLine)
101 t.Error("Failed to parse route line:", routeLine)
104 actual := NewRoute(appModule, method, path, action, fixedArgs, "", 0)
105 eq(t, "Method", actual.Method, expected.Method)
106 eq(t, "Path", actual.Path, expected.Path)
107 eq(t, "Action", actual.Action, expected.Action)
109 t.Fatal("Failed on route:", routeLine)
118 GET / Application.Index
119 GET /test/ Application.Index("Test", "Test2")
120 GET /app/:id/ Application.Show
121 GET /app-wild/*id/ Application.WildShow
122 POST /app/:id Application.Save
123 PATCH /app/:id/ Application.Update
124 PROPFIND /app/:id Application.WebDevMethodPropFind
125 MKCOL /app/:id Application.WebDevMethodMkCol
126 COPY /app/:id Application.WebDevMethodCopy
127 MOVE /app/:id Application.WebDevMethodMove
128 PROPPATCH /app/:id Application.WebDevMethodPropPatch
129 LOCK /app/:id Application.WebDevMethodLock
130 UNLOCK /app/:id Application.WebDevMethodUnLock
131 TRACE /app/:id Application.WebDevMethodTrace
132 PURGE /app/:id Application.CacheMethodPurge
133 GET /javascript/:filepath App\Static.Serve("public/js")
134 GET /public/*filepath Static.Serve("public")
135 * /:controller/:action :controller.:action
140 var routeMatchTestCases = map[*http.Request]*RouteMatch{
143 URL: &url.URL{Path: "/"},
145 ControllerName: "application",
147 FixedParams: []string{},
148 Params: map[string][]string{},
153 URL: &url.URL{Path: "/test/"},
155 ControllerName: "application",
157 FixedParams: []string{"Test", "Test2"},
158 Params: map[string][]string{},
163 URL: &url.URL{Path: "/app/123"},
165 ControllerName: "application",
167 FixedParams: []string{},
168 Params: map[string][]string{"id": {"123"}},
173 URL: &url.URL{Path: "/app/123"},
175 ControllerName: "application",
176 MethodName: "Update",
177 FixedParams: []string{},
178 Params: map[string][]string{"id": {"123"}},
183 URL: &url.URL{Path: "/app/123"},
185 ControllerName: "application",
187 FixedParams: []string{},
188 Params: map[string][]string{"id": {"123"}},
193 URL: &url.URL{Path: "/app/123/"},
195 ControllerName: "application",
197 FixedParams: []string{},
198 Params: map[string][]string{"id": {"123"}},
203 URL: &url.URL{Path: "/public/css/style.css"},
205 ControllerName: "static",
207 FixedParams: []string{"public"},
208 Params: map[string][]string{"filepath": {"css/style.css"}},
213 URL: &url.URL{Path: "/javascript/sessvars.js"},
215 ControllerName: "static",
217 FixedParams: []string{"public/js"},
218 Params: map[string][]string{"filepath": {"sessvars.js"}},
223 URL: &url.URL{Path: "/Implicit/Route"},
225 ControllerName: "implicit",
227 FixedParams: []string{},
228 Params: map[string][]string{
230 "controller": {"Implicit"},
237 URL: &url.URL{Path: "/favicon.ico"},
242 FixedParams: []string{},
243 Params: map[string][]string{},
248 URL: &url.URL{Path: "/app/123"},
249 Header: http.Header{"X-Http-Method-Override": []string{"PATCH"}},
251 ControllerName: "application",
252 MethodName: "Update",
253 FixedParams: []string{},
254 Params: map[string][]string{"id": {"123"}},
259 URL: &url.URL{Path: "/app/123"},
260 Header: http.Header{"X-Http-Method-Override": []string{"PATCH"}},
262 ControllerName: "application",
264 FixedParams: []string{},
265 Params: map[string][]string{"id": {"123"}},
270 URL: &url.URL{Path: "/app/123"},
272 ControllerName: "application",
273 MethodName: "Update",
274 FixedParams: []string{},
275 Params: map[string][]string{"id": {"123"}},
280 URL: &url.URL{Path: "/app/123"},
282 ControllerName: "application",
283 MethodName: "WebDevMethodPropFind",
284 FixedParams: []string{},
285 Params: map[string][]string{"id": {"123"}},
290 URL: &url.URL{Path: "/app/123"},
292 ControllerName: "application",
293 MethodName: "WebDevMethodMkCol",
294 FixedParams: []string{},
295 Params: map[string][]string{"id": {"123"}},
300 URL: &url.URL{Path: "/app/123"},
302 ControllerName: "application",
303 MethodName: "WebDevMethodCopy",
304 FixedParams: []string{},
305 Params: map[string][]string{"id": {"123"}},
310 URL: &url.URL{Path: "/app/123"},
312 ControllerName: "application",
313 MethodName: "WebDevMethodMove",
314 FixedParams: []string{},
315 Params: map[string][]string{"id": {"123"}},
320 URL: &url.URL{Path: "/app/123"},
322 ControllerName: "application",
323 MethodName: "WebDevMethodPropPatch",
324 FixedParams: []string{},
325 Params: map[string][]string{"id": {"123"}},
329 URL: &url.URL{Path: "/app/123"},
331 ControllerName: "application",
332 MethodName: "WebDevMethodLock",
333 FixedParams: []string{},
334 Params: map[string][]string{"id": {"123"}},
339 URL: &url.URL{Path: "/app/123"},
341 ControllerName: "application",
342 MethodName: "WebDevMethodUnLock",
343 FixedParams: []string{},
344 Params: map[string][]string{"id": {"123"}},
349 URL: &url.URL{Path: "/app/123"},
351 ControllerName: "application",
352 MethodName: "WebDevMethodTrace",
353 FixedParams: []string{},
354 Params: map[string][]string{"id": {"123"}},
359 URL: &url.URL{Path: "/app/123"},
361 ControllerName: "application",
362 MethodName: "CacheMethodPurge",
363 FixedParams: []string{},
364 Params: map[string][]string{"id": {"123"}},
368 func TestRouteMatches(t *testing.T) {
370 BasePath = "/BasePath"
371 router := NewRouter("")
372 router.Routes, _ = parseRoutes(appModule, "", "", TestRoutes, false)
373 if err := router.updateTree(); err != nil {
374 t.Errorf("updateTree failed: %s", err)
376 for req, expected := range routeMatchTestCases {
377 t.Log("Routing:", req.Method, req.URL)
379 context := NewGoContext(nil)
380 context.Request.SetRequest(req)
381 c := NewTestController(nil, req)
383 actual := router.Route(c.Request)
384 if !eq(t, "Found route", actual != nil, expected != nil) {
387 if expected.ControllerName != "" {
388 eq(t, "ControllerName", actual.ControllerName, appModule.Namespace()+expected.ControllerName)
390 eq(t, "ControllerName", actual.ControllerName, expected.ControllerName)
393 eq(t, "MethodName", actual.MethodName, strings.ToLower(expected.MethodName))
394 eq(t, "len(Params)", len(actual.Params), len(expected.Params))
395 for key, actualValue := range actual.Params {
396 eq(t, "Params "+key, actualValue[0], expected.Params[key][0])
398 eq(t, "len(FixedParams)", len(actual.FixedParams), len(expected.FixedParams))
399 for i, actualValue := range actual.FixedParams {
400 eq(t, "FixedParams", actualValue, expected.FixedParams[i])
407 type ReverseRouteArgs struct {
409 args map[string]string
412 var reverseRoutingTestCases = map[*ReverseRouteArgs]*ActionDefinition{
414 action: "Application.Index",
415 args: map[string]string{},
420 Action: "Application.Index",
424 action: "Application.Show",
425 args: map[string]string{"id": "123"},
430 Action: "Application.Show",
434 action: "Implicit.Route",
435 args: map[string]string{},
437 URL: "/implicit/route",
440 Action: "Implicit.Route",
444 action: "Application.Save",
445 args: map[string]string{"id": "123", "c": "http://continue"},
447 URL: "/app/123?c=http%3A%2F%2Fcontinue",
450 Action: "Application.Save",
454 action: "Application.WildShow",
455 args: map[string]string{"id": "123"},
457 URL: "/app-wild/123/",
460 Action: "Application.WildShow",
464 action: "Application.WebDevMethodPropFind",
465 args: map[string]string{"id": "123"},
470 Action: "Application.WebDevMethodPropFind",
473 action: "Application.WebDevMethodMkCol",
474 args: map[string]string{"id": "123"},
479 Action: "Application.WebDevMethodMkCol",
482 action: "Application.WebDevMethodCopy",
483 args: map[string]string{"id": "123"},
488 Action: "Application.WebDevMethodCopy",
491 action: "Application.WebDevMethodMove",
492 args: map[string]string{"id": "123"},
497 Action: "Application.WebDevMethodMove",
500 action: "Application.WebDevMethodPropPatch",
501 args: map[string]string{"id": "123"},
506 Action: "Application.WebDevMethodPropPatch",
509 action: "Application.WebDevMethodLock",
510 args: map[string]string{"id": "123"},
515 Action: "Application.WebDevMethodLock",
518 action: "Application.WebDevMethodUnLock",
519 args: map[string]string{"id": "123"},
524 Action: "Application.WebDevMethodUnLock",
527 action: "Application.WebDevMethodTrace",
528 args: map[string]string{"id": "123"},
533 Action: "Application.WebDevMethodTrace",
536 action: "Application.CacheMethodPurge",
537 args: map[string]string{"id": "123"},
542 Action: "Application.CacheMethodPurge",
546 type testController struct {
550 func initControllers() {
551 registerControllers()
553 func TestReverseRouting(t *testing.T) {
555 router := NewRouter("")
556 router.Routes, _ = parseRoutes(appModule, "", "", TestRoutes, false)
557 for routeArgs, expected := range reverseRoutingTestCases {
558 actual := router.Reverse(routeArgs.action, routeArgs.args)
559 if !eq(t, fmt.Sprintf("Found route %s %s", routeArgs.action, actual), actual != nil, expected != nil) {
562 eq(t, "Url", actual.URL, expected.URL)
563 eq(t, "Method", actual.Method, expected.Method)
564 eq(t, "Star", actual.Star, expected.Star)
565 eq(t, "Action", actual.Action, expected.Action)
569 func BenchmarkRouter(b *testing.B) {
570 router := NewRouter("")
571 router.Routes, _ = parseRoutes(nil, "", "", TestRoutes, false)
572 if err := router.updateTree(); err != nil {
573 b.Errorf("updateTree failed: %s", err)
576 for i := 0; i < b.N/len(routeMatchTestCases); i++ {
577 for req := range routeMatchTestCases {
578 c := NewTestController(nil, req)
579 r := router.Route(c.Request)
581 b.Errorf("Request not found: %s", req.URL.Path)
587 // The benchmark from github.com/ant0ine/go-urlrouter
588 func BenchmarkLargeRouter(b *testing.B) {
589 router := NewRouter("")
591 routePaths := []string{
599 for i := 0; i < 10; i++ {
600 for j := 0; j < 5; j++ {
601 routePaths = append(routePaths, fmt.Sprintf("/resource%d/:id/property%d", i, j))
603 routePaths = append(routePaths, fmt.Sprintf("/resource%d/:id", i))
604 routePaths = append(routePaths, fmt.Sprintf("/resource%d", i))
606 routePaths = append(routePaths, "/:any")
608 for _, p := range routePaths {
609 router.Routes = append(router.Routes,
610 NewRoute(appModule, "GET", p, "Controller.Action", "", "", 0))
612 if err := router.updateTree(); err != nil {
613 b.Errorf("updateTree failed: %s", err)
616 requestUrls := []string{
617 "http://example.org/",
618 "http://example.org/resource9/123",
619 "http://example.org/resource9/123/property1",
620 "http://example.org/doesnotexist",
622 var reqs []*http.Request
623 for _, url := range requestUrls {
624 req, _ := http.NewRequest("GET", url, nil)
625 reqs = append(reqs, req)
630 for i := 0; i < b.N/len(reqs); i++ {
631 for _, req := range reqs {
632 c := NewTestController(nil, req)
633 route := router.Route(c.Request)
635 b.Errorf("Failed to route: %s", req.URL.Path)
641 func BenchmarkRouterFilter(b *testing.B) {
642 startFakeBookingApp()
643 controllers := []*Controller{
644 NewTestController(nil, showRequest),
645 NewTestController(nil, staticRequest),
647 for _, c := range controllers {
649 ParseParams(c.Params, c.Request)
653 for i := 0; i < b.N/len(controllers); i++ {
654 for _, c := range controllers {
655 RouterFilter(c, NilChain)
660 func TestOverrideMethodFilter(t *testing.T) {
661 req, _ := http.NewRequest("POST", "/hotels/3", strings.NewReader("_method=put"))
662 req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value")
663 c := NewTestController(nil, req)
665 if HTTPMethodOverride(c, NilChain); c.Request.Method != "PUT" {
666 t.Errorf("Expected to override current method '%s' in route, found '%s' instead", "", c.Request.Method)
672 func eq(t *testing.T, name string, a, b interface{}) bool {
674 t.Error(name, ": (actual)", a, " != ", b, "(expected)")