// Copyright 2014 Manu Martinez-Almeida. All rights reserved. // Use of this source code is governed by a MIT style // license that can be found in the LICENSE file. package gin import ( "crypto/tls" "encoding/json" "fmt" "html/template" "io/ioutil" "math/rand" "net/http" "net/http/httptest" "reflect" "strconv" "strings" "sync/atomic" "testing" "time" "github.com/stretchr/testify/assert" ) func formatAsDate(t time.Time) string { year, month, day := t.Date() return fmt.Sprintf("%d/%02d/%02d", year, month, day) } func setupHTMLFiles(t *testing.T, mode string, tls bool, loadMethod func(*Engine)) *httptest.Server { SetMode(mode) defer SetMode(TestMode) var router *Engine captureOutput(t, func() { router = New() router.Delims("{[{", "}]}") router.SetFuncMap(template.FuncMap{ "formatAsDate": formatAsDate, }) loadMethod(router) router.GET("/test", func(c *Context) { c.HTML(http.StatusOK, "hello.tmpl", map[string]string{"name": "world"}) }) router.GET("/raw", func(c *Context) { c.HTML(http.StatusOK, "raw.tmpl", map[string]interface{}{ "now": time.Date(2017, 07, 01, 0, 0, 0, 0, time.UTC), }) }) }) var ts *httptest.Server if tls { ts = httptest.NewTLSServer(router) } else { ts = httptest.NewServer(router) } return ts } func TestLoadHTMLGlobDebugMode(t *testing.T) { ts := setupHTMLFiles( t, DebugMode, false, func(router *Engine) { router.LoadHTMLGlob("./testdata/template/*") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLGlobTestMode(t *testing.T) { ts := setupHTMLFiles( t, TestMode, false, func(router *Engine) { router.LoadHTMLGlob("./testdata/template/*") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLGlobReleaseMode(t *testing.T) { ts := setupHTMLFiles( t, ReleaseMode, false, func(router *Engine) { router.LoadHTMLGlob("./testdata/template/*") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLGlobUsingTLS(t *testing.T) { ts := setupHTMLFiles( t, DebugMode, true, func(router *Engine) { router.LoadHTMLGlob("./testdata/template/*") }, ) defer ts.Close() // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } client := &http.Client{Transport: tr} res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLGlobFromFuncMap(t *testing.T) { ts := setupHTMLFiles( t, DebugMode, false, func(router *Engine) { router.LoadHTMLGlob("./testdata/template/*") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "Date: 2017/07/01\n", string(resp)) } func init() { SetMode(TestMode) } func TestCreateEngine(t *testing.T) { router := New() assert.Equal(t, "/", router.basePath) assert.Equal(t, router.engine, router) assert.Empty(t, router.Handlers) } func TestLoadHTMLFilesTestMode(t *testing.T) { ts := setupHTMLFiles( t, TestMode, false, func(router *Engine) { router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLFilesDebugMode(t *testing.T) { ts := setupHTMLFiles( t, DebugMode, false, func(router *Engine) { router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLFilesReleaseMode(t *testing.T) { ts := setupHTMLFiles( t, ReleaseMode, false, func(router *Engine) { router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLFilesUsingTLS(t *testing.T) { ts := setupHTMLFiles( t, TestMode, true, func(router *Engine) { router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") }, ) defer ts.Close() // Use InsecureSkipVerify for avoiding `x509: certificate signed by unknown authority` error tr := &http.Transport{ TLSClientConfig: &tls.Config{ InsecureSkipVerify: true, }, } client := &http.Client{Transport: tr} res, err := client.Get(fmt.Sprintf("%s/test", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "

Hello world

", string(resp)) } func TestLoadHTMLFilesFuncMap(t *testing.T) { ts := setupHTMLFiles( t, TestMode, false, func(router *Engine) { router.LoadHTMLFiles("./testdata/template/hello.tmpl", "./testdata/template/raw.tmpl") }, ) defer ts.Close() res, err := http.Get(fmt.Sprintf("%s/raw", ts.URL)) if err != nil { fmt.Println(err) } resp, _ := ioutil.ReadAll(res.Body) assert.Equal(t, "Date: 2017/07/01\n", string(resp)) } func TestAddRoute(t *testing.T) { router := New() router.addRoute("GET", "/", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 1) assert.NotNil(t, router.trees.get("GET")) assert.Nil(t, router.trees.get("POST")) router.addRoute("POST", "/", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 2) assert.NotNil(t, router.trees.get("GET")) assert.NotNil(t, router.trees.get("POST")) router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) assert.Len(t, router.trees, 2) } func TestAddRouteFails(t *testing.T) { router := New() assert.Panics(t, func() { router.addRoute("", "/", HandlersChain{func(_ *Context) {}}) }) assert.Panics(t, func() { router.addRoute("GET", "a", HandlersChain{func(_ *Context) {}}) }) assert.Panics(t, func() { router.addRoute("GET", "/", HandlersChain{}) }) router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) assert.Panics(t, func() { router.addRoute("POST", "/post", HandlersChain{func(_ *Context) {}}) }) } func TestCreateDefaultRouter(t *testing.T) { router := Default() assert.Len(t, router.Handlers, 2) } func TestNoRouteWithoutGlobalHandlers(t *testing.T) { var middleware0 HandlerFunc = func(c *Context) {} var middleware1 HandlerFunc = func(c *Context) {} router := New() router.NoRoute(middleware0) assert.Nil(t, router.Handlers) assert.Len(t, router.noRoute, 1) assert.Len(t, router.allNoRoute, 1) compareFunc(t, router.noRoute[0], middleware0) compareFunc(t, router.allNoRoute[0], middleware0) router.NoRoute(middleware1, middleware0) assert.Len(t, router.noRoute, 2) assert.Len(t, router.allNoRoute, 2) compareFunc(t, router.noRoute[0], middleware1) compareFunc(t, router.allNoRoute[0], middleware1) compareFunc(t, router.noRoute[1], middleware0) compareFunc(t, router.allNoRoute[1], middleware0) } func TestNoRouteWithGlobalHandlers(t *testing.T) { var middleware0 HandlerFunc = func(c *Context) {} var middleware1 HandlerFunc = func(c *Context) {} var middleware2 HandlerFunc = func(c *Context) {} router := New() router.Use(middleware2) router.NoRoute(middleware0) assert.Len(t, router.allNoRoute, 2) assert.Len(t, router.Handlers, 1) assert.Len(t, router.noRoute, 1) compareFunc(t, router.Handlers[0], middleware2) compareFunc(t, router.noRoute[0], middleware0) compareFunc(t, router.allNoRoute[0], middleware2) compareFunc(t, router.allNoRoute[1], middleware0) router.Use(middleware1) assert.Len(t, router.allNoRoute, 3) assert.Len(t, router.Handlers, 2) assert.Len(t, router.noRoute, 1) compareFunc(t, router.Handlers[0], middleware2) compareFunc(t, router.Handlers[1], middleware1) compareFunc(t, router.noRoute[0], middleware0) compareFunc(t, router.allNoRoute[0], middleware2) compareFunc(t, router.allNoRoute[1], middleware1) compareFunc(t, router.allNoRoute[2], middleware0) } func TestNoMethodWithoutGlobalHandlers(t *testing.T) { var middleware0 HandlerFunc = func(c *Context) {} var middleware1 HandlerFunc = func(c *Context) {} router := New() router.NoMethod(middleware0) assert.Empty(t, router.Handlers) assert.Len(t, router.noMethod, 1) assert.Len(t, router.allNoMethod, 1) compareFunc(t, router.noMethod[0], middleware0) compareFunc(t, router.allNoMethod[0], middleware0) router.NoMethod(middleware1, middleware0) assert.Len(t, router.noMethod, 2) assert.Len(t, router.allNoMethod, 2) compareFunc(t, router.noMethod[0], middleware1) compareFunc(t, router.allNoMethod[0], middleware1) compareFunc(t, router.noMethod[1], middleware0) compareFunc(t, router.allNoMethod[1], middleware0) } func TestRebuild404Handlers(t *testing.T) { } func TestNoMethodWithGlobalHandlers(t *testing.T) { var middleware0 HandlerFunc = func(c *Context) {} var middleware1 HandlerFunc = func(c *Context) {} var middleware2 HandlerFunc = func(c *Context) {} router := New() router.Use(middleware2) router.NoMethod(middleware0) assert.Len(t, router.allNoMethod, 2) assert.Len(t, router.Handlers, 1) assert.Len(t, router.noMethod, 1) compareFunc(t, router.Handlers[0], middleware2) compareFunc(t, router.noMethod[0], middleware0) compareFunc(t, router.allNoMethod[0], middleware2) compareFunc(t, router.allNoMethod[1], middleware0) router.Use(middleware1) assert.Len(t, router.allNoMethod, 3) assert.Len(t, router.Handlers, 2) assert.Len(t, router.noMethod, 1) compareFunc(t, router.Handlers[0], middleware2) compareFunc(t, router.Handlers[1], middleware1) compareFunc(t, router.noMethod[0], middleware0) compareFunc(t, router.allNoMethod[0], middleware2) compareFunc(t, router.allNoMethod[1], middleware1) compareFunc(t, router.allNoMethod[2], middleware0) } func compareFunc(t *testing.T, a, b interface{}) { sf1 := reflect.ValueOf(a) sf2 := reflect.ValueOf(b) if sf1.Pointer() != sf2.Pointer() { t.Error("different functions") } } func TestListOfRoutes(t *testing.T) { router := New() router.GET("/favicon.ico", handlerTest1) router.GET("/", handlerTest1) group := router.Group("/users") { group.GET("/", handlerTest2) group.GET("/:id", handlerTest1) group.POST("/:id", handlerTest2) } router.Static("/static", ".") list := router.Routes() assert.Len(t, list, 7) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/favicon.ico", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/users/", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", }) assertRoutePresent(t, list, RouteInfo{ Method: "GET", Path: "/users/:id", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest1$", }) assertRoutePresent(t, list, RouteInfo{ Method: "POST", Path: "/users/:id", Handler: "^(.*/vendor/)?github.com/gin-gonic/gin.handlerTest2$", }) } func TestEngineHandleContext(t *testing.T) { r := New() r.GET("/", func(c *Context) { c.Request.URL.Path = "/v2" r.HandleContext(c) }) v2 := r.Group("/v2") { v2.GET("/", func(c *Context) {}) } assert.NotPanics(t, func() { w := performRequest(r, "GET", "/") assert.Equal(t, 301, w.Code) }) } func TestEngineHandleContextManyReEntries(t *testing.T) { expectValue := 10000 var handlerCounter, middlewareCounter int64 r := New() r.Use(func(c *Context) { atomic.AddInt64(&middlewareCounter, 1) }) r.GET("/:count", func(c *Context) { countStr := c.Param("count") count, err := strconv.Atoi(countStr) assert.NoError(t, err) n, err := c.Writer.Write([]byte(".")) assert.NoError(t, err) assert.Equal(t, 1, n) switch { case count > 0: c.Request.URL.Path = "/" + strconv.Itoa(count-1) r.HandleContext(c) } }, func(c *Context) { atomic.AddInt64(&handlerCounter, 1) }) assert.NotPanics(t, func() { w := performRequest(r, "GET", "/"+strconv.Itoa(expectValue-1)) // include 0 value assert.Equal(t, 200, w.Code) assert.Equal(t, expectValue, w.Body.Len()) }) assert.Equal(t, int64(expectValue), handlerCounter) assert.Equal(t, int64(expectValue), middlewareCounter) } func assertRoutePresent(t *testing.T, gotRoutes RoutesInfo, wantRoute RouteInfo) { for _, gotRoute := range gotRoutes { if gotRoute.Path == wantRoute.Path && gotRoute.Method == wantRoute.Method { assert.Regexp(t, wantRoute.Handler, gotRoute.Handler) return } } t.Errorf("route not found: %v", wantRoute) } func handlerTest1(c *Context) {} func handlerTest2(c *Context) {} // Engine.IndentJsonIndentSpaceNum func TestEngine_IndentJsonIndentSpaceNum(t *testing.T) { rand.Seed(time.Now().UnixNano()) spaceNum := rand.Intn(10) router := Default() defaultResponse := H{"name": "test", "age": 20} router.IndentJsonIndentSpaceNum(spaceNum) router.GET("/test", func(c *Context) { c.IndentedJSON(200, defaultResponse) }) s := httptest.NewServer(router) defer s.Close() req, err := http.NewRequest("GET", "/test", nil) if err != nil { t.Fatal(err) } rr := httptest.NewRecorder() router.ServeHTTP(rr, req) if rr.Code != http.StatusOK { t.Fatal("error code: ", rr.Code) } except, _ := json.MarshalIndent(defaultResponse, "", strings.Repeat(spaceString, spaceNum)) assert.Equal(t, rr.Body.Bytes(), except) t.Log(rr.Body.String()) }