echoswagger/wrapper_test.go

545 lines
15 KiB
Go

/*
* Copyright (c) 2019 Alex aka mailoman <alex@webz.asia>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @author Alex aka mailoman <alex@webz.asia>
* @copyright Copyright (c) 2019 Alex aka mailoman <alex@webz.asia>
* @since 18.12.2019
*
*/
package echoswagger
import (
"net/http"
"net/http/httptest"
"strconv"
"testing"
"time"
"github.com/labstack/echo/v4"
"github.com/stretchr/testify/assert"
)
func prepareApiRoot() ApiRoot {
r := New(echo.New(), "doc/", nil)
r.SetUI(UISetting{DetachSpec: true})
return r
}
func prepareApiGroup() ApiGroup {
r := prepareApiRoot()
return r.Group("G", "/g")
}
func prepareApi() Api {
g := prepareApiGroup()
var h func(e echo.Context) error
return g.POST("", h)
}
func TestNew(t *testing.T) {
tests := []struct {
echo *echo.Echo
docPath string
info *Info
expectPaths []string
panic bool
name string
}{
{
echo: echo.New(),
docPath: "doc/",
info: nil,
expectPaths: []string{"/doc/", "/doc/swagger.json"},
panic: false,
name: "Normal",
},
{
echo: echo.New(),
docPath: "doc",
info: &Info{
Title: "Test project",
Contact: &Contact{
URL: "https://git.webz.asia/echo-go/echoswagger",
},
},
expectPaths: []string{"/doc", "/doc/swagger.json"},
panic: false,
name: "Path slash suffix",
},
{
echo: nil,
panic: true,
name: "Panic",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.panic {
assert.Panics(t, func() {
New(tt.echo, tt.docPath, tt.info)
})
} else {
apiRoot := New(tt.echo, tt.docPath, tt.info)
assert.NotNil(t, apiRoot.(*Root))
r := apiRoot.(*Root)
assert.NotNil(t, r.spec)
if tt.info == nil {
assert.Equal(t, r.spec.Info.Title, "Project APIs")
} else {
assert.Equal(t, r.spec.Info, tt.info)
}
assert.NotNil(t, r.echo)
assert.Len(t, r.echo.Routes(), 2)
res := r.echo.Routes()
paths := []string{res[0].Path, res[1].Path}
assert.ElementsMatch(t, paths, tt.expectPaths)
}
})
}
}
func TestPath(t *testing.T) {
tests := []struct {
docInput string
docOutput, specOutput string
name string
}{
{
docInput: "doc/",
docOutput: "/doc/",
specOutput: "/doc/swagger.json",
name: "A",
}, {
docInput: "",
docOutput: "/",
specOutput: "/swagger.json",
name: "B",
}, {
docInput: "/doc",
docOutput: "/doc",
specOutput: "/doc/swagger.json",
name: "C",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
apiRoot := New(echo.New(), tt.docInput, nil)
r := apiRoot.(*Root)
assert.NotNil(t, r.echo)
assert.Len(t, r.echo.Routes(), 2)
res := r.echo.Routes()
paths := []string{res[0].Path, res[1].Path}
assert.ElementsMatch(t, paths, []string{tt.docOutput, tt.specOutput})
})
}
}
func TestGroup(t *testing.T) {
r := prepareApiRoot()
t.Run("Normal", func(t *testing.T) {
g := r.Group("Users", "users")
assert.Equal(t, g.(*group).defs, r.(*Root).defs)
})
t.Run("Invalid name", func(t *testing.T) {
assert.Panics(t, func() {
r.Group("", "")
})
})
t.Run("Repeat name", func(t *testing.T) {
ga := r.Group("Users", "users")
assert.Equal(t, ga.(*group).tag.Name, "Users_1")
gb := r.Group("Users", "users")
assert.Equal(t, gb.(*group).tag.Name, "Users_2")
})
}
func TestRouters(t *testing.T) {
r := prepareApiRoot()
var h echo.HandlerFunc
r.GET("/:id", h)
r.POST("/:id", h)
r.PUT("/:id", h)
r.DELETE("/:id", h)
r.OPTIONS("/:id", h)
r.HEAD("/:id", h)
r.PATCH("/:id", h)
assert.Len(t, r.(*Root).apis, 7)
g := prepareApiGroup()
g.GET("/:id", h)
g.POST("/:id", h)
g.PUT("/:id", h)
g.DELETE("/:id", h)
g.OPTIONS("/:id", h)
g.HEAD("/:id", h)
g.PATCH("/:id", h)
assert.Len(t, g.(*group).apis, 7)
}
func TestAddParam(t *testing.T) {
name := "name"
desc := "Param desc"
type nested struct {
Name string `json:"name" form:"name" query:"name"`
Enable bool `json:"-" form:"-" query:"-"`
}
t.Run("File", func(t *testing.T) {
a := prepareApi()
a.AddParamFile(name, desc, true)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, name)
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInFormData))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, desc)
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
assert.Equal(t, a.(*api).operation.Parameters[0].Type, "file")
})
t.Run("Path", func(t *testing.T) {
a := prepareApi()
a.AddParamPath(time.Now(), name, desc)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, name)
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInPath))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, desc)
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
assert.Equal(t, a.(*api).operation.Parameters[0].Type, "string")
a.AddParamPathNested(&nested{})
assert.Len(t, a.(*api).operation.Parameters, 2)
assert.Equal(t, a.(*api).operation.Parameters[1].Name, "name_")
assert.Equal(t, a.(*api).operation.Parameters[1].In, string(ParamInPath))
assert.Equal(t, a.(*api).operation.Parameters[1].Type, "string")
})
t.Run("Query", func(t *testing.T) {
a := prepareApi()
a.AddParamQuery(time.Now(), name, desc, true)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, name)
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInQuery))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, desc)
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
assert.Equal(t, a.(*api).operation.Parameters[0].Type, "string")
a.AddParamQueryNested(&nested{})
assert.Len(t, a.(*api).operation.Parameters, 2)
assert.Equal(t, a.(*api).operation.Parameters[1].Name, "name_")
assert.Equal(t, a.(*api).operation.Parameters[1].In, string(ParamInQuery))
assert.Equal(t, a.(*api).operation.Parameters[1].Type, "string")
})
t.Run("FormData", func(t *testing.T) {
a := prepareApi()
a.AddParamForm(time.Now(), name, desc, true)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, name)
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInFormData))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, desc)
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
assert.Equal(t, a.(*api).operation.Parameters[0].Type, "string")
a.AddParamFormNested(&nested{})
assert.Len(t, a.(*api).operation.Parameters, 2)
assert.Equal(t, a.(*api).operation.Parameters[1].Name, "name_")
assert.Equal(t, a.(*api).operation.Parameters[1].In, string(ParamInFormData))
assert.Equal(t, a.(*api).operation.Parameters[1].Type, "string")
})
t.Run("Header", func(t *testing.T) {
a := prepareApi()
a.AddParamHeader(time.Now(), name, desc, true)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, name)
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInHeader))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, desc)
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
assert.Equal(t, a.(*api).operation.Parameters[0].Type, "string")
a.AddParamHeaderNested(&nested{})
assert.Len(t, a.(*api).operation.Parameters, 2)
assert.Equal(t, a.(*api).operation.Parameters[1].Name, "name_")
assert.Equal(t, a.(*api).operation.Parameters[1].In, string(ParamInHeader))
assert.Equal(t, a.(*api).operation.Parameters[1].Type, "string")
})
}
func TestAddSchema(t *testing.T) {
type body struct {
Name string `json:"name"`
Enable bool `json:"-"`
}
t.Run("Multiple", func(t *testing.T) {
a := prepareApi()
a.AddParamBody(&body{}, "body", "body desc", true)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, "body")
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInBody))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, "body desc")
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
assert.Panics(t, func() {
a.AddParamBody(body{}, "body", "body desc", true)
})
})
t.Run("GetKey", func(t *testing.T) {
a := prepareApi()
a.AddParamBody(&body{}, "body", "body desc", true)
assert.Len(t, a.(*api).operation.Parameters, 1)
assert.Equal(t, a.(*api).operation.Parameters[0].Name, "body")
assert.Equal(t, a.(*api).operation.Parameters[0].In, string(ParamInBody))
assert.Equal(t, a.(*api).operation.Parameters[0].Description, "body desc")
assert.Equal(t, a.(*api).operation.Parameters[0].Required, true)
a.AddResponse(http.StatusOK, "response desc", body{}, nil)
ca := strconv.Itoa(http.StatusOK)
assert.Len(t, a.(*api).operation.Responses, 1)
assert.Equal(t, a.(*api).operation.Responses[ca].Description, "response desc")
assert.NotNil(t, a.(*api).defs)
da := a.(*api).defs
assert.Len(t, (*da), 1)
assert.NotNil(t, (*da)["body"])
a.AddResponse(http.StatusBadRequest, "response desc", body{Name: "name"}, nil)
cb := strconv.Itoa(http.StatusBadRequest)
assert.Len(t, a.(*api).operation.Responses, 2)
assert.Equal(t, a.(*api).operation.Responses[cb].Description, "response desc")
assert.NotNil(t, a.(*api).defs)
db := a.(*api).defs
assert.Len(t, (*db), 2)
assert.NotNil(t, (*db)["body"])
})
}
func TestAddResponse(t *testing.T) {
a := prepareApi()
a.AddResponse(http.StatusOK, "successful", nil, nil)
var f = func() {}
assert.Panics(t, func() {
a.AddResponse(http.StatusBadRequest, "bad request", f, nil)
})
assert.Panics(t, func() {
a.AddResponse(http.StatusBadRequest, "bad request", nil, time.Now())
})
}
func TestUI(t *testing.T) {
t.Run("DefaultCDN", func(t *testing.T) {
r := New(echo.New(), "doc/", nil)
se := r.(*Root)
req := httptest.NewRequest(echo.GET, "/doc/", nil)
rec := httptest.NewRecorder()
c := se.echo.NewContext(req, rec)
h := se.docHandler("doc/")
if assert.NoError(t, h(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), DefaultCDN)
}
})
t.Run("SetUI", func(t *testing.T) {
r := New(echo.New(), "doc/", nil)
se := r.(*Root)
req := httptest.NewRequest(echo.GET, "/doc/", nil)
rec := httptest.NewRecorder()
c := se.echo.NewContext(req, rec)
h := se.docHandler("doc/")
cdn := "https://cdnjs.cloudflare.com/ajax/libs/swagger-ui/3.18.0"
r.SetUI(UISetting{
HideTop: true,
CDN: cdn,
})
if assert.NoError(t, h(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.Contains(t, rec.Body.String(), cdn)
assert.Contains(t, rec.Body.String(), `{\x22swagger\x22:`)
assert.Contains(t, rec.Body.String(), "#swagger-ui>.swagger-container>.topbar")
}
r.SetUI(UISetting{
DetachSpec: true,
})
if assert.NoError(t, h(c)) {
assert.Equal(t, http.StatusOK, rec.Code)
assert.NotContains(t, rec.Body.String(), "{\x22swagger\x22:")
assert.Contains(t, rec.Body.String(), "#swagger-ui>.swagger-container>.topbar")
}
})
}
func TestScheme(t *testing.T) {
r := prepareApiRoot()
schemes := []string{"http", "https"}
r.SetScheme(schemes...)
assert.ElementsMatch(t, r.(*Root).spec.Schemes, schemes)
assert.Panics(t, func() {
r.SetScheme("grpc")
})
}
func TestRaw(t *testing.T) {
r := prepareApiRoot()
s := r.GetRaw()
assert.NotNil(t, s)
assert.NotNil(t, s.Info)
assert.Equal(t, s.Info.Version, "")
s.Info.Version = "1.0"
r.SetRaw(s)
assert.Equal(t, s.Info.Version, "1.0")
}
func TestContentType(t *testing.T) {
c := []string{"application/x-www-form-urlencoded", "multipart/form-data"}
p := []string{"application/vnd.github.v3+json", "application/vnd.github.v3.raw+json", "application/vnd.github.v3.text+json"}
t.Run("In Root", func(t *testing.T) {
r := prepareApiRoot()
r.SetRequestContentType(c...)
r.SetResponseContentType(p...)
assert.NotNil(t, r.(*Root))
assert.NotNil(t, r.(*Root).spec)
assert.Len(t, r.(*Root).spec.Consumes, 2)
assert.ElementsMatch(t, r.(*Root).spec.Consumes, c)
assert.Len(t, r.(*Root).spec.Produces, 3)
assert.ElementsMatch(t, r.(*Root).spec.Produces, p)
})
t.Run("In Api", func(t *testing.T) {
a := prepareApi()
a.SetRequestContentType(c...)
a.SetResponseContentType(p...)
assert.NotNil(t, a.(*api))
assert.Len(t, a.(*api).operation.Consumes, 2)
assert.ElementsMatch(t, a.(*api).operation.Consumes, c)
assert.Len(t, a.(*api).operation.Produces, 3)
assert.ElementsMatch(t, a.(*api).operation.Produces, p)
})
}
func TestOperationId(t *testing.T) {
id := "TestOperation"
a := prepareApi()
a.SetOperationId(id)
assert.Equal(t, a.(*api).operation.OperationID, id)
}
func TestDeprecated(t *testing.T) {
a := prepareApi()
a.SetDeprecated()
assert.Equal(t, a.(*api).operation.Deprecated, true)
}
func TestDescription(t *testing.T) {
d := "Test desc"
g := prepareApiGroup()
g.SetDescription(d)
assert.Equal(t, g.(*group).tag.Description, d)
a := prepareApi()
a.SetDescription(d)
assert.Equal(t, a.(*api).operation.Description, d)
}
func TestExternalDocs(t *testing.T) {
e := ExternalDocs{
Description: "Test desc",
URL: "http://127.0.0.1/",
}
r := prepareApiRoot()
r.SetExternalDocs(e.Description, e.URL)
assert.Equal(t, r.(*Root).spec.ExternalDocs, &e)
g := prepareApiGroup()
g.SetExternalDocs(e.Description, e.URL)
assert.Equal(t, g.(*group).tag.ExternalDocs, &e)
a := prepareApi()
a.SetExternalDocs(e.Description, e.URL)
assert.Equal(t, a.(*api).operation.ExternalDocs, &e)
}
func TestSummary(t *testing.T) {
s := "Test summary"
a := prepareApi()
a.SetSummary(s)
assert.Equal(t, a.(*api).operation.Summary, s)
}
func TestEcho(t *testing.T) {
r := prepareApiRoot()
assert.NotNil(t, r.Echo())
g := prepareApiGroup()
assert.NotNil(t, g.EchoGroup())
a := prepareApi()
assert.NotNil(t, a.Route())
}
func TestHandlers(t *testing.T) {
t.Run("ErrorGroupSecurity", func(t *testing.T) {
r := prepareApiRoot()
e := r.(*Root).echo
var h echo.HandlerFunc
r.Group("G", "/g").SetSecurity("JWT").GET("/", h)
req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).specHandler("/doc")(c)) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
}
if assert.NoError(t, r.(*Root).docHandler("/doc")(c)) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
}
})
t.Run("ErrorApiSecurity", func(t *testing.T) {
r := prepareApiRoot()
e := r.(*Root).echo
var h echo.HandlerFunc
r.GET("/", h).SetSecurity("JWT")
req := httptest.NewRequest(echo.GET, "/doc/swagger.json", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)
if assert.NoError(t, r.(*Root).specHandler("/doc")(c)) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
}
if assert.NoError(t, r.(*Root).docHandler("/doc")(c)) {
assert.Equal(t, http.StatusInternalServerError, rec.Code)
}
})
}