mirror of https://github.com/gin-gonic/gin.git
Tons of unit tests
This commit is contained in:
parent
ac1ee3fb86
commit
0a192fb0fa
|
@ -1,2 +1,4 @@
|
||||||
Godeps/*
|
Godeps/*
|
||||||
!Godeps/Godeps.json
|
!Godeps/Godeps.json
|
||||||
|
coverage.out
|
||||||
|
count.out
|
||||||
|
|
|
@ -73,6 +73,10 @@ func TestBasicAuthSearchCredential(t *testing.T) {
|
||||||
user, found = pairs.searchCredential(authorizationHeader("foo", "bar "))
|
user, found = pairs.searchCredential(authorizationHeader("foo", "bar "))
|
||||||
assert.Empty(t, user)
|
assert.Empty(t, user)
|
||||||
assert.False(t, found)
|
assert.False(t, found)
|
||||||
|
|
||||||
|
user, found = pairs.searchCredential("")
|
||||||
|
assert.Empty(t, user)
|
||||||
|
assert.False(t, found)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBasicAuthAuthorizationHeader(t *testing.T) {
|
func TestBasicAuthAuthorizationHeader(t *testing.T) {
|
||||||
|
|
|
@ -50,3 +50,10 @@ func Default(method, contentType string) Binding {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Validate(obj interface{}) error {
|
||||||
|
if err := _validator.ValidateStruct(obj); err != nil {
|
||||||
|
return error(err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"net/http"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FooStruct struct {
|
||||||
|
Foo string `json:"foo" form:"foo" xml:"foo" binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindingDefault(t *testing.T) {
|
||||||
|
assert.Equal(t, Default("GET", ""), GETForm)
|
||||||
|
assert.Equal(t, Default("GET", MIMEJSON), GETForm)
|
||||||
|
|
||||||
|
assert.Equal(t, Default("POST", MIMEJSON), JSON)
|
||||||
|
assert.Equal(t, Default("PUT", MIMEJSON), JSON)
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindingJSON(t *testing.T) {
|
||||||
|
testBinding(t,
|
||||||
|
JSON, "json",
|
||||||
|
"/", "/",
|
||||||
|
`{"foo": "bar"}`, `{"bar": "foo"}`)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindingPOSTForm(t *testing.T) {
|
||||||
|
testBinding(t,
|
||||||
|
POSTForm, "post_form",
|
||||||
|
"/", "/",
|
||||||
|
"foo=bar", "bar=foo")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindingGETForm(t *testing.T) {
|
||||||
|
testBinding(t,
|
||||||
|
GETForm, "get_form",
|
||||||
|
"/?foo=bar", "/?bar=foo",
|
||||||
|
"", "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBindingXML(t *testing.T) {
|
||||||
|
testBinding(t,
|
||||||
|
XML, "xml",
|
||||||
|
"/", "/",
|
||||||
|
"<map><foo>bar</foo></map>", "<map><bar>foo</bar></map>")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testBinding(t *testing.T, b Binding, name, path, badPath, body, badBody string) {
|
||||||
|
assert.Equal(t, b.Name(), name)
|
||||||
|
|
||||||
|
obj := FooStruct{}
|
||||||
|
req := requestWithBody(path, body)
|
||||||
|
err := b.Bind(req, &obj)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, obj.Foo, "bar")
|
||||||
|
|
||||||
|
obj = FooStruct{}
|
||||||
|
req = requestWithBody(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))
|
||||||
|
return
|
||||||
|
}
|
|
@ -19,8 +19,5 @@ func (_ getFormBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
if err := mapForm(obj, req.Form); err != nil {
|
if err := mapForm(obj, req.Form); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := _validator.ValidateStruct(obj); err != nil {
|
return Validate(obj)
|
||||||
return error(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,5 @@ func (_ jsonBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := _validator.ValidateStruct(obj); err != nil {
|
return Validate(obj)
|
||||||
return error(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,5 @@ func (_ postFormBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
if err := mapForm(obj, req.PostForm); err != nil {
|
if err := mapForm(obj, req.PostForm); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := _validator.ValidateStruct(obj); err != nil {
|
return Validate(obj)
|
||||||
return error(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
// 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 (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type struct1 struct {
|
||||||
|
Value float64 `binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type struct2 struct {
|
||||||
|
RequiredValue string `binding:"required"`
|
||||||
|
Value float64
|
||||||
|
}
|
||||||
|
|
||||||
|
type struct3 struct {
|
||||||
|
Integer int
|
||||||
|
String string
|
||||||
|
BasicSlice []int
|
||||||
|
Boolean bool
|
||||||
|
|
||||||
|
RequiredInteger int `binding:"required"`
|
||||||
|
RequiredString string `binding:"required"`
|
||||||
|
RequiredAnotherStruct struct1 `binding:"required"`
|
||||||
|
RequiredBasicSlice []int `binding:"required"`
|
||||||
|
RequiredComplexSlice []struct2 `binding:"required"`
|
||||||
|
RequiredBoolean bool `binding:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func createStruct() struct3 {
|
||||||
|
return struct3{
|
||||||
|
RequiredInteger: 2,
|
||||||
|
RequiredString: "hello",
|
||||||
|
RequiredAnotherStruct: struct1{1.5},
|
||||||
|
RequiredBasicSlice: []int{1, 2, 3, 4},
|
||||||
|
RequiredComplexSlice: []struct2{
|
||||||
|
{RequiredValue: "A"},
|
||||||
|
{RequiredValue: "B"},
|
||||||
|
},
|
||||||
|
RequiredBoolean: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValidateGoodObject(t *testing.T) {
|
||||||
|
test := createStruct()
|
||||||
|
assert.Nil(t, Validate(&test))
|
||||||
|
}
|
|
@ -20,8 +20,5 @@ func (_ xmlBinding) Bind(req *http.Request, obj interface{}) error {
|
||||||
if err := decoder.Decode(obj); err != nil {
|
if err := decoder.Decode(obj); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := _validator.ValidateStruct(obj); err != nil {
|
return Validate(obj)
|
||||||
return error(err)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,9 @@ func (c *Context) reset() {
|
||||||
|
|
||||||
func (c *Context) Copy() *Context {
|
func (c *Context) Copy() *Context {
|
||||||
var cp Context = *c
|
var cp Context = *c
|
||||||
|
cp.writermem.ResponseWriter = nil
|
||||||
|
cp.Writer = &cp.writermem
|
||||||
|
cp.Input.context = &cp
|
||||||
cp.index = AbortIndex
|
cp.index = AbortIndex
|
||||||
cp.handlers = nil
|
cp.handlers = nil
|
||||||
return &cp
|
return &cp
|
||||||
|
@ -161,7 +164,7 @@ func (c *Context) MustGet(key string) interface{} {
|
||||||
if value, exists := c.Get(key); exists {
|
if value, exists := c.Get(key); exists {
|
||||||
return value
|
return value
|
||||||
} else {
|
} else {
|
||||||
panic("Key " + key + " does not exist")
|
panic("Key \"" + key + "\" does not exist")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,11 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Unit tes TODO
|
||||||
|
// func (c *Context) File(filepath string) {
|
||||||
|
// func (c *Context) Negotiate(code int, config Negotiate) {
|
||||||
|
// BAD case: func (c *Context) Render(code int, render render.Render, obj ...interface{}) {
|
||||||
|
|
||||||
func createTestContext() (c *Context, w *httptest.ResponseRecorder, r *Engine) {
|
func createTestContext() (c *Context, w *httptest.ResponseRecorder, r *Engine) {
|
||||||
w = httptest.NewRecorder()
|
w = httptest.NewRecorder()
|
||||||
r = New()
|
r = New()
|
||||||
|
@ -64,6 +69,25 @@ func TestContextSetGet(t *testing.T) {
|
||||||
assert.Panics(t, func() { c.MustGet("no_exist") })
|
assert.Panics(t, func() { c.MustGet("no_exist") })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestContextCopy(t *testing.T) {
|
||||||
|
c, _, _ := createTestContext()
|
||||||
|
c.index = 2
|
||||||
|
c.Request, _ = http.NewRequest("POST", "/hola", nil)
|
||||||
|
c.handlers = []HandlerFunc{func(c *Context) {}}
|
||||||
|
c.Params = Params{Param{Key: "foo", Value: "bar"}}
|
||||||
|
c.Set("foo", "bar")
|
||||||
|
|
||||||
|
cp := c.Copy()
|
||||||
|
assert.Nil(t, cp.handlers)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
||||||
// 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) {
|
||||||
|
@ -79,7 +103,7 @@ func TestContextRenderJSON(t *testing.T) {
|
||||||
// and responds with Content-Type set to text/html
|
// and responds with Content-Type set to text/html
|
||||||
func TestContextRenderHTML(t *testing.T) {
|
func TestContextRenderHTML(t *testing.T) {
|
||||||
c, w, router := createTestContext()
|
c, w, router := createTestContext()
|
||||||
templ, _ := template.New("t").Parse(`Hello {{.name}}`)
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
router.SetHTMLTemplate(templ)
|
router.SetHTMLTemplate(templ)
|
||||||
|
|
||||||
c.HTML(201, "t", H{"name": "alexandernyquist"})
|
c.HTML(201, "t", H{"name": "alexandernyquist"})
|
||||||
|
@ -160,6 +184,7 @@ func TestContextNegotiationFormat(t *testing.T) {
|
||||||
c, _, _ := createTestContext()
|
c, _, _ := createTestContext()
|
||||||
c.Request, _ = http.NewRequest("POST", "", nil)
|
c.Request, _ = http.NewRequest("POST", "", nil)
|
||||||
|
|
||||||
|
assert.Panics(t, func() { c.NegotiateFormat() })
|
||||||
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON)
|
assert.Equal(t, c.NegotiateFormat(MIMEJSON, MIMEXML), MIMEJSON)
|
||||||
assert.Equal(t, c.NegotiateFormat(MIMEHTML, MIMEJSON), MIMEHTML)
|
assert.Equal(t, c.NegotiateFormat(MIMEHTML, MIMEJSON), MIMEHTML)
|
||||||
}
|
}
|
||||||
|
@ -203,13 +228,19 @@ func TestContextAbortWithStatus(t *testing.T) {
|
||||||
|
|
||||||
func TestContextError(t *testing.T) {
|
func TestContextError(t *testing.T) {
|
||||||
c, _, _ := createTestContext()
|
c, _, _ := createTestContext()
|
||||||
|
assert.Nil(t, c.LastError())
|
||||||
|
assert.Empty(t, c.Errors.String())
|
||||||
|
|
||||||
c.Error(errors.New("first error"), "some data")
|
c.Error(errors.New("first error"), "some data")
|
||||||
assert.Equal(t, c.LastError().Error(), "first error")
|
assert.Equal(t, c.LastError().Error(), "first error")
|
||||||
assert.Len(t, c.Errors, 1)
|
assert.Len(t, c.Errors, 1)
|
||||||
|
assert.Equal(t, c.Errors.String(), "Error #01: first error\n Meta: some data\n")
|
||||||
|
|
||||||
c.Error(errors.New("second error"), "some data 2")
|
c.Error(errors.New("second error"), "some data 2")
|
||||||
assert.Equal(t, c.LastError().Error(), "second error")
|
assert.Equal(t, c.LastError().Error(), "second error")
|
||||||
assert.Len(t, c.Errors, 2)
|
assert.Len(t, c.Errors, 2)
|
||||||
|
assert.Equal(t, c.Errors.String(), "Error #01: first error\n Meta: some data\n"+
|
||||||
|
"Error #02: second error\n Meta: some data 2\n")
|
||||||
|
|
||||||
assert.Equal(t, c.Errors[0].Err, "first error")
|
assert.Equal(t, c.Errors[0].Err, "first error")
|
||||||
assert.Equal(t, c.Errors[0].Meta, "some data")
|
assert.Equal(t, c.Errors[0].Meta, "some data")
|
||||||
|
|
|
@ -10,6 +10,10 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// func debugRoute(httpMethod, absolutePath string, handlers []HandlerFunc) {
|
||||||
|
// func debugPrint(format string, values ...interface{}) {
|
||||||
|
|
||||||
func TestIsDebugging(t *testing.T) {
|
func TestIsDebugging(t *testing.T) {
|
||||||
SetMode(DebugMode)
|
SetMode(DebugMode)
|
||||||
assert.True(t, IsDebugging())
|
assert.True(t, IsDebugging())
|
||||||
|
|
|
@ -43,7 +43,7 @@ func (a errorMsgs) String() string {
|
||||||
}
|
}
|
||||||
var buffer bytes.Buffer
|
var buffer bytes.Buffer
|
||||||
for i, msg := range a {
|
for i, msg := range a {
|
||||||
text := fmt.Sprintf("Error #%02d: %s \n Meta: %v\n", (i + 1), msg.Err, msg.Meta)
|
text := fmt.Sprintf("Error #%02d: %s\n Meta: %v\n", (i + 1), msg.Err, msg.Meta)
|
||||||
buffer.WriteString(text)
|
buffer.WriteString(text)
|
||||||
}
|
}
|
||||||
return buffer.String()
|
return buffer.String()
|
||||||
|
|
|
@ -1,49 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/flosch/pongo2"
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/gin-gonic/gin/render"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
router := gin.Default()
|
|
||||||
router.HTMLRender = newPongoRender()
|
|
||||||
|
|
||||||
router.GET("/index", func(c *gin.Context) {
|
|
||||||
c.HTML(200, "index.html", gin.H{
|
|
||||||
"title": "Gin meets pongo2 !",
|
|
||||||
"name": c.Input.Get("name"),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
router.Run(":8080")
|
|
||||||
}
|
|
||||||
|
|
||||||
type pongoRender struct {
|
|
||||||
cache map[string]*pongo2.Template
|
|
||||||
}
|
|
||||||
|
|
||||||
func newPongoRender() *pongoRender {
|
|
||||||
return &pongoRender{map[string]*pongo2.Template{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *pongoRender) Render(w http.ResponseWriter, code int, data ...interface{}) error {
|
|
||||||
file := data[0].(string)
|
|
||||||
ctx := data[1].(pongo2.Context)
|
|
||||||
var t *pongo2.Template
|
|
||||||
|
|
||||||
if tmpl, ok := p.cache[file]; ok {
|
|
||||||
t = tmpl
|
|
||||||
} else {
|
|
||||||
tmpl, err := pongo2.FromFile(file)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
p.cache[file] = tmpl
|
|
||||||
t = tmpl
|
|
||||||
}
|
|
||||||
render.WriteHeader(w, code, "text/html")
|
|
||||||
return t.ExecuteWriter(ctx, w)
|
|
||||||
}
|
|
|
@ -1,12 +0,0 @@
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="ja">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>{{ title }}</title>
|
|
||||||
<meta name="keywords" content="">
|
|
||||||
<meta name="description" content="">
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
Hello {{ name }} !
|
|
||||||
</body>
|
|
||||||
</html>
|
|
7
gin.go
7
gin.go
|
@ -144,6 +144,13 @@ func (engine *Engine) handle(method, path string, handlers []HandlerFunc) {
|
||||||
if path[0] != '/' {
|
if path[0] != '/' {
|
||||||
panic("path must begin with '/'")
|
panic("path must begin with '/'")
|
||||||
}
|
}
|
||||||
|
if method == "" {
|
||||||
|
panic("HTTP method can not be empty")
|
||||||
|
}
|
||||||
|
if len(handlers) == 0 {
|
||||||
|
panic("there must be at least one handler")
|
||||||
|
}
|
||||||
|
|
||||||
root := engine.trees[method]
|
root := engine.trees[method]
|
||||||
if root == nil {
|
if root == nil {
|
||||||
root = new(node)
|
root = new(node)
|
||||||
|
|
12
gin_test.go
12
gin_test.go
|
@ -10,6 +10,12 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
// func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||||
|
// func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||||
|
// func (engine *Engine) Run(addr string) error {
|
||||||
|
// func (engine *Engine) RunTLS(addr string, cert string, key string) error {
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
SetMode(TestMode)
|
SetMode(TestMode)
|
||||||
}
|
}
|
||||||
|
@ -20,9 +26,9 @@ func TestCreateEngine(t *testing.T) {
|
||||||
assert.Equal(t, router.engine, router)
|
assert.Equal(t, router.engine, router)
|
||||||
assert.Empty(t, router.Handlers)
|
assert.Empty(t, router.Handlers)
|
||||||
|
|
||||||
// TODO
|
assert.Panics(t, func() { router.handle("", "/", []HandlerFunc{func(_ *Context) {}}) })
|
||||||
// assert.Equal(t, router.router.NotFound, router.handle404)
|
assert.Panics(t, func() { router.handle("GET", "", []HandlerFunc{func(_ *Context) {}}) })
|
||||||
// assert.Equal(t, router.router.MethodNotAllowed, router.handle405)
|
assert.Panics(t, func() { router.handle("GET", "/", []HandlerFunc{}) })
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateDefaultRouter(t *testing.T) {
|
func TestCreateDefaultRouter(t *testing.T) {
|
||||||
|
|
|
@ -0,0 +1,344 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
type route struct {
|
||||||
|
method string
|
||||||
|
path string
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://developer.github.com/v3/
|
||||||
|
var githubAPI = []route{
|
||||||
|
// OAuth Authorizations
|
||||||
|
{"GET", "/authorizations"},
|
||||||
|
{"GET", "/authorizations/:id"},
|
||||||
|
{"POST", "/authorizations"},
|
||||||
|
//{"PUT", "/authorizations/clients/:client_id"},
|
||||||
|
//{"PATCH", "/authorizations/:id"},
|
||||||
|
{"DELETE", "/authorizations/:id"},
|
||||||
|
{"GET", "/applications/:client_id/tokens/:access_token"},
|
||||||
|
{"DELETE", "/applications/:client_id/tokens"},
|
||||||
|
{"DELETE", "/applications/:client_id/tokens/:access_token"},
|
||||||
|
|
||||||
|
// Activity
|
||||||
|
{"GET", "/events"},
|
||||||
|
{"GET", "/repos/:owner/:repo/events"},
|
||||||
|
{"GET", "/networks/:owner/:repo/events"},
|
||||||
|
{"GET", "/orgs/:org/events"},
|
||||||
|
{"GET", "/users/:user/received_events"},
|
||||||
|
{"GET", "/users/:user/received_events/public"},
|
||||||
|
{"GET", "/users/:user/events"},
|
||||||
|
{"GET", "/users/:user/events/public"},
|
||||||
|
{"GET", "/users/:user/events/orgs/:org"},
|
||||||
|
{"GET", "/feeds"},
|
||||||
|
{"GET", "/notifications"},
|
||||||
|
{"GET", "/repos/:owner/:repo/notifications"},
|
||||||
|
{"PUT", "/notifications"},
|
||||||
|
{"PUT", "/repos/:owner/:repo/notifications"},
|
||||||
|
{"GET", "/notifications/threads/:id"},
|
||||||
|
//{"PATCH", "/notifications/threads/:id"},
|
||||||
|
{"GET", "/notifications/threads/:id/subscription"},
|
||||||
|
{"PUT", "/notifications/threads/:id/subscription"},
|
||||||
|
{"DELETE", "/notifications/threads/:id/subscription"},
|
||||||
|
{"GET", "/repos/:owner/:repo/stargazers"},
|
||||||
|
{"GET", "/users/:user/starred"},
|
||||||
|
{"GET", "/user/starred"},
|
||||||
|
{"GET", "/user/starred/:owner/:repo"},
|
||||||
|
{"PUT", "/user/starred/:owner/:repo"},
|
||||||
|
{"DELETE", "/user/starred/:owner/:repo"},
|
||||||
|
{"GET", "/repos/:owner/:repo/subscribers"},
|
||||||
|
{"GET", "/users/:user/subscriptions"},
|
||||||
|
{"GET", "/user/subscriptions"},
|
||||||
|
{"GET", "/repos/:owner/:repo/subscription"},
|
||||||
|
{"PUT", "/repos/:owner/:repo/subscription"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/subscription"},
|
||||||
|
{"GET", "/user/subscriptions/:owner/:repo"},
|
||||||
|
{"PUT", "/user/subscriptions/:owner/:repo"},
|
||||||
|
{"DELETE", "/user/subscriptions/:owner/:repo"},
|
||||||
|
|
||||||
|
// Gists
|
||||||
|
{"GET", "/users/:user/gists"},
|
||||||
|
{"GET", "/gists"},
|
||||||
|
//{"GET", "/gists/public"},
|
||||||
|
//{"GET", "/gists/starred"},
|
||||||
|
{"GET", "/gists/:id"},
|
||||||
|
{"POST", "/gists"},
|
||||||
|
//{"PATCH", "/gists/:id"},
|
||||||
|
{"PUT", "/gists/:id/star"},
|
||||||
|
{"DELETE", "/gists/:id/star"},
|
||||||
|
{"GET", "/gists/:id/star"},
|
||||||
|
{"POST", "/gists/:id/forks"},
|
||||||
|
{"DELETE", "/gists/:id"},
|
||||||
|
|
||||||
|
// Git Data
|
||||||
|
{"GET", "/repos/:owner/:repo/git/blobs/:sha"},
|
||||||
|
{"POST", "/repos/:owner/:repo/git/blobs"},
|
||||||
|
{"GET", "/repos/:owner/:repo/git/commits/:sha"},
|
||||||
|
{"POST", "/repos/:owner/:repo/git/commits"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/git/refs/*ref"},
|
||||||
|
{"GET", "/repos/:owner/:repo/git/refs"},
|
||||||
|
{"POST", "/repos/:owner/:repo/git/refs"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/git/refs/*ref"},
|
||||||
|
//{"DELETE", "/repos/:owner/:repo/git/refs/*ref"},
|
||||||
|
{"GET", "/repos/:owner/:repo/git/tags/:sha"},
|
||||||
|
{"POST", "/repos/:owner/:repo/git/tags"},
|
||||||
|
{"GET", "/repos/:owner/:repo/git/trees/:sha"},
|
||||||
|
{"POST", "/repos/:owner/:repo/git/trees"},
|
||||||
|
|
||||||
|
// Issues
|
||||||
|
{"GET", "/issues"},
|
||||||
|
{"GET", "/user/issues"},
|
||||||
|
{"GET", "/orgs/:org/issues"},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues"},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues/:number"},
|
||||||
|
{"POST", "/repos/:owner/:repo/issues"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/issues/:number"},
|
||||||
|
{"GET", "/repos/:owner/:repo/assignees"},
|
||||||
|
{"GET", "/repos/:owner/:repo/assignees/:assignee"},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues/:number/comments"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/issues/comments"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/issues/comments/:id"},
|
||||||
|
{"POST", "/repos/:owner/:repo/issues/:number/comments"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/issues/comments/:id"},
|
||||||
|
//{"DELETE", "/repos/:owner/:repo/issues/comments/:id"},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues/:number/events"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/issues/events"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/issues/events/:id"},
|
||||||
|
{"GET", "/repos/:owner/:repo/labels"},
|
||||||
|
{"GET", "/repos/:owner/:repo/labels/:name"},
|
||||||
|
{"POST", "/repos/:owner/:repo/labels"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/labels/:name"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/labels/:name"},
|
||||||
|
{"GET", "/repos/:owner/:repo/issues/:number/labels"},
|
||||||
|
{"POST", "/repos/:owner/:repo/issues/:number/labels"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/issues/:number/labels/:name"},
|
||||||
|
{"PUT", "/repos/:owner/:repo/issues/:number/labels"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/issues/:number/labels"},
|
||||||
|
{"GET", "/repos/:owner/:repo/milestones/:number/labels"},
|
||||||
|
{"GET", "/repos/:owner/:repo/milestones"},
|
||||||
|
{"GET", "/repos/:owner/:repo/milestones/:number"},
|
||||||
|
{"POST", "/repos/:owner/:repo/milestones"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/milestones/:number"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/milestones/:number"},
|
||||||
|
|
||||||
|
// Miscellaneous
|
||||||
|
{"GET", "/emojis"},
|
||||||
|
{"GET", "/gitignore/templates"},
|
||||||
|
{"GET", "/gitignore/templates/:name"},
|
||||||
|
{"POST", "/markdown"},
|
||||||
|
{"POST", "/markdown/raw"},
|
||||||
|
{"GET", "/meta"},
|
||||||
|
{"GET", "/rate_limit"},
|
||||||
|
|
||||||
|
// Organizations
|
||||||
|
{"GET", "/users/:user/orgs"},
|
||||||
|
{"GET", "/user/orgs"},
|
||||||
|
{"GET", "/orgs/:org"},
|
||||||
|
//{"PATCH", "/orgs/:org"},
|
||||||
|
{"GET", "/orgs/:org/members"},
|
||||||
|
{"GET", "/orgs/:org/members/:user"},
|
||||||
|
{"DELETE", "/orgs/:org/members/:user"},
|
||||||
|
{"GET", "/orgs/:org/public_members"},
|
||||||
|
{"GET", "/orgs/:org/public_members/:user"},
|
||||||
|
{"PUT", "/orgs/:org/public_members/:user"},
|
||||||
|
{"DELETE", "/orgs/:org/public_members/:user"},
|
||||||
|
{"GET", "/orgs/:org/teams"},
|
||||||
|
{"GET", "/teams/:id"},
|
||||||
|
{"POST", "/orgs/:org/teams"},
|
||||||
|
//{"PATCH", "/teams/:id"},
|
||||||
|
{"DELETE", "/teams/:id"},
|
||||||
|
{"GET", "/teams/:id/members"},
|
||||||
|
{"GET", "/teams/:id/members/:user"},
|
||||||
|
{"PUT", "/teams/:id/members/:user"},
|
||||||
|
{"DELETE", "/teams/:id/members/:user"},
|
||||||
|
{"GET", "/teams/:id/repos"},
|
||||||
|
{"GET", "/teams/:id/repos/:owner/:repo"},
|
||||||
|
{"PUT", "/teams/:id/repos/:owner/:repo"},
|
||||||
|
{"DELETE", "/teams/:id/repos/:owner/:repo"},
|
||||||
|
{"GET", "/user/teams"},
|
||||||
|
|
||||||
|
// Pull Requests
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls"},
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls/:number"},
|
||||||
|
{"POST", "/repos/:owner/:repo/pulls"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/pulls/:number"},
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls/:number/commits"},
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls/:number/files"},
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls/:number/merge"},
|
||||||
|
{"PUT", "/repos/:owner/:repo/pulls/:number/merge"},
|
||||||
|
{"GET", "/repos/:owner/:repo/pulls/:number/comments"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/pulls/comments"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/pulls/comments/:number"},
|
||||||
|
{"PUT", "/repos/:owner/:repo/pulls/:number/comments"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/pulls/comments/:number"},
|
||||||
|
//{"DELETE", "/repos/:owner/:repo/pulls/comments/:number"},
|
||||||
|
|
||||||
|
// Repositories
|
||||||
|
{"GET", "/user/repos"},
|
||||||
|
{"GET", "/users/:user/repos"},
|
||||||
|
{"GET", "/orgs/:org/repos"},
|
||||||
|
{"GET", "/repositories"},
|
||||||
|
{"POST", "/user/repos"},
|
||||||
|
{"POST", "/orgs/:org/repos"},
|
||||||
|
{"GET", "/repos/:owner/:repo"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo"},
|
||||||
|
{"GET", "/repos/:owner/:repo/contributors"},
|
||||||
|
{"GET", "/repos/:owner/:repo/languages"},
|
||||||
|
{"GET", "/repos/:owner/:repo/teams"},
|
||||||
|
{"GET", "/repos/:owner/:repo/tags"},
|
||||||
|
{"GET", "/repos/:owner/:repo/branches"},
|
||||||
|
{"GET", "/repos/:owner/:repo/branches/:branch"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo"},
|
||||||
|
{"GET", "/repos/:owner/:repo/collaborators"},
|
||||||
|
{"GET", "/repos/:owner/:repo/collaborators/:user"},
|
||||||
|
{"PUT", "/repos/:owner/:repo/collaborators/:user"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/collaborators/:user"},
|
||||||
|
{"GET", "/repos/:owner/:repo/comments"},
|
||||||
|
{"GET", "/repos/:owner/:repo/commits/:sha/comments"},
|
||||||
|
{"POST", "/repos/:owner/:repo/commits/:sha/comments"},
|
||||||
|
{"GET", "/repos/:owner/:repo/comments/:id"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/comments/:id"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/comments/:id"},
|
||||||
|
{"GET", "/repos/:owner/:repo/commits"},
|
||||||
|
{"GET", "/repos/:owner/:repo/commits/:sha"},
|
||||||
|
{"GET", "/repos/:owner/:repo/readme"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/contents/*path"},
|
||||||
|
//{"PUT", "/repos/:owner/:repo/contents/*path"},
|
||||||
|
//{"DELETE", "/repos/:owner/:repo/contents/*path"},
|
||||||
|
//{"GET", "/repos/:owner/:repo/:archive_format/:ref"},
|
||||||
|
{"GET", "/repos/:owner/:repo/keys"},
|
||||||
|
{"GET", "/repos/:owner/:repo/keys/:id"},
|
||||||
|
{"POST", "/repos/:owner/:repo/keys"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/keys/:id"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/keys/:id"},
|
||||||
|
{"GET", "/repos/:owner/:repo/downloads"},
|
||||||
|
{"GET", "/repos/:owner/:repo/downloads/:id"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/downloads/:id"},
|
||||||
|
{"GET", "/repos/:owner/:repo/forks"},
|
||||||
|
{"POST", "/repos/:owner/:repo/forks"},
|
||||||
|
{"GET", "/repos/:owner/:repo/hooks"},
|
||||||
|
{"GET", "/repos/:owner/:repo/hooks/:id"},
|
||||||
|
{"POST", "/repos/:owner/:repo/hooks"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/hooks/:id"},
|
||||||
|
{"POST", "/repos/:owner/:repo/hooks/:id/tests"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/hooks/:id"},
|
||||||
|
{"POST", "/repos/:owner/:repo/merges"},
|
||||||
|
{"GET", "/repos/:owner/:repo/releases"},
|
||||||
|
{"GET", "/repos/:owner/:repo/releases/:id"},
|
||||||
|
{"POST", "/repos/:owner/:repo/releases"},
|
||||||
|
//{"PATCH", "/repos/:owner/:repo/releases/:id"},
|
||||||
|
{"DELETE", "/repos/:owner/:repo/releases/:id"},
|
||||||
|
{"GET", "/repos/:owner/:repo/releases/:id/assets"},
|
||||||
|
{"GET", "/repos/:owner/:repo/stats/contributors"},
|
||||||
|
{"GET", "/repos/:owner/:repo/stats/commit_activity"},
|
||||||
|
{"GET", "/repos/:owner/:repo/stats/code_frequency"},
|
||||||
|
{"GET", "/repos/:owner/:repo/stats/participation"},
|
||||||
|
{"GET", "/repos/:owner/:repo/stats/punch_card"},
|
||||||
|
{"GET", "/repos/:owner/:repo/statuses/:ref"},
|
||||||
|
{"POST", "/repos/:owner/:repo/statuses/:ref"},
|
||||||
|
|
||||||
|
// Search
|
||||||
|
{"GET", "/search/repositories"},
|
||||||
|
{"GET", "/search/code"},
|
||||||
|
{"GET", "/search/issues"},
|
||||||
|
{"GET", "/search/users"},
|
||||||
|
{"GET", "/legacy/issues/search/:owner/:repository/:state/:keyword"},
|
||||||
|
{"GET", "/legacy/repos/search/:keyword"},
|
||||||
|
{"GET", "/legacy/user/search/:keyword"},
|
||||||
|
{"GET", "/legacy/user/email/:email"},
|
||||||
|
|
||||||
|
// Users
|
||||||
|
{"GET", "/users/:user"},
|
||||||
|
{"GET", "/user"},
|
||||||
|
//{"PATCH", "/user"},
|
||||||
|
{"GET", "/users"},
|
||||||
|
{"GET", "/user/emails"},
|
||||||
|
{"POST", "/user/emails"},
|
||||||
|
{"DELETE", "/user/emails"},
|
||||||
|
{"GET", "/users/:user/followers"},
|
||||||
|
{"GET", "/user/followers"},
|
||||||
|
{"GET", "/users/:user/following"},
|
||||||
|
{"GET", "/user/following"},
|
||||||
|
{"GET", "/user/following/:user"},
|
||||||
|
{"GET", "/users/:user/following/:target_user"},
|
||||||
|
{"PUT", "/user/following/:user"},
|
||||||
|
{"DELETE", "/user/following/:user"},
|
||||||
|
{"GET", "/users/:user/keys"},
|
||||||
|
{"GET", "/user/keys"},
|
||||||
|
{"GET", "/user/keys/:id"},
|
||||||
|
{"POST", "/user/keys"},
|
||||||
|
//{"PATCH", "/user/keys/:id"},
|
||||||
|
{"DELETE", "/user/keys/:id"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGithubAPI(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
|
||||||
|
for _, route := range githubAPI {
|
||||||
|
router.Handle(route.method, route.path, []HandlerFunc{func(c *Context) {
|
||||||
|
output := H{"status": "good"}
|
||||||
|
for _, param := range c.Params {
|
||||||
|
output[param.Key] = param.Value
|
||||||
|
}
|
||||||
|
c.JSON(200, output)
|
||||||
|
}})
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range githubAPI {
|
||||||
|
path, values := exampleFromPath(route.path)
|
||||||
|
w := performRequest(router, route.method, path)
|
||||||
|
|
||||||
|
// TEST
|
||||||
|
assert.Contains(t, w.Body.String(), "\"status\":\"good\"")
|
||||||
|
for _, value := range values {
|
||||||
|
str := fmt.Sprintf("\"%s\":\"%s\"", value.Key, value.Value)
|
||||||
|
assert.Contains(t, w.Body.String(), str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleFromPath(path string) (string, Params) {
|
||||||
|
output := new(bytes.Buffer)
|
||||||
|
params := make(Params, 0, 6)
|
||||||
|
start := -1
|
||||||
|
for i, c := range path {
|
||||||
|
if c == ':' {
|
||||||
|
start = i + 1
|
||||||
|
}
|
||||||
|
if start >= 0 {
|
||||||
|
if c == '/' {
|
||||||
|
value := fmt.Sprint(rand.Intn(100000))
|
||||||
|
params = append(params, Param{
|
||||||
|
Key: path[start:i],
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
output.WriteString(value)
|
||||||
|
output.WriteRune(c)
|
||||||
|
start = -1
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
output.WriteRune(c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if start >= 0 {
|
||||||
|
value := fmt.Sprint(rand.Intn(100000))
|
||||||
|
params = append(params, Param{
|
||||||
|
Key: path[start:len(path)],
|
||||||
|
Value: value,
|
||||||
|
})
|
||||||
|
output.WriteString(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
return output.String(), params
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
// 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 (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
// func (engine *Engine) LoadHTMLGlob(pattern string) {
|
||||||
|
// func (engine *Engine) LoadHTMLFiles(files ...string) {
|
||||||
|
// func (engine *Engine) Run(addr string) error {
|
||||||
|
// func (engine *Engine) RunTLS(addr string, cert string, key string) error {
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SetMode(TestMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestLogger(t *testing.T) {
|
||||||
|
buffer := new(bytes.Buffer)
|
||||||
|
router := New()
|
||||||
|
router.Use(LoggerWithFile(buffer))
|
||||||
|
router.GET("/example", func(c *Context) {})
|
||||||
|
|
||||||
|
performRequest(router, "GET", "/example")
|
||||||
|
|
||||||
|
assert.Contains(t, buffer.String(), "200")
|
||||||
|
assert.Contains(t, buffer.String(), "GET")
|
||||||
|
assert.Contains(t, buffer.String(), "/example")
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||||
|
// Based on the path package, Copyright 2009 The Go Authors.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
|
// in the LICENSE file.
|
||||||
|
|
||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"runtime"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cleanTests = []struct {
|
||||||
|
path, result string
|
||||||
|
}{
|
||||||
|
// Already clean
|
||||||
|
{"/", "/"},
|
||||||
|
{"/abc", "/abc"},
|
||||||
|
{"/a/b/c", "/a/b/c"},
|
||||||
|
{"/abc/", "/abc/"},
|
||||||
|
{"/a/b/c/", "/a/b/c/"},
|
||||||
|
|
||||||
|
// missing root
|
||||||
|
{"", "/"},
|
||||||
|
{"abc", "/abc"},
|
||||||
|
{"abc/def", "/abc/def"},
|
||||||
|
{"a/b/c", "/a/b/c"},
|
||||||
|
|
||||||
|
// Remove doubled slash
|
||||||
|
{"//", "/"},
|
||||||
|
{"/abc//", "/abc/"},
|
||||||
|
{"/abc/def//", "/abc/def/"},
|
||||||
|
{"/a/b/c//", "/a/b/c/"},
|
||||||
|
{"/abc//def//ghi", "/abc/def/ghi"},
|
||||||
|
{"//abc", "/abc"},
|
||||||
|
{"///abc", "/abc"},
|
||||||
|
{"//abc//", "/abc/"},
|
||||||
|
|
||||||
|
// Remove . elements
|
||||||
|
{".", "/"},
|
||||||
|
{"./", "/"},
|
||||||
|
{"/abc/./def", "/abc/def"},
|
||||||
|
{"/./abc/def", "/abc/def"},
|
||||||
|
{"/abc/.", "/abc/"},
|
||||||
|
|
||||||
|
// Remove .. elements
|
||||||
|
{"..", "/"},
|
||||||
|
{"../", "/"},
|
||||||
|
{"../../", "/"},
|
||||||
|
{"../..", "/"},
|
||||||
|
{"../../abc", "/abc"},
|
||||||
|
{"/abc/def/ghi/../jkl", "/abc/def/jkl"},
|
||||||
|
{"/abc/def/../ghi/../jkl", "/abc/jkl"},
|
||||||
|
{"/abc/def/..", "/abc"},
|
||||||
|
{"/abc/def/../..", "/"},
|
||||||
|
{"/abc/def/../../..", "/"},
|
||||||
|
{"/abc/def/../../..", "/"},
|
||||||
|
{"/abc/def/../../../ghi/jkl/../../../mno", "/mno"},
|
||||||
|
|
||||||
|
// Combinations
|
||||||
|
{"abc/./../def", "/def"},
|
||||||
|
{"abc//./../def", "/def"},
|
||||||
|
{"abc/../../././../def", "/def"},
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathClean(t *testing.T) {
|
||||||
|
for _, test := range cleanTests {
|
||||||
|
assert.Equal(t, CleanPath(test.path), test.result)
|
||||||
|
assert.Equal(t, CleanPath(test.result), test.result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPathCleanMallocs(t *testing.T) {
|
||||||
|
if testing.Short() {
|
||||||
|
t.Skip("skipping malloc count in short mode")
|
||||||
|
}
|
||||||
|
if runtime.GOMAXPROCS(0) > 1 {
|
||||||
|
t.Log("skipping AllocsPerRun checks; GOMAXPROCS>1")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range cleanTests {
|
||||||
|
allocs := testing.AllocsPerRun(100, func() { CleanPath(test.result) })
|
||||||
|
assert.Equal(t, allocs, 0)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,79 @@
|
||||||
|
// 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 render
|
||||||
|
|
||||||
|
import (
|
||||||
|
"html/template"
|
||||||
|
"net/http/httptest"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRenderJSON(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
err := JSON.Render(w, 201, map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, w.Code, 201)
|
||||||
|
assert.Equal(t, w.Body.String(), "{\"foo\":\"bar\"}\n")
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "application/json; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderIndentedJSON(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
err := IndentedJSON.Render(w, 202, map[string]interface{}{
|
||||||
|
"foo": "bar",
|
||||||
|
"bar": "foo",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, w.Code, 202)
|
||||||
|
assert.Equal(t, w.Body.String(), "{\n \"bar\": \"foo\",\n \"foo\": \"bar\"\n}")
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "application/json; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderPlain(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
err := Plain.Render(w, 400, "hola %s %d", []interface{}{"manu", 2})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, w.Code, 400)
|
||||||
|
assert.Equal(t, w.Body.String(), "hola manu 2")
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "text/plain; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderPlainHTML(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
err := HTMLPlain.Render(w, 401, "hola %s %d", []interface{}{"manu", 2})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, w.Code, 401)
|
||||||
|
assert.Equal(t, w.Body.String(), "hola manu 2")
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRenderHTMLTemplate(t *testing.T) {
|
||||||
|
w := httptest.NewRecorder()
|
||||||
|
templ := template.Must(template.New("t").Parse(`Hello {{.name}}`))
|
||||||
|
htmlRender := HTMLRender{Template: templ}
|
||||||
|
err := htmlRender.Render(w, 402, "t", map[string]interface{}{
|
||||||
|
"name": "alexandernyquist",
|
||||||
|
})
|
||||||
|
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, w.Code, 402)
|
||||||
|
assert.Equal(t, w.Body.String(), "Hello alexandernyquist")
|
||||||
|
assert.Equal(t, w.Header().Get("Content-Type"), "text/html; charset=utf-8")
|
||||||
|
}
|
||||||
|
|
||||||
|
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")
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,11 @@ import (
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
// func (w *responseWriter) Hijack() (net.Conn, *bufio.ReadWriter, error) {
|
||||||
|
// func (w *responseWriter) CloseNotify() <-chan bool {
|
||||||
|
// func (w *responseWriter) Flush() {
|
||||||
|
|
||||||
var _ ResponseWriter = &responseWriter{}
|
var _ ResponseWriter = &responseWriter{}
|
||||||
var _ http.ResponseWriter = &responseWriter{}
|
var _ http.ResponseWriter = &responseWriter{}
|
||||||
var _ http.ResponseWriter = ResponseWriter(&responseWriter{})
|
var _ http.ResponseWriter = ResponseWriter(&responseWriter{})
|
||||||
|
|
|
@ -119,9 +119,10 @@ func (group *RouterGroup) createStaticHandler(absolutePath, root string) func(*C
|
||||||
|
|
||||||
func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
|
func (group *RouterGroup) combineHandlers(handlers []HandlerFunc) []HandlerFunc {
|
||||||
finalSize := len(group.Handlers) + len(handlers)
|
finalSize := len(group.Handlers) + len(handlers)
|
||||||
mergedHandlers := make([]HandlerFunc, 0, finalSize)
|
mergedHandlers := make([]HandlerFunc, finalSize)
|
||||||
mergedHandlers = append(mergedHandlers, group.Handlers...)
|
copy(mergedHandlers, group.Handlers)
|
||||||
return append(mergedHandlers, handlers...)
|
copy(mergedHandlers[len(group.Handlers):], handlers)
|
||||||
|
return mergedHandlers
|
||||||
}
|
}
|
||||||
|
|
||||||
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
|
func (group *RouterGroup) calculateAbsolutePath(relativePath string) string {
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
// 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 (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SetMode(TestMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterGroupBasic(t *testing.T) {
|
||||||
|
router := New()
|
||||||
|
group := router.Group("/hola", func(c *Context) {})
|
||||||
|
group.Use(func(c *Context) {})
|
||||||
|
|
||||||
|
assert.Len(t, group.Handlers, 2)
|
||||||
|
assert.Equal(t, group.absolutePath, "/hola")
|
||||||
|
assert.Equal(t, group.engine, router)
|
||||||
|
|
||||||
|
group2 := group.Group("manu")
|
||||||
|
group2.Use(func(c *Context) {}, func(c *Context) {})
|
||||||
|
|
||||||
|
assert.Len(t, group2.Handlers, 4)
|
||||||
|
assert.Equal(t, group2.absolutePath, "/hola/manu")
|
||||||
|
assert.Equal(t, group2.engine, router)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRouterGroupBasicHandle(t *testing.T) {
|
||||||
|
performRequestInGroup(t, "GET")
|
||||||
|
performRequestInGroup(t, "POST")
|
||||||
|
performRequestInGroup(t, "PUT")
|
||||||
|
performRequestInGroup(t, "PATCH")
|
||||||
|
performRequestInGroup(t, "DELETE")
|
||||||
|
performRequestInGroup(t, "HEAD")
|
||||||
|
performRequestInGroup(t, "OPTIONS")
|
||||||
|
performRequestInGroup(t, "LINK")
|
||||||
|
performRequestInGroup(t, "UNLINK")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func performRequestInGroup(t *testing.T, method string) {
|
||||||
|
router := New()
|
||||||
|
v1 := router.Group("v1", func(c *Context) {})
|
||||||
|
assert.Equal(t, v1.absolutePath, "/v1")
|
||||||
|
|
||||||
|
login := v1.Group("/login/", func(c *Context) {}, func(c *Context) {})
|
||||||
|
assert.Equal(t, login.absolutePath, "/v1/login/")
|
||||||
|
|
||||||
|
handler := func(c *Context) {
|
||||||
|
c.String(400, "the method was %s and index %d", c.Request.Method, c.index)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch method {
|
||||||
|
case "GET":
|
||||||
|
v1.GET("/test", handler)
|
||||||
|
login.GET("/test", handler)
|
||||||
|
case "POST":
|
||||||
|
v1.POST("/test", handler)
|
||||||
|
login.POST("/test", handler)
|
||||||
|
case "PUT":
|
||||||
|
v1.PUT("/test", handler)
|
||||||
|
login.PUT("/test", handler)
|
||||||
|
case "PATCH":
|
||||||
|
v1.PATCH("/test", handler)
|
||||||
|
login.PATCH("/test", handler)
|
||||||
|
case "DELETE":
|
||||||
|
v1.DELETE("/test", handler)
|
||||||
|
login.DELETE("/test", handler)
|
||||||
|
case "HEAD":
|
||||||
|
v1.HEAD("/test", handler)
|
||||||
|
login.HEAD("/test", handler)
|
||||||
|
case "OPTIONS":
|
||||||
|
v1.OPTIONS("/test", handler)
|
||||||
|
login.OPTIONS("/test", handler)
|
||||||
|
case "LINK":
|
||||||
|
v1.LINK("/test", handler)
|
||||||
|
login.LINK("/test", handler)
|
||||||
|
case "UNLINK":
|
||||||
|
v1.UNLINK("/test", handler)
|
||||||
|
login.UNLINK("/test", handler)
|
||||||
|
default:
|
||||||
|
panic("unknown method")
|
||||||
|
}
|
||||||
|
|
||||||
|
w := performRequest(router, method, "/v1/login/test")
|
||||||
|
assert.Equal(t, w.Code, 400)
|
||||||
|
assert.Equal(t, w.Body.String(), "the method was "+method+" and index 3")
|
||||||
|
|
||||||
|
w = performRequest(router, method, "/v1/test")
|
||||||
|
assert.Equal(t, w.Code, 400)
|
||||||
|
assert.Equal(t, w.Body.String(), "the method was "+method+" and index 1")
|
||||||
|
}
|
|
@ -0,0 +1,608 @@
|
||||||
|
// Copyright 2013 Julien Schmidt. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be found
|
||||||
|
// in the LICENSE file.
|
||||||
|
|
||||||
|
package gin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func printChildren(n *node, prefix string) {
|
||||||
|
fmt.Printf(" %02d:%02d %s%s[%d] %v %t %d \r\n", n.priority, n.maxParams, prefix, n.path, len(n.children), n.handlers, n.wildChild, n.nType)
|
||||||
|
for l := len(n.path); l > 0; l-- {
|
||||||
|
prefix += " "
|
||||||
|
}
|
||||||
|
for _, child := range n.children {
|
||||||
|
printChildren(child, prefix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Used as a workaround since we can't compare functions or their adresses
|
||||||
|
var fakeHandlerValue string
|
||||||
|
|
||||||
|
func fakeHandler(val string) []HandlerFunc {
|
||||||
|
return []HandlerFunc{func(c *Context) {
|
||||||
|
fakeHandlerValue = val
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
type testRequests []struct {
|
||||||
|
path string
|
||||||
|
nilHandler bool
|
||||||
|
route string
|
||||||
|
ps Params
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkRequests(t *testing.T, tree *node, requests testRequests) {
|
||||||
|
for _, request := range requests {
|
||||||
|
handler, ps, _ := tree.getValue(request.path, nil)
|
||||||
|
|
||||||
|
if handler == nil {
|
||||||
|
if !request.nilHandler {
|
||||||
|
t.Errorf("handle mismatch for route '%s': Expected non-nil handle", request.path)
|
||||||
|
}
|
||||||
|
} else if request.nilHandler {
|
||||||
|
t.Errorf("handle mismatch for route '%s': Expected nil handle", request.path)
|
||||||
|
} else {
|
||||||
|
handler[0](nil)
|
||||||
|
if fakeHandlerValue != request.route {
|
||||||
|
t.Errorf("handle mismatch for route '%s': Wrong handle (%s != %s)", request.path, fakeHandlerValue, request.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !reflect.DeepEqual(ps, request.ps) {
|
||||||
|
t.Errorf("Params mismatch for route '%s'", request.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPriorities(t *testing.T, n *node) uint32 {
|
||||||
|
var prio uint32
|
||||||
|
for i := range n.children {
|
||||||
|
prio += checkPriorities(t, n.children[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.handlers != nil {
|
||||||
|
prio++
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.priority != prio {
|
||||||
|
t.Errorf(
|
||||||
|
"priority mismatch for node '%s': is %d, should be %d",
|
||||||
|
n.path, n.priority, prio,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return prio
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkMaxParams(t *testing.T, n *node) uint8 {
|
||||||
|
var maxParams uint8
|
||||||
|
for i := range n.children {
|
||||||
|
params := checkMaxParams(t, n.children[i])
|
||||||
|
if params > maxParams {
|
||||||
|
maxParams = params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if n.nType != static && !n.wildChild {
|
||||||
|
maxParams++
|
||||||
|
}
|
||||||
|
|
||||||
|
if n.maxParams != maxParams {
|
||||||
|
t.Errorf(
|
||||||
|
"maxParams mismatch for node '%s': is %d, should be %d",
|
||||||
|
n.path, n.maxParams, maxParams,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxParams
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestCountParams(t *testing.T) {
|
||||||
|
if countParams("/path/:param1/static/*catch-all") != 2 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
if countParams(strings.Repeat("/:param", 256)) != 255 {
|
||||||
|
t.Fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeAddAndGet(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/hi",
|
||||||
|
"/contact",
|
||||||
|
"/co",
|
||||||
|
"/c",
|
||||||
|
"/a",
|
||||||
|
"/ab",
|
||||||
|
"/doc/",
|
||||||
|
"/doc/go_faq.html",
|
||||||
|
"/doc/go1.html",
|
||||||
|
"/α",
|
||||||
|
"/β",
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
tree.addRoute(route, fakeHandler(route))
|
||||||
|
}
|
||||||
|
|
||||||
|
//printChildren(tree, "")
|
||||||
|
|
||||||
|
checkRequests(t, tree, testRequests{
|
||||||
|
{"/a", false, "/a", nil},
|
||||||
|
{"/", true, "", nil},
|
||||||
|
{"/hi", false, "/hi", nil},
|
||||||
|
{"/contact", false, "/contact", nil},
|
||||||
|
{"/co", false, "/co", nil},
|
||||||
|
{"/con", true, "", nil}, // key mismatch
|
||||||
|
{"/cona", true, "", nil}, // key mismatch
|
||||||
|
{"/no", true, "", nil}, // no matching child
|
||||||
|
{"/ab", false, "/ab", nil},
|
||||||
|
{"/α", false, "/α", nil},
|
||||||
|
{"/β", false, "/β", nil},
|
||||||
|
})
|
||||||
|
|
||||||
|
checkPriorities(t, tree)
|
||||||
|
checkMaxParams(t, tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeWildcard(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/",
|
||||||
|
"/cmd/:tool/:sub",
|
||||||
|
"/cmd/:tool/",
|
||||||
|
"/src/*filepath",
|
||||||
|
"/search/",
|
||||||
|
"/search/:query",
|
||||||
|
"/user_:name",
|
||||||
|
"/user_:name/about",
|
||||||
|
"/files/:dir/*filepath",
|
||||||
|
"/doc/",
|
||||||
|
"/doc/go_faq.html",
|
||||||
|
"/doc/go1.html",
|
||||||
|
"/info/:user/public",
|
||||||
|
"/info/:user/project/:project",
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
tree.addRoute(route, fakeHandler(route))
|
||||||
|
}
|
||||||
|
|
||||||
|
//printChildren(tree, "")
|
||||||
|
|
||||||
|
checkRequests(t, tree, testRequests{
|
||||||
|
{"/", false, "/", nil},
|
||||||
|
{"/cmd/test/", false, "/cmd/:tool/", Params{Param{"tool", "test"}}},
|
||||||
|
{"/cmd/test", true, "", Params{Param{"tool", "test"}}},
|
||||||
|
{"/cmd/test/3", false, "/cmd/:tool/:sub", Params{Param{"tool", "test"}, Param{"sub", "3"}}},
|
||||||
|
{"/src/", false, "/src/*filepath", Params{Param{"filepath", "/"}}},
|
||||||
|
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
||||||
|
{"/search/", false, "/search/", nil},
|
||||||
|
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||||
|
{"/search/someth!ng+in+ünìcodé/", true, "", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||||
|
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
||||||
|
{"/user_gopher/about", false, "/user_:name/about", Params{Param{"name", "gopher"}}},
|
||||||
|
{"/files/js/inc/framework.js", false, "/files/:dir/*filepath", Params{Param{"dir", "js"}, Param{"filepath", "/inc/framework.js"}}},
|
||||||
|
{"/info/gordon/public", false, "/info/:user/public", Params{Param{"user", "gordon"}}},
|
||||||
|
{"/info/gordon/project/go", false, "/info/:user/project/:project", Params{Param{"user", "gordon"}, Param{"project", "go"}}},
|
||||||
|
})
|
||||||
|
|
||||||
|
checkPriorities(t, tree)
|
||||||
|
checkMaxParams(t, tree)
|
||||||
|
}
|
||||||
|
|
||||||
|
func catchPanic(testFunc func()) (recv interface{}) {
|
||||||
|
defer func() {
|
||||||
|
recv = recover()
|
||||||
|
}()
|
||||||
|
|
||||||
|
testFunc()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type testRoute struct {
|
||||||
|
path string
|
||||||
|
conflict bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func testRoutes(t *testing.T, routes []testRoute) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.addRoute(route.path, nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
if route.conflict {
|
||||||
|
if recv == nil {
|
||||||
|
t.Errorf("no panic for conflicting route '%s'", route.path)
|
||||||
|
}
|
||||||
|
} else if recv != nil {
|
||||||
|
t.Errorf("unexpected panic for route '%s': %v", route.path, recv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printChildren(tree, "")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeWildcardConflict(t *testing.T) {
|
||||||
|
routes := []testRoute{
|
||||||
|
{"/cmd/:tool/:sub", false},
|
||||||
|
{"/cmd/vet", true},
|
||||||
|
{"/src/*filepath", false},
|
||||||
|
{"/src/*filepathx", true},
|
||||||
|
{"/src/", true},
|
||||||
|
{"/src1/", false},
|
||||||
|
{"/src1/*filepath", true},
|
||||||
|
{"/src2*filepath", true},
|
||||||
|
{"/search/:query", false},
|
||||||
|
{"/search/invalid", true},
|
||||||
|
{"/user_:name", false},
|
||||||
|
{"/user_x", true},
|
||||||
|
{"/user_:name", false},
|
||||||
|
{"/id:id", false},
|
||||||
|
{"/id/:id", true},
|
||||||
|
}
|
||||||
|
testRoutes(t, routes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeChildConflict(t *testing.T) {
|
||||||
|
routes := []testRoute{
|
||||||
|
{"/cmd/vet", false},
|
||||||
|
{"/cmd/:tool/:sub", true},
|
||||||
|
{"/src/AUTHORS", false},
|
||||||
|
{"/src/*filepath", true},
|
||||||
|
{"/user_x", false},
|
||||||
|
{"/user_:name", true},
|
||||||
|
{"/id/:id", false},
|
||||||
|
{"/id:id", true},
|
||||||
|
{"/:id", true},
|
||||||
|
{"/*filepath", true},
|
||||||
|
}
|
||||||
|
testRoutes(t, routes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeDupliatePath(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/",
|
||||||
|
"/doc/",
|
||||||
|
"/src/*filepath",
|
||||||
|
"/search/:query",
|
||||||
|
"/user_:name",
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.addRoute(route, fakeHandler(route))
|
||||||
|
})
|
||||||
|
if recv != nil {
|
||||||
|
t.Fatalf("panic inserting route '%s': %v", route, recv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add again
|
||||||
|
recv = catchPanic(func() {
|
||||||
|
tree.addRoute(route, nil)
|
||||||
|
})
|
||||||
|
if recv == nil {
|
||||||
|
t.Fatalf("no panic while inserting duplicate route '%s", route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printChildren(tree, "")
|
||||||
|
|
||||||
|
checkRequests(t, tree, testRequests{
|
||||||
|
{"/", false, "/", nil},
|
||||||
|
{"/doc/", false, "/doc/", nil},
|
||||||
|
{"/src/some/file.png", false, "/src/*filepath", Params{Param{"filepath", "/some/file.png"}}},
|
||||||
|
{"/search/someth!ng+in+ünìcodé", false, "/search/:query", Params{Param{"query", "someth!ng+in+ünìcodé"}}},
|
||||||
|
{"/user_gopher", false, "/user_:name", Params{Param{"name", "gopher"}}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEmptyWildcardName(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/user:",
|
||||||
|
"/user:/",
|
||||||
|
"/cmd/:/",
|
||||||
|
"/src/*",
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.addRoute(route, nil)
|
||||||
|
})
|
||||||
|
if recv == nil {
|
||||||
|
t.Fatalf("no panic while inserting route with empty wildcard name '%s", route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeCatchAllConflict(t *testing.T) {
|
||||||
|
routes := []testRoute{
|
||||||
|
{"/src/*filepath/x", true},
|
||||||
|
{"/src2/", false},
|
||||||
|
{"/src2/*filepath/x", true},
|
||||||
|
}
|
||||||
|
testRoutes(t, routes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeCatchAllConflictRoot(t *testing.T) {
|
||||||
|
routes := []testRoute{
|
||||||
|
{"/", false},
|
||||||
|
{"/*filepath", true},
|
||||||
|
}
|
||||||
|
testRoutes(t, routes)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeDoubleWildcard(t *testing.T) {
|
||||||
|
const panicMsg = "only one wildcard per path segment is allowed"
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/:foo:bar",
|
||||||
|
"/:foo:bar/",
|
||||||
|
"/:foo*bar",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
tree := &node{}
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.addRoute(route, nil)
|
||||||
|
})
|
||||||
|
|
||||||
|
if rs, ok := recv.(string); !ok || rs != panicMsg {
|
||||||
|
t.Fatalf(`"Expected panic "%s" for route '%s', got "%v"`, panicMsg, route, recv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*func TestTreeDuplicateWildcard(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/:id/:name/:id",
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
}*/
|
||||||
|
|
||||||
|
func TestTreeTrailingSlashRedirect(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/hi",
|
||||||
|
"/b/",
|
||||||
|
"/search/:query",
|
||||||
|
"/cmd/:tool/",
|
||||||
|
"/src/*filepath",
|
||||||
|
"/x",
|
||||||
|
"/x/y",
|
||||||
|
"/y/",
|
||||||
|
"/y/z",
|
||||||
|
"/0/:id",
|
||||||
|
"/0/:id/1",
|
||||||
|
"/1/:id/",
|
||||||
|
"/1/:id/2",
|
||||||
|
"/aa",
|
||||||
|
"/a/",
|
||||||
|
"/doc",
|
||||||
|
"/doc/go_faq.html",
|
||||||
|
"/doc/go1.html",
|
||||||
|
"/no/a",
|
||||||
|
"/no/b",
|
||||||
|
"/api/hello/:name",
|
||||||
|
}
|
||||||
|
for _, route := range routes {
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.addRoute(route, fakeHandler(route))
|
||||||
|
})
|
||||||
|
if recv != nil {
|
||||||
|
t.Fatalf("panic inserting route '%s': %v", route, recv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//printChildren(tree, "")
|
||||||
|
|
||||||
|
tsrRoutes := [...]string{
|
||||||
|
"/hi/",
|
||||||
|
"/b",
|
||||||
|
"/search/gopher/",
|
||||||
|
"/cmd/vet",
|
||||||
|
"/src",
|
||||||
|
"/x/",
|
||||||
|
"/y",
|
||||||
|
"/0/go/",
|
||||||
|
"/1/go",
|
||||||
|
"/a",
|
||||||
|
"/doc/",
|
||||||
|
}
|
||||||
|
for _, route := range tsrRoutes {
|
||||||
|
handler, _, tsr := tree.getValue(route, nil)
|
||||||
|
if handler != nil {
|
||||||
|
t.Fatalf("non-nil handler for TSR route '%s", route)
|
||||||
|
} else if !tsr {
|
||||||
|
t.Errorf("expected TSR recommendation for route '%s'", route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
noTsrRoutes := [...]string{
|
||||||
|
"/",
|
||||||
|
"/no",
|
||||||
|
"/no/",
|
||||||
|
"/_",
|
||||||
|
"/_/",
|
||||||
|
"/api/world/abc",
|
||||||
|
}
|
||||||
|
for _, route := range noTsrRoutes {
|
||||||
|
handler, _, tsr := tree.getValue(route, nil)
|
||||||
|
if handler != nil {
|
||||||
|
t.Fatalf("non-nil handler for No-TSR route '%s", route)
|
||||||
|
} else if tsr {
|
||||||
|
t.Errorf("expected no TSR recommendation for route '%s'", route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeFindCaseInsensitivePath(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
|
||||||
|
routes := [...]string{
|
||||||
|
"/hi",
|
||||||
|
"/b/",
|
||||||
|
"/ABC/",
|
||||||
|
"/search/:query",
|
||||||
|
"/cmd/:tool/",
|
||||||
|
"/src/*filepath",
|
||||||
|
"/x",
|
||||||
|
"/x/y",
|
||||||
|
"/y/",
|
||||||
|
"/y/z",
|
||||||
|
"/0/:id",
|
||||||
|
"/0/:id/1",
|
||||||
|
"/1/:id/",
|
||||||
|
"/1/:id/2",
|
||||||
|
"/aa",
|
||||||
|
"/a/",
|
||||||
|
"/doc",
|
||||||
|
"/doc/go_faq.html",
|
||||||
|
"/doc/go1.html",
|
||||||
|
"/doc/go/away",
|
||||||
|
"/no/a",
|
||||||
|
"/no/b",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, route := range routes {
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.addRoute(route, fakeHandler(route))
|
||||||
|
})
|
||||||
|
if recv != nil {
|
||||||
|
t.Fatalf("panic inserting route '%s': %v", route, recv)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check out == in for all registered routes
|
||||||
|
// With fixTrailingSlash = true
|
||||||
|
for _, route := range routes {
|
||||||
|
out, found := tree.findCaseInsensitivePath(route, true)
|
||||||
|
if !found {
|
||||||
|
t.Errorf("Route '%s' not found!", route)
|
||||||
|
} else if string(out) != route {
|
||||||
|
t.Errorf("Wrong result for route '%s': %s", route, string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// With fixTrailingSlash = false
|
||||||
|
for _, route := range routes {
|
||||||
|
out, found := tree.findCaseInsensitivePath(route, false)
|
||||||
|
if !found {
|
||||||
|
t.Errorf("Route '%s' not found!", route)
|
||||||
|
} else if string(out) != route {
|
||||||
|
t.Errorf("Wrong result for route '%s': %s", route, string(out))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
in string
|
||||||
|
out string
|
||||||
|
found bool
|
||||||
|
slash bool
|
||||||
|
}{
|
||||||
|
{"/HI", "/hi", true, false},
|
||||||
|
{"/HI/", "/hi", true, true},
|
||||||
|
{"/B", "/b/", true, true},
|
||||||
|
{"/B/", "/b/", true, false},
|
||||||
|
{"/abc", "/ABC/", true, true},
|
||||||
|
{"/abc/", "/ABC/", true, false},
|
||||||
|
{"/aBc", "/ABC/", true, true},
|
||||||
|
{"/aBc/", "/ABC/", true, false},
|
||||||
|
{"/abC", "/ABC/", true, true},
|
||||||
|
{"/abC/", "/ABC/", true, false},
|
||||||
|
{"/SEARCH/QUERY", "/search/QUERY", true, false},
|
||||||
|
{"/SEARCH/QUERY/", "/search/QUERY", true, true},
|
||||||
|
{"/CMD/TOOL/", "/cmd/TOOL/", true, false},
|
||||||
|
{"/CMD/TOOL", "/cmd/TOOL/", true, true},
|
||||||
|
{"/SRC/FILE/PATH", "/src/FILE/PATH", true, false},
|
||||||
|
{"/x/Y", "/x/y", true, false},
|
||||||
|
{"/x/Y/", "/x/y", true, true},
|
||||||
|
{"/X/y", "/x/y", true, false},
|
||||||
|
{"/X/y/", "/x/y", true, true},
|
||||||
|
{"/X/Y", "/x/y", true, false},
|
||||||
|
{"/X/Y/", "/x/y", true, true},
|
||||||
|
{"/Y/", "/y/", true, false},
|
||||||
|
{"/Y", "/y/", true, true},
|
||||||
|
{"/Y/z", "/y/z", true, false},
|
||||||
|
{"/Y/z/", "/y/z", true, true},
|
||||||
|
{"/Y/Z", "/y/z", true, false},
|
||||||
|
{"/Y/Z/", "/y/z", true, true},
|
||||||
|
{"/y/Z", "/y/z", true, false},
|
||||||
|
{"/y/Z/", "/y/z", true, true},
|
||||||
|
{"/Aa", "/aa", true, false},
|
||||||
|
{"/Aa/", "/aa", true, true},
|
||||||
|
{"/AA", "/aa", true, false},
|
||||||
|
{"/AA/", "/aa", true, true},
|
||||||
|
{"/aA", "/aa", true, false},
|
||||||
|
{"/aA/", "/aa", true, true},
|
||||||
|
{"/A/", "/a/", true, false},
|
||||||
|
{"/A", "/a/", true, true},
|
||||||
|
{"/DOC", "/doc", true, false},
|
||||||
|
{"/DOC/", "/doc", true, true},
|
||||||
|
{"/NO", "", false, true},
|
||||||
|
{"/DOC/GO", "", false, true},
|
||||||
|
}
|
||||||
|
// With fixTrailingSlash = true
|
||||||
|
for _, test := range tests {
|
||||||
|
out, found := tree.findCaseInsensitivePath(test.in, true)
|
||||||
|
if found != test.found || (found && (string(out) != test.out)) {
|
||||||
|
t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t",
|
||||||
|
test.in, string(out), found, test.out, test.found)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// With fixTrailingSlash = false
|
||||||
|
for _, test := range tests {
|
||||||
|
out, found := tree.findCaseInsensitivePath(test.in, false)
|
||||||
|
if test.slash {
|
||||||
|
if found { // test needs a trailingSlash fix. It must not be found!
|
||||||
|
t.Errorf("Found without fixTrailingSlash: %s; got %s", test.in, string(out))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if found != test.found || (found && (string(out) != test.out)) {
|
||||||
|
t.Errorf("Wrong result for '%s': got %s, %t; want %s, %t",
|
||||||
|
test.in, string(out), found, test.out, test.found)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTreeInvalidNodeType(t *testing.T) {
|
||||||
|
tree := &node{}
|
||||||
|
tree.addRoute("/", fakeHandler("/"))
|
||||||
|
tree.addRoute("/:page", fakeHandler("/:page"))
|
||||||
|
|
||||||
|
// set invalid node type
|
||||||
|
tree.children[0].nType = 42
|
||||||
|
|
||||||
|
// normal lookup
|
||||||
|
recv := catchPanic(func() {
|
||||||
|
tree.getValue("/test", nil)
|
||||||
|
})
|
||||||
|
if rs, ok := recv.(string); !ok || rs != "Invalid node type" {
|
||||||
|
t.Fatalf(`Expected panic "Invalid node type", got "%v"`, recv)
|
||||||
|
}
|
||||||
|
|
||||||
|
// case-insensitive lookup
|
||||||
|
recv = catchPanic(func() {
|
||||||
|
tree.findCaseInsensitivePath("/test", true)
|
||||||
|
})
|
||||||
|
if rs, ok := recv.(string); !ok || rs != "Invalid node type" {
|
||||||
|
t.Fatalf(`Expected panic "Invalid node type", got "%v"`, recv)
|
||||||
|
}
|
||||||
|
}
|
|
@ -45,7 +45,17 @@ func TestFilterFlags(t *testing.T) {
|
||||||
assert.Equal(t, result, "text/html")
|
assert.Equal(t, result, "text/html")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestFunctionName(t *testing.T) {
|
||||||
|
assert.Equal(t, nameOfFunction(somefunction), "github.com/gin-gonic/gin.somefunction")
|
||||||
|
}
|
||||||
|
|
||||||
|
func somefunction() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func TestJoinPaths(t *testing.T) {
|
func TestJoinPaths(t *testing.T) {
|
||||||
|
assert.Equal(t, joinPaths("", ""), "")
|
||||||
|
assert.Equal(t, joinPaths("", "/"), "/")
|
||||||
assert.Equal(t, joinPaths("/a", ""), "/a")
|
assert.Equal(t, joinPaths("/a", ""), "/a")
|
||||||
assert.Equal(t, joinPaths("/a/", ""), "/a/")
|
assert.Equal(t, joinPaths("/a/", ""), "/a/")
|
||||||
assert.Equal(t, joinPaths("/a/", "/"), "/a/")
|
assert.Equal(t, joinPaths("/a/", "/"), "/a/")
|
||||||
|
|
Loading…
Reference in New Issue