Merge pull request #224 from fnproject/222

Fix route update procedure
This commit is contained in:
Denis Makogon
2017-08-18 01:47:01 +03:00
committed by GitHub
11 changed files with 258 additions and 120 deletions

View File

@@ -22,41 +22,97 @@ import (
update only update only
Patch accepts partial updates / skips validation of zero values. Patch accepts partial updates / skips validation of zero values.
*/ */
func (s *Server) handleRouteCreateOrUpdate(c *gin.Context) { func (s *Server) handleRoutesPostPutPatch(c *gin.Context) {
ctx := c.Request.Context() ctx := c.Request.Context()
method := strings.ToUpper(c.Request.Method) method := strings.ToUpper(c.Request.Method)
var wroute models.RouteWrapper var wroute models.RouteWrapper
err := bindRoute(c, method, &wroute)
err := s.bindAndValidate(c, method, &wroute)
if err != nil { if err != nil {
handleErrorResponse(c, err) handleErrorResponse(c, err)
return return
} }
if method != http.MethodPatch {
// Create the app if it does not exist. err = s.ensureApp(ctx, &wroute, method)
err = s.ensureApp(ctx, &wroute, method) if err != nil {
if err != nil { handleErrorResponse(c, err)
handleErrorResponse(c, err) return
return }
} }
resp, err := s.ensureRoute(ctx, method, &wroute)
resp, err := s.updateOrInsertRoute(ctx, method, wroute)
if err != nil { if err != nil {
handleErrorResponse(c, err) handleErrorResponse(c, err)
return return
} }
s.cachedelete(resp.Route.AppName, resp.Route.Path) s.cachedelete(resp.Route.AppName, resp.Route.Path)
c.JSON(http.StatusOK, resp) c.JSON(http.StatusOK, resp)
} }
func (s *Server) submitRoute(ctx context.Context, wroute *models.RouteWrapper) error {
err := s.setDefaultsAndValidate(wroute)
if err != nil {
return err
}
r, err := s.Datastore.InsertRoute(ctx, wroute.Route)
if err != nil {
return err
}
wroute.Route = r
return nil
}
func (s *Server) changeRoute(ctx context.Context, wroute *models.RouteWrapper) error {
err := wroute.Validate(true)
if err != nil {
return err
}
r, err := s.Datastore.UpdateRoute(ctx, wroute.Route)
if err != nil {
return err
}
wroute.Route = r
return nil
}
// ensureApp will only execute if it is on put
func (s *Server) ensureRoute(ctx context.Context, method string, wroute *models.RouteWrapper) (routeResponse, error) {
bad := new(routeResponse)
switch method {
case http.MethodPost:
err := s.submitRoute(ctx, wroute)
if err != nil {
return *bad, err
}
return routeResponse{"Route successfully created", wroute.Route}, nil
case http.MethodPut:
_, err := s.Datastore.GetRoute(ctx, wroute.Route.AppName, wroute.Route.Path)
if err != nil && err == models.ErrRoutesNotFound {
err := s.submitRoute(ctx, wroute)
if err != nil {
return *bad, err
}
return routeResponse{"Route successfully created", wroute.Route}, nil
} else {
err := s.changeRoute(ctx, wroute)
if err != nil {
return *bad, err
}
return routeResponse{"Route successfully updated", wroute.Route}, nil
}
case http.MethodPatch:
err := s.changeRoute(ctx, wroute)
if err != nil {
return *bad, err
}
return routeResponse{"Route successfully updated", wroute.Route}, nil
}
return *bad, nil
}
// ensureApp will only execute if it is on post or put. Patch is not allowed to create apps. // ensureApp will only execute if it is on post or put. Patch is not allowed to create apps.
func (s *Server) ensureApp(ctx context.Context, wroute *models.RouteWrapper, method string) error { func (s *Server) ensureApp(ctx context.Context, wroute *models.RouteWrapper, method string) error {
if !(method == http.MethodPost || method == http.MethodPut) {
return nil
}
app, err := s.Datastore.GetApp(ctx, wroute.Route.AppName) app, err := s.Datastore.GetApp(ctx, wroute.Route.AppName)
if err != nil && err != models.ErrAppsNotFound { if err != nil && err != models.ErrAppsNotFound {
return err return err
@@ -86,11 +142,8 @@ func (s *Server) ensureApp(ctx context.Context, wroute *models.RouteWrapper, met
return nil return nil
} }
/* bindAndValidate binds the RouteWrapper to the json from the request and validates that it is correct. // bindRoute binds the RouteWrapper to the json from the request.
If it is a put or patch it makes sure that the path in the url matches the provideed one in the body. func bindRoute(c *gin.Context, method string, wroute *models.RouteWrapper) error {
Defaults are set and if patch skipZero is true for validating the RouteWrapper
*/
func (s *Server) bindAndValidate(c *gin.Context, method string, wroute *models.RouteWrapper) error {
err := c.BindJSON(wroute) err := c.BindJSON(wroute)
if err != nil { if err != nil {
return models.ErrInvalidJSON return models.ErrInvalidJSON
@@ -109,33 +162,15 @@ func (s *Server) bindAndValidate(c *gin.Context, method string, wroute *models.R
} }
wroute.Route.Path = p wroute.Route.Path = p
} }
if method == http.MethodPost {
wroute.Route.SetDefaults() if wroute.Route.Path == "" {
return models.ErrRoutesValidationMissingPath
return wroute.Validate(method == http.MethodPatch)
}
// updateOrInsertRoute will either update or insert the route respective the method.
func (s *Server) updateOrInsertRoute(ctx context.Context, method string, wroute models.RouteWrapper) (routeResponse, error) {
var route *models.Route
var err error
resp := routeResponse{"Route successfully created", nil}
up := routeResponse{"Route successfully updated", nil}
switch method {
case http.MethodPost:
route, err = s.Datastore.InsertRoute(ctx, wroute.Route)
case http.MethodPut:
route, err = s.Datastore.UpdateRoute(ctx, wroute.Route)
if err == models.ErrRoutesNotFound {
// try insert then
route, err = s.Datastore.InsertRoute(ctx, wroute.Route)
} }
case http.MethodPatch:
// When patching if there is an error around the app we will return one and the update fails.
route, err = s.Datastore.UpdateRoute(ctx, wroute.Route)
resp = up
} }
resp.Route = route return nil
return resp, err }
func (s *Server) setDefaultsAndValidate(wroute *models.RouteWrapper) error {
wroute.Route.SetDefaults()
return wroute.Validate(false)
} }

View File

@@ -57,13 +57,13 @@ func TestRouteCreate(t *testing.T) {
for i, test := range []routeTestCase{ for i, test := range []routeTestCase{
// errors // errors
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "type": "sync" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "path": "/myroute", "type": "sync" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "path": "/myroute", "type": "sync" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "type": "sync" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingPath},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidPath}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "myroute", "type": "sync" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidPath},
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/$/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/$/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "sync" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName},
{datastore.NewMockInit(nil, {datastore.NewMockInit(nil,
[]*models.Route{ []*models.Route{
{ {
@@ -71,10 +71,10 @@ func TestRouteCreate(t *testing.T) {
Path: "/myroute", Path: "/myroute",
}, },
}, nil, nil, }, nil, nil,
), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusConflict, models.ErrRoutesAlreadyExists}, ), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "sync" } }`, http.StatusConflict, models.ErrRoutesAlreadyExists},
// success // success
{datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil}, {datastore.NewMock(), logs.NewMock(), http.MethodPost, "/v1/apps/a/routes", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "sync" } }`, http.StatusOK, nil},
} { } {
test.run(t, i, buf) test.run(t, i, buf)
} }
@@ -86,29 +86,20 @@ func TestRoutePut(t *testing.T) {
for i, test := range []routeTestCase{ for i, test := range []routeTestCase{
// errors (NOTE: this route doesn't exist yet) // errors (NOTE: this route doesn't exist yet)
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "path": "/myroute" }`, http.StatusBadRequest, models.ErrRoutesMissingNew}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "path": "/myroute", "type": "sync" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "type": "sync" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "path": "/myroute" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "path": "/myroute", "type": "sync" } }`, http.StatusBadRequest, models.ErrRoutesValidationMissingImage},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "myroute" } }`, http.StatusConflict, models.ErrRoutesPathImmutable}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "myroute", "type": "sync" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "diffRoute" } }`, http.StatusConflict, models.ErrRoutesPathImmutable}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "diffRoute", "type": "sync" } }`, http.StatusConflict, models.ErrRoutesPathImmutable},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/$/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/$/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "sync" } }`, http.StatusBadRequest, models.ErrAppsValidationInvalidName},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "invalid-type" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidType}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "invalid-type" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidType},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "format": "invalid-format" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidFormat}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "format": "invalid-format", "type": "sync" } }`, http.StatusBadRequest, models.ErrRoutesValidationInvalidFormat},
// success // success
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute" } }`, http.StatusOK, nil}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "path": "/myroute", "type": "sync" } }`, http.StatusOK, nil},
{datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello" } }`, http.StatusOK, nil}, {datastore.NewMock(), logs.NewMock(), http.MethodPut, "/v1/apps/a/routes/myroute", `{ "route": { "image": "funcy/hello", "type": "sync" } }`, http.StatusOK, nil},
} { } {
test.run(t, i, buf) test.run(t, i, buf)
test.ds = datastore.NewMockInit(nil,
[]*models.Route{
{
AppName: "a",
Path: "/myroute",
},
}, nil, nil,
)
test.run(t, i, buf)
} }
} }

View File

@@ -365,10 +365,10 @@ func (s *Server) bindHandlers(ctx context.Context) {
apps := v1.Group("/apps/:app") apps := v1.Group("/apps/:app")
{ {
apps.GET("/routes", s.handleRouteList) apps.GET("/routes", s.handleRouteList)
apps.POST("/routes", s.handleRouteCreateOrUpdate) apps.POST("/routes", s.handleRoutesPostPutPatch)
apps.GET("/routes/*route", s.handleRouteGet) apps.GET("/routes/*route", s.handleRouteGet)
apps.PATCH("/routes/*route", s.handleRouteCreateOrUpdate) apps.PATCH("/routes/*route", s.handleRoutesPostPutPatch)
apps.PUT("/routes/*route", s.handleRouteCreateOrUpdate) apps.PUT("/routes/*route", s.handleRoutesPostPutPatch)
apps.DELETE("/routes/*route", s.handleRouteDelete) apps.DELETE("/routes/*route", s.handleRouteDelete)
apps.GET("/calls", s.handleCallList) apps.GET("/calls", s.handleCallList)

View File

@@ -116,8 +116,8 @@ func TestFullStack(t *testing.T) {
{"list apps", "GET", "/v1/apps", ``, http.StatusOK, 0}, {"list apps", "GET", "/v1/apps", ``, http.StatusOK, 0},
{"get app", "GET", "/v1/apps/myapp", ``, http.StatusOK, 0}, {"get app", "GET", "/v1/apps/myapp", ``, http.StatusOK, 0},
// NOTE: cache is lazy, loads when a request comes in for the route, not when added // NOTE: cache is lazy, loads when a request comes in for the route, not when added
{"add myroute", "POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute", "path": "/myroute", "image": "funcy/hello" } }`, http.StatusOK, 0}, {"add myroute", "POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute", "path": "/myroute", "image": "funcy/hello", "type": "sync" } }`, http.StatusOK, 0},
{"add myroute2", "POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute2", "path": "/myroute2", "image": "funcy/error" } }`, http.StatusOK, 0}, {"add myroute2", "POST", "/v1/apps/myapp/routes", `{ "route": { "name": "myroute2", "path": "/myroute2", "image": "funcy/error", "type": "sync" } }`, http.StatusOK, 0},
{"get myroute", "GET", "/v1/apps/myapp/routes/myroute", ``, http.StatusOK, 0}, {"get myroute", "GET", "/v1/apps/myapp/routes/myroute", ``, http.StatusOK, 0},
{"get myroute2", "GET", "/v1/apps/myapp/routes/myroute2", ``, http.StatusOK, 0}, {"get myroute2", "GET", "/v1/apps/myapp/routes/myroute2", ``, http.StatusOK, 0},
{"get all routes", "GET", "/v1/apps/myapp/routes", ``, http.StatusOK, 0}, {"get all routes", "GET", "/v1/apps/myapp/routes", ``, http.StatusOK, 0},

View File

@@ -4,7 +4,6 @@ import (
"context" "context"
"strings" "strings"
"testing" "testing"
"time"
"github.com/funcy/functions_go/client" "github.com/funcy/functions_go/client"
"github.com/funcy/functions_go/client/apps" "github.com/funcy/functions_go/client/apps"
@@ -59,7 +58,7 @@ func CreateAppNoAssert(ctx context.Context, fnclient *client.Functions, appName
}, },
Context: ctx, Context: ctx,
} }
cfg.WithTimeout(time.Second * 60)
return fnclient.Apps.PostApps(cfg) return fnclient.Apps.PostApps(cfg)
} }
@@ -84,6 +83,7 @@ func CreateUpdateApp(t *testing.T, ctx context.Context, fnclient *client.Functio
}, },
Context: ctx, Context: ctx,
} }
appPayload, err := fnclient.Apps.PatchAppsApp(cfg) appPayload, err := fnclient.Apps.PatchAppsApp(cfg)
CheckAppResponseError(t, err) CheckAppResponseError(t, err)
return appPayload return appPayload
@@ -94,7 +94,18 @@ func DeleteApp(t *testing.T, ctx context.Context, fnclient *client.Functions, ap
App: appName, App: appName,
Context: ctx, Context: ctx,
} }
cfg.WithTimeout(time.Second * 60)
_, err := fnclient.Apps.DeleteAppsApp(cfg) _, err := fnclient.Apps.DeleteAppsApp(cfg)
CheckAppResponseError(t, err) CheckAppResponseError(t, err)
} }
func GetApp(t *testing.T, ctx context.Context, fnclient *client.Functions, appName string) *models.App {
cfg := &apps.GetAppsAppParams{
App: appName,
Context: ctx,
}
app, err := fnclient.Apps.GetAppsApp(cfg)
CheckAppResponseError(t, err)
return app.Payload.App
}

View File

@@ -41,14 +41,8 @@ func TestApps(t *testing.T) {
t.Parallel() t.Parallel()
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{"A": "a"})
cfg := &apps.GetAppsAppParams{ app := GetApp(t, s.Context, s.Client, s.AppName)
Context: s.Context, val, ok := app.Config["A"]
App: s.AppName,
}
appPayload, err := s.Client.Apps.GetAppsApp(cfg)
CheckAppResponseError(t, err)
appBody := appPayload.Payload.App
val, ok := appBody.Config["A"]
if !ok { if !ok {
t.Error("Error during app config inspect: config map misses required entity `A` with value `a`.") t.Error("Error during app config inspect: config map misses required entity `A` with value `a`.")
} }

View File

@@ -49,7 +49,7 @@ func TestCalls(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
cfg := &call.GetAppsAppCallsCallParams{ cfg := &call.GetAppsAppCallsCallParams{
Call: "dummy", Call: "dummy",
@@ -71,7 +71,7 @@ func TestCalls(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -106,7 +106,7 @@ func TestCalls(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",

View File

@@ -57,7 +57,7 @@ func TestRouteExecutions(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync", CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -84,7 +84,7 @@ func TestRouteExecutions(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync", CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -115,7 +115,7 @@ func TestRouteExecutions(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync", CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -141,7 +141,7 @@ func TestRouteExecutions(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync", CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "sync",
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -202,7 +202,7 @@ func TestRouteExecutions(t *testing.T) {
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType, CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -253,7 +253,7 @@ func TestRouteExecutions(t *testing.T) {
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType, CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -298,7 +298,7 @@ func TestRouteExecutions(t *testing.T) {
image := "denismakogon/os.environ" image := "denismakogon/os.environ"
routeType := "sync" routeType := "sync"
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType, CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -330,7 +330,7 @@ func TestRouteExecutions(t *testing.T) {
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType, CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
u := url.URL{ u := url.URL{
Scheme: "http", Scheme: "http",
@@ -372,7 +372,7 @@ func TestRouteExecutions(t *testing.T) {
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType, CreateRoute(t, s.Context, s.Client, s.AppName, routePath, image, routeType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
size := 1 * 1024 * 1024 * 1024 size := 1 * 1024 * 1024 * 1024
u := url.URL{ u := url.URL{

View File

@@ -3,7 +3,6 @@ package tests
import ( import (
"context" "context"
"testing" "testing"
"time"
"github.com/funcy/functions_go/client" "github.com/funcy/functions_go/client"
"github.com/funcy/functions_go/client/routes" "github.com/funcy/functions_go/client/routes"
@@ -55,19 +54,28 @@ func CheckRouteResponseError(t *testing.T, err error) {
msg := err.(*routes.PatchAppsAppRoutesRouteDefault).Payload.Error.Message msg := err.(*routes.PatchAppsAppRoutesRouteDefault).Payload.Error.Message
code := err.(*routes.PatchAppsAppRoutesRouteDefault).Code() code := err.(*routes.PatchAppsAppRoutesRouteDefault).Code()
t.Errorf("Unexpected error occurred: %v. Status code: %v", msg, code) t.Errorf("Unexpected error occurred: %v. Status code: %v", msg, code)
case *routes.PutAppsAppRoutesRouteBadRequest:
msg := err.(*routes.PutAppsAppRoutesRouteBadRequest).Payload.Error.Message
t.Errorf("Unexpected error occurred: %v.", msg)
case *routes.PutAppsAppRoutesRouteDefault:
msg := err.(*routes.PutAppsAppRoutesRouteDefault).Payload.Error.Message
code := err.(*routes.PutAppsAppRoutesRouteDefault).Code()
t.Errorf("Unexpected error occurred: %v. Status code: %v", msg, code)
default: default:
t.Errorf("Unable to determine type of error: %s", err) t.Errorf("Unable to determine type of error: %s", err)
} }
} }
} }
func assertRouteFields(t *testing.T, routeObject *models.Route, path, image, routeType string) { func assertRouteFields(t *testing.T, routeObject *models.Route, path, image, routeType, routeFormat string) {
rPath := routeObject.Path rPath := routeObject.Path
rImage := routeObject.Image rImage := routeObject.Image
rType := routeObject.Type rType := routeObject.Type
rTimeout := *routeObject.Timeout rTimeout := *routeObject.Timeout
rIdleTimeout := *routeObject.IDLETimeout rIdleTimeout := *routeObject.IDLETimeout
rFormat := routeObject.Format
if rPath != path { if rPath != path {
t.Errorf("Route path mismatch. Expected: %v. Actual: %v", path, rPath) t.Errorf("Route path mismatch. Expected: %v. Actual: %v", path, rPath)
} }
@@ -83,6 +91,9 @@ func assertRouteFields(t *testing.T, routeObject *models.Route, path, image, rou
if rIdleTimeout == 0 { if rIdleTimeout == 0 {
t.Error("Route idle timeout should have default value of 30 seconds, but got 0 seconds") t.Error("Route idle timeout should have default value of 30 seconds, but got 0 seconds")
} }
if rFormat != routeFormat {
t.Errorf("Route format mismatch. Expected: %v. Actual: %v", routeFormat, rFormat)
}
} }
@@ -100,16 +111,16 @@ func createRoute(ctx context.Context, fnclient *client.Functions, appName, image
}, },
Context: ctx, Context: ctx,
} }
cfg.WithTimeout(time.Second * 60)
return fnclient.Routes.PostAppsAppRoutes(cfg) return fnclient.Routes.PostAppsAppRoutes(cfg)
} }
func CreateRoute(t *testing.T, ctx context.Context, fnclient *client.Functions, appName, routePath, image, routeType string, routeConfig map[string]string, headers map[string][]string) { func CreateRoute(t *testing.T, ctx context.Context, fnclient *client.Functions, appName, routePath, image, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) {
routeResponse, err := createRoute(ctx, fnclient, appName, image, routePath, routeType, routeConfig, headers) routeResponse, err := createRoute(ctx, fnclient, appName, image, routePath, routeType, routeConfig, headers)
CheckRouteResponseError(t, err) CheckRouteResponseError(t, err)
assertRouteFields(t, routeResponse.Payload.Route, routePath, image, routeType) assertRouteFields(t, routeResponse.Payload.Route, routePath, image, routeType, routeFormat)
} }
func deleteRoute(ctx context.Context, fnclient *client.Functions, appName, routePath string) (*routes.DeleteAppsAppRoutesRouteOK, error) { func deleteRoute(ctx context.Context, fnclient *client.Functions, appName, routePath string) (*routes.DeleteAppsAppRoutesRouteOK, error) {
@@ -118,7 +129,7 @@ func deleteRoute(ctx context.Context, fnclient *client.Functions, appName, route
Route: routePath, Route: routePath,
Context: ctx, Context: ctx,
} }
cfg.WithTimeout(time.Second * 60)
return fnclient.Routes.DeleteAppsAppRoutesRoute(cfg) return fnclient.Routes.DeleteAppsAppRoutesRoute(cfg)
} }
@@ -132,7 +143,7 @@ func ListRoutes(t *testing.T, ctx context.Context, fnclient *client.Functions, a
App: appName, App: appName,
Context: ctx, Context: ctx,
} }
cfg.WithTimeout(time.Second * 60)
routesResponse, err := fnclient.Routes.GetAppsAppRoutes(cfg) routesResponse, err := fnclient.Routes.GetAppsAppRoutes(cfg)
CheckRouteResponseError(t, err) CheckRouteResponseError(t, err)
return routesResponse.Payload.Routes return routesResponse.Payload.Routes
@@ -144,7 +155,7 @@ func GetRoute(t *testing.T, ctx context.Context, fnclient *client.Functions, app
Route: routePath, Route: routePath,
Context: ctx, Context: ctx,
} }
cfg.WithTimeout(time.Second * 60)
routeResponse, err := fnclient.Routes.GetAppsAppRoutesRoute(cfg) routeResponse, err := fnclient.Routes.GetAppsAppRoutesRoute(cfg)
CheckRouteResponseError(t, err) CheckRouteResponseError(t, err)
return routeResponse.Payload.Route return routeResponse.Payload.Route
@@ -205,7 +216,6 @@ func UpdateRoute(t *testing.T, ctx context.Context, fnclient *client.Functions,
}, },
Route: routePath, Route: routePath,
} }
cfg.WithTimeout(time.Second * 60)
return fnclient.Routes.PatchAppsAppRoutesRoute(cfg) return fnclient.Routes.PatchAppsAppRoutesRoute(cfg)
} }
@@ -218,3 +228,25 @@ func assertContainsRoute(routeModels []*models.Route, expectedRoute string) bool
} }
return false return false
} }
func DeployRoute(t *testing.T, ctx context.Context, fnclient *client.Functions, appName, routePath, image, routeType, routeFormat string, routeConfig map[string]string, headers map[string][]string) *models.Route {
cfg := &routes.PutAppsAppRoutesRouteParams{
App: appName,
Context: ctx,
Route: routePath,
Body: &models.RouteWrapper{
Route: &models.Route{
Config: routeConfig,
Headers: headers,
Image: image,
Path: routePath,
Type: routeType,
Format: routeFormat,
},
},
}
route, err := fnclient.Routes.PutAppsAppRoutesRoute(cfg)
CheckRouteResponseError(t, err)
return route.Payload.Route
}

View File

@@ -11,13 +11,24 @@ import (
func TestRoutes(t *testing.T) { func TestRoutes(t *testing.T) {
newRouteType := "sync" newRouteType := "sync"
newRoutePath := id.New().String() newRoutePath := id.New().String()
t.Run("create-route-with-empty-type", func(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
_, err := createRoute(s.Context, s.Client, s.AppName, s.RoutePath, s.Image, "",
s.RouteConfig, s.RouteHeaders)
if err == nil {
t.Errorf("Should fail with Invalid route Type.")
}
DeleteApp(t, s.Context, s.Client, s.AppName)
})
t.Run("create-route", func(t *testing.T) { t.Run("create-route", func(t *testing.T) {
t.Parallel() t.Parallel()
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath) DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName) DeleteApp(t, s.Context, s.Client, s.AppName)
}) })
@@ -27,7 +38,7 @@ func TestRoutes(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
if !assertContainsRoute(ListRoutes(t, s.Context, s.Client, s.AppName), s.RoutePath) { if !assertContainsRoute(ListRoutes(t, s.Context, s.Client, s.AppName), s.RoutePath) {
t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath) t.Errorf("Unable to find corresponding route `%v` in list", s.RoutePath)
} }
@@ -40,7 +51,7 @@ func TestRoutes(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
rObjects := []*models.Route{GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)} rObjects := []*models.Route{GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)}
if !assertContainsRoute(rObjects, s.RoutePath) { if !assertContainsRoute(rObjects, s.RoutePath) {
@@ -56,7 +67,7 @@ func TestRoutes(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
routeResp, err := UpdateRoute( routeResp, err := UpdateRoute(
t, s.Context, s.Client, t, s.Context, s.Client,
@@ -65,7 +76,31 @@ func TestRoutes(t *testing.T) {
s.Memory, s.RouteConfig, s.RouteHeaders, "") s.Memory, s.RouteConfig, s.RouteHeaders, "")
CheckRouteResponseError(t, err) CheckRouteResponseError(t, err)
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, newRouteType) assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, newRouteType, s.Format)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
})
t.Run("patch-route-with-config", func(t *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.RouteConfig, s.RouteHeaders)
newRouteConf := map[string]string{
"A": "a",
}
routeResp, err := UpdateRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, s.RouteType, s.Format,
s.Memory, newRouteConf, s.RouteHeaders, "")
CheckRouteResponseError(t, err)
assertRouteFields(t, routeResp.Payload.Route, s.RoutePath, s.Image, s.RouteType, s.Format)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath) DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName) DeleteApp(t, s.Context, s.Client, s.AppName)
@@ -76,7 +111,7 @@ func TestRoutes(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
_, err := UpdateRoute( _, err := UpdateRoute(
t, s.Context, s.Client, t, s.Context, s.Client,
@@ -96,7 +131,7 @@ func TestRoutes(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
_, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath, newRouteType, s.RouteConfig, s.RouteHeaders) _, err := createRoute(s.Context, s.Client, s.AppName, s.Image, s.RoutePath, newRouteType, s.RouteConfig, s.RouteHeaders)
if err == nil { if err == nil {
@@ -112,7 +147,7 @@ func TestRoutes(t *testing.T) {
s := SetupDefaultSuite() s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{}) CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.RouteConfig, s.RouteHeaders) s.Format, s.RouteConfig, s.RouteHeaders)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath) DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName) DeleteApp(t, s.Context, s.Client, s.AppName)
@@ -129,4 +164,43 @@ func TestRoutes(t *testing.T) {
} }
DeleteApp(t, s.Context, s.Client, s.AppName) DeleteApp(t, s.Context, s.Client, s.AppName)
}) })
t.Run("deploy-route-without-existing-app", func(T *testing.T) {
t.Parallel()
s := SetupDefaultSuite()
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
GetApp(t, s.Context, s.Client, s.AppName)
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
})
t.Run("deploy-route-with-existing-app", func(T *testing.T) {
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
DeployRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType, s.Format, s.RouteConfig, s.RouteHeaders)
GetApp(t, s.Context, s.Client, s.AppName)
GetRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
})
t.Run("deploy-update-with-existing-route-and-app", func(T *testing.T) {
newRouteType := "sync"
s := SetupDefaultSuite()
CreateApp(t, s.Context, s.Client, s.AppName, map[string]string{})
CreateRoute(t, s.Context, s.Client, s.AppName, s.RoutePath, s.Image, s.RouteType,
s.Format, s.RouteConfig, s.RouteHeaders)
updatedRoute := DeployRoute(
t, s.Context, s.Client,
s.AppName, s.RoutePath,
s.Image, newRouteType,
s.Format, s.RouteConfig, s.RouteHeaders)
assertRouteFields(t, updatedRoute, s.RoutePath, s.Image, newRouteType, s.Format)
DeleteRoute(t, s.Context, s.Client, s.AppName, s.RoutePath)
DeleteApp(t, s.Context, s.Client, s.AppName)
})
} }

View File

@@ -118,8 +118,9 @@ func RandStringBytes(n int) string {
} }
func SetupDefaultSuite() *SuiteSetup { func SetupDefaultSuite() *SuiteSetup {
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
ss := &SuiteSetup{ ss := &SuiteSetup{
Context: context.Background(), Context: ctx,
Client: APIClient(), Client: APIClient(),
AppName: RandStringBytes(10), AppName: RandStringBytes(10),
RoutePath: "/" + RandStringBytes(10), RoutePath: "/" + RandStringBytes(10),
@@ -128,7 +129,7 @@ func SetupDefaultSuite() *SuiteSetup {
RouteType: "async", RouteType: "async",
RouteConfig: map[string]string{}, RouteConfig: map[string]string{},
RouteHeaders: map[string][]string{}, RouteHeaders: map[string][]string{},
Cancel: func() {}, Cancel: cancel,
} }
_, ok := ss.Client.Version.GetVersion(nil) _, ok := ss.Client.Version.GetVersion(nil)