- More unit tests

- Improves HTML debug render
- InputHolder removed
- More debug logs
This commit is contained in:
Manu Mtz-Almeida 2015-05-05 15:06:38 +02:00
parent 0a192fb0fa
commit f414648384
13 changed files with 371 additions and 298 deletions

View File

@ -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 ##gin 0.x series authors
**Original Developer:** Manu Martinez-Almeida (@manucorporat) **Maintainer:** Manu Martinez-Almeida (@manucorporat), Javier Provecho (@javierprovecho)
**Long-term Maintainer:** Javier Provecho (@javierprovecho)
People and companies, who have contributed, in alphabetical order. People and companies, who have contributed, in alphabetical order.

View File

@ -28,25 +28,22 @@ type Binding interface {
var _validator = validator.NewValidator("binding", validator.BakedInValidators) var _validator = validator.NewValidator("binding", validator.BakedInValidators)
var ( var (
JSON = jsonBinding{} JSON = jsonBinding{}
XML = xmlBinding{} XML = xmlBinding{}
GETForm = getFormBinding{} Form = formBinding{}
POSTForm = postFormBinding{}
) )
func Default(method, contentType string) Binding { func Default(method, contentType string) Binding {
if method == "GET" { if method == "GET" {
return GETForm return Form
} else { } else {
switch contentType { switch contentType {
case MIMEPOSTForm:
return POSTForm
case MIMEJSON: case MIMEJSON:
return JSON return JSON
case MIMEXML, MIMEXML2: case MIMEXML, MIMEXML2:
return XML return XML
default: default:
return GETForm return Form
} }
} }
} }

View File

@ -17,8 +17,8 @@ type FooStruct struct {
} }
func TestBindingDefault(t *testing.T) { func TestBindingDefault(t *testing.T) {
assert.Equal(t, Default("GET", ""), GETForm) assert.Equal(t, Default("GET", ""), Form)
assert.Equal(t, Default("GET", MIMEJSON), GETForm) assert.Equal(t, Default("GET", MIMEJSON), Form)
assert.Equal(t, Default("POST", MIMEJSON), JSON) assert.Equal(t, Default("POST", MIMEJSON), JSON)
assert.Equal(t, Default("PUT", 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("POST", MIMEXML), XML)
assert.Equal(t, Default("PUT", MIMEXML2), XML) assert.Equal(t, Default("PUT", MIMEXML2), XML)
assert.Equal(t, Default("POST", MIMEPOSTForm), POSTForm) assert.Equal(t, Default("POST", MIMEPOSTForm), Form)
assert.Equal(t, Default("DELETE", MIMEPOSTForm), POSTForm) assert.Equal(t, Default("DELETE", MIMEPOSTForm), Form)
} }
func TestBindingJSON(t *testing.T) { func TestBindingJSON(t *testing.T) {
testBinding(t, testBodyBinding(t,
JSON, "json", JSON, "json",
"/", "/", "/", "/",
`{"foo": "bar"}`, `{"bar": "foo"}`) `{"foo": "bar"}`, `{"bar": "foo"}`)
} }
func TestBindingPOSTForm(t *testing.T) { func TestBindingForm(t *testing.T) {
testBinding(t, testFormBinding(t, "POST",
POSTForm, "post_form",
"/", "/", "/", "/",
"foo=bar", "bar=foo") "foo=bar", "bar=foo")
} }
func TestBindingGETForm(t *testing.T) { func TestBindingForm2(t *testing.T) {
testBinding(t, testFormBinding(t, "GET",
GETForm, "get_form",
"/?foo=bar", "/?bar=foo", "/?foo=bar", "/?bar=foo",
"", "") "", "")
} }
func TestBindingXML(t *testing.T) { func TestBindingXML(t *testing.T) {
testBinding(t, testBodyBinding(t,
XML, "xml", XML, "xml",
"/", "/", "/", "/",
"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>") "<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
} }
func testBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) { func testFormBinding(t *testing.T, method, path, badPath, body, badBody string) {
assert.Equal(t, b.Name(), name) b := Form
assert.Equal(t, b.Name(), "query")
obj := FooStruct{} 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) err := b.Bind(req, &obj)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, obj.Foo, "bar") assert.Equal(t, obj.Foo, "bar")
obj = FooStruct{} obj = FooStruct{}
req = requestWithBody(badPath, badBody) req = requestWithBody(method, badPath, badBody)
err = JSON.Bind(req, &obj) err = JSON.Bind(req, &obj)
assert.Error(t, err) assert.Error(t, err)
} }
func requestWithBody(path, body string) (req *http.Request) { func testBodyBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
req, _ = http.NewRequest("POST", path, bytes.NewBufferString(body)) 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 return
} }

View File

@ -6,13 +6,13 @@ package binding
import "net/http" import "net/http"
type getFormBinding struct{} type formBinding struct{}
func (_ getFormBinding) Name() string { func (_ formBinding) Name() string {
return "get_form" 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 { if err := req.ParseForm(); err != nil {
return err return err
} }

View File

@ -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)
}

View File

@ -13,6 +13,7 @@ import (
"github.com/gin-gonic/gin/binding" "github.com/gin-gonic/gin/binding"
"github.com/gin-gonic/gin/render" "github.com/gin-gonic/gin/render"
"golang.org/x/net/context"
) )
const ( const (
@ -27,15 +28,37 @@ const (
const AbortIndex = math.MaxInt8 / 2 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, // 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. // manage the flow, validate the JSON of a request and render a JSON response for example.
type Context struct { type Context struct {
context.Context
writermem responseWriter writermem responseWriter
Request *http.Request Request *http.Request
Writer ResponseWriter Writer ResponseWriter
Params Params Params Params
Input inputHolder
handlers []HandlerFunc handlers []HandlerFunc
index int8 index int8
@ -63,7 +86,6 @@ func (c *Context) Copy() *Context {
var cp Context = *c var cp Context = *c
cp.writermem.ResponseWriter = nil cp.writermem.ResponseWriter = nil
cp.Writer = &cp.writermem cp.Writer = &cp.writermem
cp.Input.context = &cp
cp.index = AbortIndex cp.index = AbortIndex
cp.handlers = nil cp.handlers = nil
return &cp return &cp
@ -138,6 +160,75 @@ func (c *Context) LastError() error {
return nil 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********/ /******** 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 **********/ /********* PARSING REQUEST **********/
/************************************/ /************************************/

View File

@ -79,15 +79,58 @@ func TestContextCopy(t *testing.T) {
cp := c.Copy() cp := c.Copy()
assert.Nil(t, cp.handlers) 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.Request, c.Request)
assert.Equal(t, cp.index, AbortIndex) 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.Keys, c.Keys)
assert.Equal(t, cp.Engine, c.Engine) assert.Equal(t, cp.Engine, c.Engine)
assert.Equal(t, cp.Params, c.Params) 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 // Tests that the response is serialized as JSON
// and Content-Type is set to application/json // and Content-Type is set to application/json
func TestContextRenderJSON(t *testing.T) { func TestContextRenderJSON(t *testing.T) {

12
gin.go
View File

@ -86,9 +86,7 @@ func Default() *Engine {
} }
func (engine *Engine) allocateContext() (context *Context) { func (engine *Engine) allocateContext() (context *Context) {
context = &Context{Engine: engine} return &Context{Engine: engine}
context.Input = inputHolder{context: context}
return
} }
func (engine *Engine) LoadHTMLGlob(pattern string) { func (engine *Engine) LoadHTMLGlob(pattern string) {
@ -110,9 +108,7 @@ func (engine *Engine) LoadHTMLFiles(files ...string) {
} }
func (engine *Engine) SetHTMLTemplate(templ *template.Template) { func (engine *Engine) SetHTMLTemplate(templ *template.Template) {
engine.HTMLRender = render.HTMLRender{ engine.HTMLRender = render.HTMLRender{Template: templ}
Template: templ,
}
} }
// Adds handlers for NoRoute. It return a 404 code by default. // 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 { 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) debugPrint("Listening and serving HTTP on %s\n", addr)
return http.ListenAndServe(addr, engine) return http.ListenAndServe(addr, engine)
} }
func (engine *Engine) RunTLS(addr string, cert string, key string) error { 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) debugPrint("Listening and serving HTTPS on %s\n", addr)
return http.ListenAndServeTLS(addr, cert, key, engine) return http.ListenAndServeTLS(addr, cert, key, engine)
} }
@ -233,6 +231,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
} else { } else {
req.URL.Path = path + "/" req.URL.Path = path + "/"
} }
debugPrint("redirecting request %d: %s --> %s", code, path, req.URL.String())
http.Redirect(c.Writer, req, req.URL.String(), code) http.Redirect(c.Writer, req, req.URL.String(), code)
return true return true
} }
@ -245,6 +244,7 @@ func (engine *Engine) serveAutoRedirect(c *Context, root *node, tsr bool) bool {
) )
if found { if found {
req.URL.Path = string(fixedPath) 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) http.Redirect(c.Writer, req, req.URL.String(), code)
return true return true
} }

View File

@ -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
}

144
middleware_test.go Normal file
View File

@ -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")
}

View File

@ -5,6 +5,7 @@
package render package render
import ( import (
"errors"
"html/template" "html/template"
"net/http" "net/http"
) )
@ -19,24 +20,19 @@ func (r *HTMLDebugRender) Render(w http.ResponseWriter, code int, data ...interf
file := data[0].(string) file := data[0].(string)
obj := data[1] obj := data[1]
if t, err := r.newTemplate(); err == nil { if t, err := r.loadTemplate(); err == nil {
return t.ExecuteTemplate(w, file, obj) return t.ExecuteTemplate(w, file, obj)
} else { } else {
return err return err
} }
} }
func (r *HTMLDebugRender) newTemplate() (*template.Template, error) { func (r *HTMLDebugRender) loadTemplate() (*template.Template, error) {
t := template.New("")
if len(r.Files) > 0 { if len(r.Files) > 0 {
if _, err := t.ParseFiles(r.Files...); err != nil { return template.ParseFiles(r.Files...)
return nil, err
}
} }
if len(r.Glob) > 0 { if len(r.Glob) > 0 {
if _, err := t.ParseGlob(r.Glob); err != nil { return template.ParseGlob(r.Glob)
return nil, err
}
} }
return t, nil return nil, errors.New("the HTML debug render was created without files or glob pattern")
} }

View File

@ -75,5 +75,4 @@ func TestRenderJoinStrings(t *testing.T) {
assert.Equal(t, joinStrings("a", "BB", "c"), "aBBc") assert.Equal(t, joinStrings("a", "BB", "c"), "aBBc")
assert.Equal(t, joinStrings("a", "", "c"), "ac") assert.Equal(t, joinStrings("a", "", "c"), "ac")
assert.Equal(t, joinStrings("text/html", "; charset=utf-8"), "text/html; charset=utf-8") assert.Equal(t, joinStrings("text/html", "; charset=utf-8"), "text/html; charset=utf-8")
} }

View File

@ -5,7 +5,6 @@
package gin package gin
import ( import (
"errors"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/http/httptest" "net/http/httptest"
@ -107,8 +106,26 @@ func TestRouteNotOK2(t *testing.T) {
testRouteNotOK2("HEAD", 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 // TestHandleStaticFile - ensure the static file handles properly
func TestHandleStaticFile(t *testing.T) { func TestRouteStaticFile(t *testing.T) {
// SETUP file // SETUP file
testRoot, _ := os.Getwd() testRoot, _ := os.Getwd()
f, err := ioutil.TempFile(testRoot, "") f, err := ioutil.TempFile(testRoot, "")
@ -134,7 +151,7 @@ func TestHandleStaticFile(t *testing.T) {
} }
// TestHandleStaticDir - ensure the root/sub dir handles properly // TestHandleStaticDir - ensure the root/sub dir handles properly
func TestHandleStaticDir(t *testing.T) { func TestRouteStaticDir(t *testing.T) {
// SETUP // SETUP
r := New() r := New()
r.Static("/", "./") r.Static("/", "./")
@ -151,7 +168,7 @@ func TestHandleStaticDir(t *testing.T) {
} }
// TestHandleHeadToDir - ensure the root/sub dir handles properly // TestHandleHeadToDir - ensure the root/sub dir handles properly
func TestHandleHeadToDir(t *testing.T) { func TestRouteHeadToDir(t *testing.T) {
// SETUP // SETUP
router := New() router := New()
router.Static("/", "./") router.Static("/", "./")
@ -166,152 +183,3 @@ func TestHandleHeadToDir(t *testing.T) {
assert.Contains(t, bodyAsString, "gin.go") assert.Contains(t, bodyAsString, "gin.go")
assert.Equal(t, w.HeaderMap.Get("Content-Type"), "text/html; charset=utf-8") 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")
}