X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Frouter_test.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Frouter_test.go;h=fe669700ebde8d091774893dad6c8b0a46083312;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/router_test.go b/src/foundation/api/revel/router_test.go new file mode 100644 index 0000000..fe66970 --- /dev/null +++ b/src/foundation/api/revel/router_test.go @@ -0,0 +1,678 @@ +// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved. +// Revel Framework source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package revel + +import ( + "fmt" + "net/http" + "net/url" + "strings" + "testing" +) + +// Data-driven tests that check that a given routes-file line translates into +// the expected Route object. +var routeTestCases = map[string]*Route{ + "get / Application.Index": { + Method: "GET", + Path: "/", + Action: "Application.Index", + FixedParams: []string{}, + }, + + "post /app/:id Application.SaveApp": { + Method: "POST", + Path: "/app/:id", + Action: "Application.SaveApp", + FixedParams: []string{}, + }, + + "get /app/ Application.List": { + Method: "GET", + Path: "/app/", + Action: "Application.List", + FixedParams: []string{}, + }, + + `get /app/:appId/ Application.Show`: { + Method: "GET", + Path: `/app/:appId/`, + Action: "Application.Show", + FixedParams: []string{}, + }, + + `get /app-wild/*appId/ Application.WildShow`: { + Method: "GET", + Path: `/app-wild/*appId/`, + Action: "Application.WildShow", + FixedParams: []string{}, + }, + + `GET /public/:filepath Static.Serve("public")`: { + Method: "GET", + Path: "/public/:filepath", + Action: "Static.Serve", + FixedParams: []string{ + "public", + }, + }, + + `GET /javascript/:filepath Static.Serve("public/js")`: { + Method: "GET", + Path: "/javascript/:filepath", + Action: "Static.Serve", + FixedParams: []string{ + "public", + }, + }, + + "* /apps/:id/:action Application.:action": { + Method: "*", + Path: "/apps/:id/:action", + Action: "Application.:action", + FixedParams: []string{}, + }, + + "* /:controller/:action :controller.:action": { + Method: "*", + Path: "/:controller/:action", + Action: ":controller.:action", + FixedParams: []string{}, + }, + + `GET / Application.Index("Test", "Test2")`: { + Method: "GET", + Path: "/", + Action: "Application.Index", + FixedParams: []string{ + "Test", + "Test2", + }, + }, +} + +// Run the test cases above. +func TestComputeRoute(t *testing.T) { + for routeLine, expected := range routeTestCases { + method, path, action, fixedArgs, found := parseRouteLine(routeLine) + if !found { + t.Error("Failed to parse route line:", routeLine) + continue + } + actual := NewRoute(appModule, method, path, action, fixedArgs, "", 0) + eq(t, "Method", actual.Method, expected.Method) + eq(t, "Path", actual.Path, expected.Path) + eq(t, "Action", actual.Action, expected.Action) + if t.Failed() { + t.Fatal("Failed on route:", routeLine) + } + } +} + +// Router Tests + +const TestRoutes = ` +# This is a comment +GET / Application.Index +GET /test/ Application.Index("Test", "Test2") +GET /app/:id/ Application.Show +GET /app-wild/*id/ Application.WildShow +POST /app/:id Application.Save +PATCH /app/:id/ Application.Update +PROPFIND /app/:id Application.WebDevMethodPropFind +MKCOL /app/:id Application.WebDevMethodMkCol +COPY /app/:id Application.WebDevMethodCopy +MOVE /app/:id Application.WebDevMethodMove +PROPPATCH /app/:id Application.WebDevMethodPropPatch +LOCK /app/:id Application.WebDevMethodLock +UNLOCK /app/:id Application.WebDevMethodUnLock +TRACE /app/:id Application.WebDevMethodTrace +PURGE /app/:id Application.CacheMethodPurge +GET /javascript/:filepath App\Static.Serve("public/js") +GET /public/*filepath Static.Serve("public") +* /:controller/:action :controller.:action + +GET /favicon.ico 404 +` + +var routeMatchTestCases = map[*http.Request]*RouteMatch{ + { + Method: "GET", + URL: &url.URL{Path: "/"}, + }: { + ControllerName: "application", + MethodName: "Index", + FixedParams: []string{}, + Params: map[string][]string{}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/test/"}, + }: { + ControllerName: "application", + MethodName: "Index", + FixedParams: []string{"Test", "Test2"}, + Params: map[string][]string{}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "Show", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "PATCH", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "Update", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "POST", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "Save", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/app/123/"}, + }: { + ControllerName: "application", + MethodName: "Show", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/public/css/style.css"}, + }: { + ControllerName: "static", + MethodName: "Serve", + FixedParams: []string{"public"}, + Params: map[string][]string{"filepath": {"css/style.css"}}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/javascript/sessvars.js"}, + }: { + ControllerName: "static", + MethodName: "Serve", + FixedParams: []string{"public/js"}, + Params: map[string][]string{"filepath": {"sessvars.js"}}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/Implicit/Route"}, + }: { + ControllerName: "implicit", + MethodName: "Route", + FixedParams: []string{}, + Params: map[string][]string{ + "METHOD": {"GET"}, + "controller": {"Implicit"}, + "action": {"Route"}, + }, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/favicon.ico"}, + }: { + ControllerName: "", + MethodName: "", + Action: "404", + FixedParams: []string{}, + Params: map[string][]string{}, + }, + + { + Method: "POST", + URL: &url.URL{Path: "/app/123"}, + Header: http.Header{"X-Http-Method-Override": []string{"PATCH"}}, + }: { + ControllerName: "application", + MethodName: "Update", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "GET", + URL: &url.URL{Path: "/app/123"}, + Header: http.Header{"X-Http-Method-Override": []string{"PATCH"}}, + }: { + ControllerName: "application", + MethodName: "Show", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "PATCH", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "Update", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "PROPFIND", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodPropFind", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "MKCOL", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodMkCol", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "COPY", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodCopy", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "MOVE", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodMove", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "PROPPATCH", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodPropPatch", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + { + Method: "LOCK", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodLock", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "UNLOCK", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodUnLock", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "TRACE", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "WebDevMethodTrace", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, + + { + Method: "PURGE", + URL: &url.URL{Path: "/app/123"}, + }: { + ControllerName: "application", + MethodName: "CacheMethodPurge", + FixedParams: []string{}, + Params: map[string][]string{"id": {"123"}}, + }, +} + +func TestRouteMatches(t *testing.T) { + initControllers() + BasePath = "/BasePath" + router := NewRouter("") + router.Routes, _ = parseRoutes(appModule, "", "", TestRoutes, false) + if err := router.updateTree(); err != nil { + t.Errorf("updateTree failed: %s", err) + } + for req, expected := range routeMatchTestCases { + t.Log("Routing:", req.Method, req.URL) + + context := NewGoContext(nil) + context.Request.SetRequest(req) + c := NewTestController(nil, req) + + actual := router.Route(c.Request) + if !eq(t, "Found route", actual != nil, expected != nil) { + continue + } + if expected.ControllerName != "" { + eq(t, "ControllerName", actual.ControllerName, appModule.Namespace()+expected.ControllerName) + } else { + eq(t, "ControllerName", actual.ControllerName, expected.ControllerName) + } + + eq(t, "MethodName", actual.MethodName, strings.ToLower(expected.MethodName)) + eq(t, "len(Params)", len(actual.Params), len(expected.Params)) + for key, actualValue := range actual.Params { + eq(t, "Params "+key, actualValue[0], expected.Params[key][0]) + } + eq(t, "len(FixedParams)", len(actual.FixedParams), len(expected.FixedParams)) + for i, actualValue := range actual.FixedParams { + eq(t, "FixedParams", actualValue, expected.FixedParams[i]) + } + } +} + +// Reverse Routing + +type ReverseRouteArgs struct { + action string + args map[string]string +} + +var reverseRoutingTestCases = map[*ReverseRouteArgs]*ActionDefinition{ + { + action: "Application.Index", + args: map[string]string{}, + }: { + URL: "/", + Method: "GET", + Star: false, + Action: "Application.Index", + }, + + { + action: "Application.Show", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123/", + Method: "GET", + Star: false, + Action: "Application.Show", + }, + + { + action: "Implicit.Route", + args: map[string]string{}, + }: { + URL: "/implicit/route", + Method: "GET", + Star: true, + Action: "Implicit.Route", + }, + + { + action: "Application.Save", + args: map[string]string{"id": "123", "c": "http://continue"}, + }: { + URL: "/app/123?c=http%3A%2F%2Fcontinue", + Method: "POST", + Star: false, + Action: "Application.Save", + }, + + { + action: "Application.WildShow", + args: map[string]string{"id": "123"}, + }: { + URL: "/app-wild/123/", + Method: "GET", + Star: false, + Action: "Application.WildShow", + }, + + { + action: "Application.WebDevMethodPropFind", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "PROPFIND", + Star: false, + Action: "Application.WebDevMethodPropFind", + }, + { + action: "Application.WebDevMethodMkCol", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "MKCOL", + Star: false, + Action: "Application.WebDevMethodMkCol", + }, + { + action: "Application.WebDevMethodCopy", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "COPY", + Star: false, + Action: "Application.WebDevMethodCopy", + }, + { + action: "Application.WebDevMethodMove", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "MOVE", + Star: false, + Action: "Application.WebDevMethodMove", + }, + { + action: "Application.WebDevMethodPropPatch", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "PROPPATCH", + Star: false, + Action: "Application.WebDevMethodPropPatch", + }, + { + action: "Application.WebDevMethodLock", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "LOCK", + Star: false, + Action: "Application.WebDevMethodLock", + }, + { + action: "Application.WebDevMethodUnLock", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "UNLOCK", + Star: false, + Action: "Application.WebDevMethodUnLock", + }, + { + action: "Application.WebDevMethodTrace", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "TRACE", + Star: false, + Action: "Application.WebDevMethodTrace", + }, + { + action: "Application.CacheMethodPurge", + args: map[string]string{"id": "123"}, + }: { + URL: "/app/123", + Method: "PURGE", + Star: false, + Action: "Application.CacheMethodPurge", + }, +} + +type testController struct { + *Controller +} + +func initControllers() { + registerControllers() +} +func TestReverseRouting(t *testing.T) { + initControllers() + router := NewRouter("") + router.Routes, _ = parseRoutes(appModule, "", "", TestRoutes, false) + for routeArgs, expected := range reverseRoutingTestCases { + actual := router.Reverse(routeArgs.action, routeArgs.args) + if !eq(t, fmt.Sprintf("Found route %s %s", routeArgs.action, actual), actual != nil, expected != nil) { + continue + } + eq(t, "Url", actual.URL, expected.URL) + eq(t, "Method", actual.Method, expected.Method) + eq(t, "Star", actual.Star, expected.Star) + eq(t, "Action", actual.Action, expected.Action) + } +} + +func BenchmarkRouter(b *testing.B) { + router := NewRouter("") + router.Routes, _ = parseRoutes(nil, "", "", TestRoutes, false) + if err := router.updateTree(); err != nil { + b.Errorf("updateTree failed: %s", err) + } + b.ResetTimer() + for i := 0; i < b.N/len(routeMatchTestCases); i++ { + for req := range routeMatchTestCases { + c := NewTestController(nil, req) + r := router.Route(c.Request) + if r == nil { + b.Errorf("Request not found: %s", req.URL.Path) + } + } + } +} + +// The benchmark from github.com/ant0ine/go-urlrouter +func BenchmarkLargeRouter(b *testing.B) { + router := NewRouter("") + + routePaths := []string{ + "/", + "/signin", + "/signout", + "/profile", + "/settings", + "/upload/*file", + } + for i := 0; i < 10; i++ { + for j := 0; j < 5; j++ { + routePaths = append(routePaths, fmt.Sprintf("/resource%d/:id/property%d", i, j)) + } + routePaths = append(routePaths, fmt.Sprintf("/resource%d/:id", i)) + routePaths = append(routePaths, fmt.Sprintf("/resource%d", i)) + } + routePaths = append(routePaths, "/:any") + + for _, p := range routePaths { + router.Routes = append(router.Routes, + NewRoute(appModule, "GET", p, "Controller.Action", "", "", 0)) + } + if err := router.updateTree(); err != nil { + b.Errorf("updateTree failed: %s", err) + } + + requestUrls := []string{ + "http://example.org/", + "http://example.org/resource9/123", + "http://example.org/resource9/123/property1", + "http://example.org/doesnotexist", + } + var reqs []*http.Request + for _, url := range requestUrls { + req, _ := http.NewRequest("GET", url, nil) + reqs = append(reqs, req) + } + + b.ResetTimer() + + for i := 0; i < b.N/len(reqs); i++ { + for _, req := range reqs { + c := NewTestController(nil, req) + route := router.Route(c.Request) + if route == nil { + b.Errorf("Failed to route: %s", req.URL.Path) + } + } + } +} + +func BenchmarkRouterFilter(b *testing.B) { + startFakeBookingApp() + controllers := []*Controller{ + NewTestController(nil, showRequest), + NewTestController(nil, staticRequest), + } + for _, c := range controllers { + c.Params = &Params{} + ParseParams(c.Params, c.Request) + } + + b.ResetTimer() + for i := 0; i < b.N/len(controllers); i++ { + for _, c := range controllers { + RouterFilter(c, NilChain) + } + } +} + +func TestOverrideMethodFilter(t *testing.T) { + req, _ := http.NewRequest("POST", "/hotels/3", strings.NewReader("_method=put")) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; param=value") + c := NewTestController(nil, req) + + if HTTPMethodOverride(c, NilChain); c.Request.Method != "PUT" { + t.Errorf("Expected to override current method '%s' in route, found '%s' instead", "", c.Request.Method) + } +} + +// Helpers + +func eq(t *testing.T, name string, a, b interface{}) bool { + if a != b { + t.Error(name, ": (actual)", a, " != ", b, "(expected)") + return false + } + return true +}