From f4146483847f5fad5cc40df195964e0450cde091 Mon Sep 17 00:00:00 2001 From: Manu Mtz-Almeida Date: Tue, 5 May 2015 15:06:38 +0200 Subject: [PATCH] - More unit tests - Improves HTML debug render - InputHolder removed - More debug logs --- AUTHORS.md | 3 +- binding/binding.go | 13 +-- binding/binding_test.go | 53 ++++++---- binding/{get_form.go => form.go} | 8 +- binding/post_form.go | 23 ---- context.go | 106 ++++++++++++++++++- context_test.go | 47 ++++++++- gin.go | 12 +-- input_holder.go | 69 ------------ middleware_test.go | 144 +++++++++++++++++++++++++ render/html_debug.go | 16 ++- render/render_test.go | 1 - routes_test.go | 174 ++++--------------------------- 13 files changed, 371 insertions(+), 298 deletions(-) rename binding/{get_form.go => form.go} (68%) delete mode 100644 binding/post_form.go delete mode 100644 input_holder.go create mode 100644 middleware_test.go diff --git a/AUTHORS.md b/AUTHORS.md index 467a003..2feaf46 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -4,8 +4,7 @@ List of all the awesome people working to make Gin the best Web Framework in Go. ##gin 0.x series authors -**Original Developer:** Manu Martinez-Almeida (@manucorporat) -**Long-term Maintainer:** Javier Provecho (@javierprovecho) +**Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho) People and companies, who have contributed, in alphabetical order. diff --git a/binding/binding.go b/binding/binding.go index 83cae29..4a7eb8f 100644 --- a/binding/binding.go +++ b/binding/binding.go @@ -28,25 +28,22 @@ type Binding interface { var _validator = validator.NewValidator("binding", validator.BakedInValidators) var ( - JSON = jsonBinding{} - XML = xmlBinding{} - GETForm = getFormBinding{} - POSTForm = postFormBinding{} + JSON = jsonBinding{} + XML = xmlBinding{} + Form = formBinding{} ) func Default(method, contentType string) Binding { if method == "GET" { - return GETForm + return Form } else { switch contentType { - case MIMEPOSTForm: - return POSTForm case MIMEJSON: return JSON case MIMEXML, MIMEXML2: return XML default: - return GETForm + return Form } } } diff --git a/binding/binding_test.go b/binding/binding_test.go index e28ee15..ca16a2d 100644 --- a/binding/binding_test.go +++ b/binding/binding_test.go @@ -17,8 +17,8 @@ type FooStruct struct { } func TestBindingDefault(t *testing.T) { - assert.Equal(t, Default("GET", ""), GETForm) - assert.Equal(t, Default("GET", MIMEJSON), GETForm) + assert.Equal(t, Default("GET", ""), Form) + assert.Equal(t, Default("GET", MIMEJSON), Form) assert.Equal(t, Default("POST", MIMEJSON), JSON) assert.Equal(t, Default("PUT", MIMEJSON), JSON) @@ -26,54 +26,71 @@ func TestBindingDefault(t *testing.T) { assert.Equal(t, Default("POST", MIMEXML), XML) assert.Equal(t, Default("PUT", MIMEXML2), XML) - assert.Equal(t, Default("POST", MIMEPOSTForm), POSTForm) - assert.Equal(t, Default("DELETE", MIMEPOSTForm), POSTForm) + assert.Equal(t, Default("POST", MIMEPOSTForm), Form) + assert.Equal(t, Default("DELETE", MIMEPOSTForm), Form) } func TestBindingJSON(t *testing.T) { - testBinding(t, + testBodyBinding(t, JSON, "json", "/", "/", `{"foo": "bar"}`, `{"bar": "foo"}`) } -func TestBindingPOSTForm(t *testing.T) { - testBinding(t, - POSTForm, "post_form", +func TestBindingForm(t *testing.T) { + testFormBinding(t, "POST", "/", "/", "foo=bar", "bar=foo") } -func TestBindingGETForm(t *testing.T) { - testBinding(t, - GETForm, "get_form", +func TestBindingForm2(t *testing.T) { + testFormBinding(t, "GET", "/?foo=bar", "/?bar=foo", "", "") } func TestBindingXML(t *testing.T) { - testBinding(t, + testBodyBinding(t, XML, "xml", "/", "/", "bar", "foo") } -func testBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { - assert.Equal(t, b.Name(), name) +func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) { + b := Form + assert.Equal(t, b.Name(), "query") obj := FooStruct{} - req := requestWithBody(path, body) + req := requestWithBody(method, path, body) + if method == "POST" { + req.Header.Add("Content-Type", MIMEPOSTForm) + } err := b.Bind(req, &obj) assert.NoError(t, err) assert.Equal(t, obj.Foo, "bar") obj = FooStruct{} - req = requestWithBody(badPath, badBody) + req = requestWithBody(method, badPath, badBody) err = JSON.Bind(req, &obj) assert.Error(t, err) } -func requestWithBody(path, body string) (req *http.Request) { - req, _ = http.NewRequest("POST", path, bytes.NewBufferString(body)) +func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { + assert.Equal(t, b.Name(), name) + + obj := FooStruct{} + req := requestWithBody("POST", path, body) + err := b.Bind(req, &obj) + assert.NoError(t, err) + assert.Equal(t, obj.Foo, "bar") + + obj = FooStruct{} + req = requestWithBody("POST", badPath, badBody) + err = JSON.Bind(req, &obj) + assert.Error(t, err) +} + +func requestWithBody(method, path, body string) (req *http.Request) { + req, _ = http.NewRequest(method, path, bytes.NewBufferString(body)) return } diff --git a/binding/get_form.go b/binding/form.go similarity index 68% rename from binding/get_form.go rename to binding/form.go index 6226c51..9d906b3 100644 --- a/binding/get_form.go +++ b/binding/form.go @@ -6,13 +6,13 @@ package binding import "net/http" -type getFormBinding struct{} +type formBinding struct{} -func (_ getFormBinding) Name() string { - return "get_form" +func (_ formBinding) Name() string { + return "query" } -func (_ getFormBinding) Bind(req *http.Request, obj interface{}) error { +func (_ formBinding) Bind(req *http.Request, obj interface{}) error { if err := req.ParseForm(); err != nil { return err } diff --git a/binding/post_form.go b/binding/post_form.go deleted file mode 100644 index 9a0f0b6..0000000 --- a/binding/post_form.go +++ /dev/null @@ -1,23 +0,0 @@ -// 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 binding - -import "net/http" - -type postFormBinding struct{} - -func (_ postFormBinding) Name() string { - return "post_form" -} - -func (_ postFormBinding) Bind(req *http.Request, obj interface{}) error { - if err := req.ParseForm(); err != nil { - return err - } - if err := mapForm(obj, req.PostForm); err != nil { - return err - } - return Validate(obj) -} diff --git a/context.go b/context.go index 78e1cc0..c9674f7 100644 --- a/context.go +++ b/context.go @@ -13,6 +13,7 @@ import ( "github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/render" + "golang.org/x/net/context" ) const ( @@ -27,15 +28,37 @@ const ( const AbortIndex = math.MaxInt8 / 2 +// Param is a single URL parameter, consisting of a key and a value. +type Param struct { + Key string + Value string +} + +// Params is a Param-slice, as returned by the router. +// The slice is ordered, the first URL parameter is also the first slice value. +// It is therefore safe to read values by the index. +type Params []Param + +// ByName returns the value of the first Param which key matches the given name. +// If no matching Param is found, an empty string is returned. +func (ps Params) ByName(name string) string { + for _, entry := range ps { + if entry.Key == name { + return entry.Value + } + } + return "" +} + // Context is the most important part of gin. It allows us to pass variables between middleware, // manage the flow, validate the JSON of a request and render a JSON response for example. type Context struct { + context.Context writermem responseWriter Request *http.Request Writer ResponseWriter Params Params - Input inputHolder handlers []HandlerFunc index int8 @@ -63,7 +86,6 @@ func (c *Context) Copy() *Context { var cp Context = *c cp.writermem.ResponseWriter = nil cp.Writer = &cp.writermem - cp.Input.context = &cp cp.index = AbortIndex cp.handlers = nil return &cp @@ -138,6 +160,75 @@ func (c *Context) LastError() error { return nil } +/************************************/ +/************ INPUT DATA ************/ +/************************************/ + +/** Shortcut for c.Request.FormValue(key) */ +func (c *Context) FormValue(key string) (va string) { + va, _ = c.formValue(key) + return +} + +/** Shortcut for c.Request.PostFormValue(key) */ +func (c *Context) PostFormValue(key string) (va string) { + va, _ = c.postFormValue(key) + return +} + +/** Shortcut for c.Params.ByName(key) */ +func (c *Context) ParamValue(key string) (va string) { + va, _ = c.paramValue(key) + return +} + +func (c *Context) DefaultPostFormValue(key, defaultValue string) string { + if va, ok := c.postFormValue(key); ok { + return va + } else { + return defaultValue + } +} + +func (c *Context) DefaultFormValue(key, defaultValue string) string { + if va, ok := c.formValue(key); ok { + return va + } else { + return defaultValue + } +} + +func (c *Context) DefaultParamValue(key, defaultValue string) string { + if va, ok := c.paramValue(key); ok { + return va + } else { + return defaultValue + } +} + +func (c *Context) paramValue(key string) (string, bool) { + va := c.Params.ByName(key) + return va, len(va) > 0 +} + +func (c *Context) formValue(key string) (string, bool) { + req := c.Request + req.ParseForm() + if values, ok := req.Form[key]; ok && len(values) > 0 { + return values[0], true + } + return "", false +} + +func (c *Context) postFormValue(key string) (string, bool) { + req := c.Request + req.ParseForm() + if values, ok := req.PostForm[key]; ok && len(values) > 0 { + return values[0], true + } + return "", false +} + /************************************/ /******** METADATA MANAGEMENT********/ /************************************/ @@ -168,6 +259,17 @@ func (c *Context) MustGet(key string) interface{} { } } +func (c *Context) Value(key interface{}) interface{} { + if key == 0 { + return c.Request + } + if keyAsString, ok := key.(string); ok { + val, _ := c.Get(keyAsString) + return val + } + return c.Context.Value(key) +} + /************************************/ /********* PARSING REQUEST **********/ /************************************/ diff --git a/context_test.go b/context_test.go index dd84473..54c3581 100644 --- a/context_test.go +++ b/context_test.go @@ -79,15 +79,58 @@ func TestContextCopy(t *testing.T) { cp := c.Copy() assert.Nil(t, cp.handlers) + assert.Nil(t, cp.writermem.ResponseWriter) + assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter)) assert.Equal(t, cp.Request, c.Request) assert.Equal(t, cp.index, AbortIndex) - assert.Equal(t, &cp.writermem, cp.Writer.(*responseWriter)) - assert.Equal(t, cp.Input.context, cp) assert.Equal(t, cp.Keys, c.Keys) assert.Equal(t, cp.Engine, c.Engine) assert.Equal(t, cp.Params, c.Params) } +func TestContextFormParse(t *testing.T) { + c, _, _ := createTestContext() + c.Request, _ = http.NewRequest("GET", "http://example.com/?foo=bar&page=10", nil) + + assert.Equal(t, c.DefaultFormValue("foo", "none"), "bar") + assert.Equal(t, c.FormValue("foo"), "bar") + assert.Empty(t, c.PostFormValue("foo")) + + assert.Equal(t, c.DefaultFormValue("page", "0"), "10") + assert.Equal(t, c.FormValue("page"), "10") + assert.Empty(t, c.PostFormValue("page")) + + assert.Equal(t, c.DefaultFormValue("NoKey", "nada"), "nada") + assert.Empty(t, c.FormValue("NoKey")) + assert.Empty(t, c.PostFormValue("NoKey")) + +} + +func TestContextPostFormParse(t *testing.T) { + c, _, _ := createTestContext() + body := bytes.NewBufferString("foo=bar&page=11&both=POST") + c.Request, _ = http.NewRequest("POST", "http://example.com/?both=GET&id=main", body) + c.Request.Header.Add("Content-Type", MIMEPOSTForm) + + assert.Equal(t, c.DefaultPostFormValue("foo", "none"), "bar") + assert.Equal(t, c.PostFormValue("foo"), "bar") + assert.Equal(t, c.FormValue("foo"), "bar") + + assert.Equal(t, c.DefaultPostFormValue("page", "0"), "11") + assert.Equal(t, c.PostFormValue("page"), "11") + assert.Equal(t, c.FormValue("page"), "11") + + assert.Equal(t, c.PostFormValue("both"), "POST") + assert.Equal(t, c.FormValue("both"), "POST") + + assert.Equal(t, c.FormValue("id"), "main") + assert.Empty(t, c.PostFormValue("id")) + + assert.Equal(t, c.DefaultPostFormValue("NoKey", "nada"), "nada") + assert.Empty(t, c.PostFormValue("NoKey")) + assert.Empty(t, c.FormValue("NoKey")) +} + // Tests that the response is serialized as JSON // and Content-Type is set to application/json func TestContextRenderJSON(t *testing.T) { diff --git a/gin.go b/gin.go index 829a28d..aa6c770 100644 --- a/gin.go +++ b/gin.go @@ -86,9 +86,7 @@ func Default() *Engine { } func (engine *Engine) allocateContext() (context *Context) { - context = &Context{Engine: engine} - context.Input = inputHolder{context: context} - return + return &Context{Engine: engine} } func (engine *Engine) LoadHTMLGlob(pattern string) { @@ -110,9 +108,7 @@ func (engine *Engine) LoadHTMLFiles(files ...string) { } func (engine *Engine) SetHTMLTemplate(templ *template.Template) { - engine.HTMLRender = render.HTMLRender{ - Template: templ, - } + engine.HTMLRender = render.HTMLRender{Template: templ} } // Adds handlers for NoRoute. It return a 404 code by default. @@ -160,11 +156,13 @@ func (engine *Engine) handle(method, path string, handlers []HandlerFunc) { } func (engine *Engine) Run(addr string) error { + debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production") debugPrint("Listening and serving HTTP on %s\n", addr) return http.ListenAndServe(addr, engine) } func (engine *Engine) RunTLS(addr string, cert string, key string) error { + debugPrint("[WARNING] Running in DEBUG mode! Disable it before going production") debugPrint("Listening and serving HTTPS on %s\n", addr) return http.ListenAndServeTLS(addr, cert, key, engine) } @@ -233,6 +231,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { } else { req.URL.Path = path + "/" } + debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String()) http.Redirect(c.Writer, req, req.URL.String(), code) return true } @@ -245,6 +244,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool { ) if found { req.URL.Path = string(fixedPath) + debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String()) http.Redirect(c.Writer, req, req.URL.String(), code) return true } diff --git a/input_holder.go b/input_holder.go deleted file mode 100644 index b40eb28..0000000 --- a/input_holder.go +++ /dev/null @@ -1,69 +0,0 @@ -// 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 - -// Param is a single URL parameter, consisting of a key and a value. -type Param struct { - Key string - Value string -} - -// Params is a Param-slice, as returned by the router. -// The slice is ordered, the first URL parameter is also the first slice value. -// It is therefore safe to read values by the index. -type Params []Param - -// ByName returns the value of the first Param which key matches the given name. -// If no matching Param is found, an empty string is returned. -func (ps Params) ByName(name string) string { - for _, entry := range ps { - if entry.Key == name { - return entry.Value - } - } - return "" -} - -type inputHolder struct { - context *Context -} - -func (i inputHolder) FromGET(key string) (va string) { - va, _ = i.fromGET(key) - return -} - -func (i inputHolder) FromPOST(key string) (va string) { - va, _ = i.fromPOST(key) - return -} - -func (i inputHolder) Get(key string) string { - if value, exists := i.fromPOST(key); exists { - return value - } - if value, exists := i.fromGET(key); exists { - return value - } - return "" -} - -func (i inputHolder) fromGET(key string) (string, bool) { - req := i.context.Request - req.ParseForm() - if values, ok := req.Form[key]; ok && len(values) > 0 { - return values[0], true - } - return "", false -} - -func (i inputHolder) fromPOST(key string) (string, bool) { - req := i.context.Request - req.ParseForm() - if values, ok := req.PostForm[key]; ok && len(values) > 0 { - return values[0], true - } - return "", false -} diff --git a/middleware_test.go b/middleware_test.go new file mode 100644 index 0000000..4ae367a --- /dev/null +++ b/middleware_test.go @@ -0,0 +1,144 @@ +// 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 ( + "errors" + + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMiddlewareGeneralCase(t *testing.T) { + signature := "" + router := New() + router.Use(func(c *Context) { + signature += "A" + c.Next() + signature += "B" + }) + router.Use(func(c *Context) { + signature += "C" + }) + router.GET("/", func(c *Context) { + signature += "D" + }) + router.NoRoute(func(c *Context) { + signature += "X" + }) + router.NoMethod(func(c *Context) { + signature += "X" + }) + // RUN + w := performRequest(router, "GET", "/") + + // TEST + assert.Equal(t, w.Code, 200) + assert.Equal(t, signature, "ACDB") +} + +// TestBadAbortHandlersChain - ensure that Abort after switch context will not interrupt pending handlers +func TestMiddlewareNextOrder(t *testing.T) { + signature := "" + router := New() + router.Use(func(c *Context) { + signature += "A" + c.Next() + signature += "B" + }) + router.Use(func(c *Context) { + signature += "C" + c.Next() + signature += "D" + }) + router.NoRoute(func(c *Context) { + signature += "E" + c.Next() + signature += "F" + }, func(c *Context) { + signature += "G" + c.Next() + signature += "H" + }) + // RUN + w := performRequest(router, "GET", "/") + + // TEST + assert.Equal(t, w.Code, 404) + assert.Equal(t, signature, "ACEGHFDB") +} + +// TestAbortHandlersChain - ensure that Abort interrupt used middlewares in fifo order +func TestMiddlewareAbortHandlersChain(t *testing.T) { + signature := "" + router := New() + router.Use(func(c *Context) { + signature += "A" + }) + router.Use(func(c *Context) { + signature += "C" + c.AbortWithStatus(409) + c.Next() + signature += "D" + }) + router.GET("/", func(c *Context) { + signature += "D" + c.Next() + signature += "E" + }) + + // RUN + w := performRequest(router, "GET", "/") + + // TEST + assert.Equal(t, w.Code, 409) + assert.Equal(t, signature, "ACD") +} + +func TestMiddlewareAbortHandlersChainAndNext(t *testing.T) { + signature := "" + router := New() + router.Use(func(c *Context) { + signature += "A" + c.AbortWithStatus(410) + c.Next() + signature += "B" + + }) + router.GET("/", func(c *Context) { + signature += "C" + c.Next() + }) + // RUN + w := performRequest(router, "GET", "/") + + // TEST + assert.Equal(t, w.Code, 410) + assert.Equal(t, signature, "AB") +} + +// TestFailHandlersChain - ensure that Fail interrupt used middlewares in fifo order as +// as well as Abort +func TestMiddlewareFailHandlersChain(t *testing.T) { + // SETUP + signature := "" + router := New() + router.Use(func(context *Context) { + signature += "A" + context.Fail(500, errors.New("foo")) + }) + router.Use(func(context *Context) { + signature += "B" + context.Next() + signature += "C" + }) + // RUN + w := performRequest(router, "GET", "/") + + // TEST + assert.Equal(t, w.Code, 500) + assert.Equal(t, signature, "A") +} diff --git a/render/html_debug.go b/render/html_debug.go index 1edac5d..2a5a697 100644 --- a/render/html_debug.go +++ b/render/html_debug.go @@ -5,6 +5,7 @@ package render import ( + "errors" "html/template" "net/http" ) @@ -19,24 +20,19 @@ func (r *HTMLDebugRender) Render(w http.ResponseWriter, code int, data ...interf file := data[0].(string) obj := data[1] - if t, err := r.newTemplate(); err == nil { + if t, err := r.loadTemplate(); err == nil { return t.ExecuteTemplate(w, file, obj) } else { return err } } -func (r *HTMLDebugRender) newTemplate() (*template.Template, error) { - t := template.New("") +func (r *HTMLDebugRender) loadTemplate() (*template.Template, error) { if len(r.Files) > 0 { - if _, err := t.ParseFiles(r.Files...); err != nil { - return nil, err - } + return template.ParseFiles(r.Files...) } if len(r.Glob) > 0 { - if _, err := t.ParseGlob(r.Glob); err != nil { - return nil, err - } + return template.ParseGlob(r.Glob) } - return t, nil + return nil, errors.New("the HTML debug render was created without files or glob pattern") } diff --git a/render/render_test.go b/render/render_test.go index b406122..88ee24f 100644 --- a/render/render_test.go +++ b/render/render_test.go @@ -75,5 +75,4 @@ func TestRenderJoinStrings(t *testing.T) { assert.Equal(t, joinStrings("a", "BB", "c"), "aBBc") assert.Equal(t, joinStrings("a", "", "c"), "ac") assert.Equal(t, joinStrings("text/html", "; charset=utf-8"), "text/html; charset=utf-8") - } diff --git a/routes_test.go b/routes_test.go index fd4d5b6..5c8821e 100644 --- a/routes_test.go +++ b/routes_test.go @@ -5,7 +5,6 @@ package gin import ( - "errors" "io/ioutil" "net/http" "net/http/httptest" @@ -107,8 +106,26 @@ func TestRouteNotOK2(t *testing.T) { testRouteNotOK2("HEAD", t) } +// TestContextParamsGet tests that a parameter can be parsed from the URL. +func TestRouteParamsByName(t *testing.T) { + name := "" + lastName := "" + router := New() + router.GET("/test/:name/:last_name", func(c *Context) { + name = c.Params.ByName("name") + lastName = c.Params.ByName("last_name") + }) + // RUN + w := performRequest(router, "GET", "/test/john/smith") + + // TEST + assert.Equal(t, w.Code, 200) + assert.Equal(t, name, "john") + assert.Equal(t, lastName, "smith") +} + // TestHandleStaticFile - ensure the static file handles properly -func TestHandleStaticFile(t *testing.T) { +func TestRouteStaticFile(t *testing.T) { // SETUP file testRoot, _ := os.Getwd() f, err := ioutil.TempFile(testRoot, "") @@ -134,7 +151,7 @@ func TestHandleStaticFile(t *testing.T) { } // TestHandleStaticDir - ensure the root/sub dir handles properly -func TestHandleStaticDir(t *testing.T) { +func TestRouteStaticDir(t *testing.T) { // SETUP r := New() r.Static("/", "./") @@ -151,7 +168,7 @@ func TestHandleStaticDir(t *testing.T) { } // TestHandleHeadToDir - ensure the root/sub dir handles properly -func TestHandleHeadToDir(t *testing.T) { +func TestRouteHeadToDir(t *testing.T) { // SETUP router := New() router.Static("/", "./") @@ -166,152 +183,3 @@ func TestHandleHeadToDir(t *testing.T) { assert.Contains(t, bodyAsString, "gin.go") assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") } - -func TestContextGeneralCase(t *testing.T) { - signature := "" - router := New() - router.Use(func(c *Context) { - signature += "A" - c.Next() - signature += "B" - }) - router.Use(func(c *Context) { - signature += "C" - }) - router.GET("/", func(c *Context) { - signature += "D" - }) - router.NoRoute(func(c *Context) { - signature += "X" - }) - router.NoMethod(func(c *Context) { - signature += "X" - }) - // RUN - w := performRequest(router, "GET", "/") - - // TEST - assert.Equal(t, w.Code, 200) - assert.Equal(t, signature, "ACDB") -} - -// TestBadAbortHandlersChain - ensure that Abort after switch context will not interrupt pending handlers -func TestContextNextOrder(t *testing.T) { - signature := "" - router := New() - router.Use(func(c *Context) { - signature += "A" - c.Next() - signature += "B" - }) - router.Use(func(c *Context) { - signature += "C" - c.Next() - signature += "D" - }) - router.NoRoute(func(c *Context) { - signature += "E" - c.Next() - signature += "F" - }, func(c *Context) { - signature += "G" - c.Next() - signature += "H" - }) - // RUN - w := performRequest(router, "GET", "/") - - // TEST - assert.Equal(t, w.Code, 404) - assert.Equal(t, signature, "ACEGHFDB") -} - -// TestAbortHandlersChain - ensure that Abort interrupt used middlewares in fifo order -func TestAbortHandlersChain(t *testing.T) { - signature := "" - router := New() - router.Use(func(c *Context) { - signature += "A" - }) - router.Use(func(c *Context) { - signature += "C" - c.AbortWithStatus(409) - c.Next() - signature += "D" - }) - router.GET("/", func(c *Context) { - signature += "D" - c.Next() - signature += "E" - }) - - // RUN - w := performRequest(router, "GET", "/") - - // TEST - assert.Equal(t, w.Code, 409) - assert.Equal(t, signature, "ACD") -} - -func TestAbortHandlersChainAndNext(t *testing.T) { - signature := "" - router := New() - router.Use(func(c *Context) { - signature += "A" - c.AbortWithStatus(410) - c.Next() - signature += "B" - - }) - router.GET("/", func(c *Context) { - signature += "C" - c.Next() - }) - // RUN - w := performRequest(router, "GET", "/") - - // TEST - assert.Equal(t, w.Code, 410) - assert.Equal(t, signature, "AB") -} - -// TestContextParamsGet tests that a parameter can be parsed from the URL. -func TestContextParamsByName(t *testing.T) { - name := "" - lastName := "" - router := New() - router.GET("/test/:name/:last_name", func(c *Context) { - name = c.Params.ByName("name") - lastName = c.Params.ByName("last_name") - }) - // RUN - w := performRequest(router, "GET", "/test/john/smith") - - // TEST - assert.Equal(t, w.Code, 200) - assert.Equal(t, name, "john") - assert.Equal(t, lastName, "smith") -} - -// TestFailHandlersChain - ensure that Fail interrupt used middlewares in fifo order as -// as well as Abort -func TestFailHandlersChain(t *testing.T) { - // SETUP - signature := "" - router := New() - router.Use(func(context *Context) { - signature += "A" - context.Fail(500, errors.New("foo")) - }) - router.Use(func(context *Context) { - signature += "B" - context.Next() - signature += "C" - }) - // RUN - w := performRequest(router, "GET", "/") - - // TEST - assert.Equal(t, w.Code, 500) - assert.Equal(t, signature, "A") -}