mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
Merge pull request #30 from pedronasser/router-tests
API endpoints test
This commit is contained in:
@@ -3,6 +3,7 @@ package models
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"path"
|
||||
|
||||
apiErrors "github.com/go-openapi/errors"
|
||||
)
|
||||
@@ -28,29 +29,34 @@ type Route struct {
|
||||
}
|
||||
|
||||
var (
|
||||
ErrRoutesValidationName = errors.New("Missing route Name")
|
||||
ErrRoutesValidationImage = errors.New("Missing route Image")
|
||||
ErrRoutesValidationAppName = errors.New("Missing route AppName")
|
||||
ErrRoutesValidationPath = errors.New("Missing route Path")
|
||||
ErrRoutesValidationMissingName = errors.New("Missing route Name")
|
||||
ErrRoutesValidationMissingImage = errors.New("Missing route Image")
|
||||
ErrRoutesValidationMissingAppName = errors.New("Missing route AppName")
|
||||
ErrRoutesValidationMissingPath = errors.New("Missing route Path")
|
||||
ErrRoutesValidationInvalidPath = errors.New("Invalid Path format")
|
||||
)
|
||||
|
||||
func (r *Route) Validate() error {
|
||||
var res []error
|
||||
|
||||
if r.Name == "" {
|
||||
res = append(res, ErrRoutesValidationAppName)
|
||||
res = append(res, ErrRoutesValidationMissingName)
|
||||
}
|
||||
|
||||
if r.Image == "" {
|
||||
res = append(res, ErrRoutesValidationImage)
|
||||
res = append(res, ErrRoutesValidationMissingImage)
|
||||
}
|
||||
|
||||
if r.AppName == "" {
|
||||
res = append(res, ErrRoutesValidationAppName)
|
||||
res = append(res, ErrRoutesValidationMissingAppName)
|
||||
}
|
||||
|
||||
if r.Path == "" {
|
||||
res = append(res, ErrRoutesValidationPath)
|
||||
res = append(res, ErrRoutesValidationMissingPath)
|
||||
}
|
||||
|
||||
if !path.IsAbs(r.Path) {
|
||||
res = append(res, ErrRoutesValidationInvalidPath)
|
||||
}
|
||||
|
||||
if len(res) > 0 {
|
||||
|
||||
42
api/server/datastore/mock.go
Normal file
42
api/server/datastore/mock.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package datastore
|
||||
|
||||
import "github.com/iron-io/functions/api/models"
|
||||
|
||||
type Mock struct {
|
||||
FakeApp *models.App
|
||||
FakeApps []*models.App
|
||||
FakeRoute *models.Route
|
||||
FakeRoutes []*models.Route
|
||||
}
|
||||
|
||||
func (m *Mock) GetApp(app string) (*models.App, error) {
|
||||
return m.FakeApp, nil
|
||||
}
|
||||
|
||||
func (m *Mock) GetApps(appFilter *models.AppFilter) ([]*models.App, error) {
|
||||
return m.FakeApps, nil
|
||||
}
|
||||
|
||||
func (m *Mock) StoreApp(app *models.App) (*models.App, error) {
|
||||
return m.FakeApp, nil
|
||||
}
|
||||
|
||||
func (m *Mock) RemoveApp(app string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Mock) GetRoute(app, route string) (*models.Route, error) {
|
||||
return m.FakeRoute, nil
|
||||
}
|
||||
|
||||
func (m *Mock) GetRoutes(routeFilter *models.RouteFilter) ([]*models.Route, error) {
|
||||
return m.FakeRoutes, nil
|
||||
}
|
||||
|
||||
func (m *Mock) StoreRoute(route *models.Route) (*models.Route, error) {
|
||||
return m.FakeRoute, nil
|
||||
}
|
||||
|
||||
func (m *Mock) RemoveRoute(app, route string) error {
|
||||
return nil
|
||||
}
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/iron-io/functions/api/models"
|
||||
)
|
||||
|
||||
func handleAppDestroy(c *gin.Context) {
|
||||
func handleAppDelete(c *gin.Context) {
|
||||
store := c.MustGet("store").(models.Datastore)
|
||||
log := c.MustGet("log").(logrus.FieldLogger)
|
||||
|
||||
@@ -27,18 +27,5 @@ func handleAppGet(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
filter := &models.RouteFilter{
|
||||
AppName: appName,
|
||||
}
|
||||
|
||||
routes, err := store.GetRoutes(filter)
|
||||
if err != nil {
|
||||
log.WithError(err).Error(models.ErrRoutesGet)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesGet))
|
||||
return
|
||||
}
|
||||
|
||||
app.Routes = routes
|
||||
|
||||
c.JSON(http.StatusOK, &models.AppWrapper{app})
|
||||
}
|
||||
|
||||
177
api/server/router/apps_test.go
Normal file
177
api/server/router/apps_test.go
Normal file
@@ -0,0 +1,177 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/iron-io/functions/api/models"
|
||||
"github.com/iron-io/functions/api/server/datastore"
|
||||
)
|
||||
|
||||
func TestAppCreate(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{"/v1/apps", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/v1/apps", `{}`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
||||
{"/v1/apps", `{ "name": "Test" }`, http.StatusBadRequest, models.ErrAppsMissingNew},
|
||||
{"/v1/apps", `{ "app": { "name": "" } }`, http.StatusInternalServerError, models.ErrAppsValidationMissingName},
|
||||
{"/v1/apps", `{ "app": { "name": "1234567890123456789012345678901" } }`, http.StatusInternalServerError, models.ErrAppsValidationTooLongName},
|
||||
{"/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
{"/v1/apps", `{ "app": { "name": "&&%@!#$#@$" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
|
||||
// success
|
||||
{"/v1/apps", `{ "app": { "name": "teste" } }`, http.StatusOK, nil},
|
||||
} {
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, router, "POST", test.path, body)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppDelete(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/v1/apps", "", http.StatusNotFound, nil},
|
||||
{"/v1/apps/myapp", "", http.StatusOK, nil},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "DELETE", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppList(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/v1/apps", "", http.StatusOK, nil},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppGet(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/v1/apps/myapp", "", http.StatusNotFound, nil},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppUpdate(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{"/v1/apps/myapp", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/v1/apps/myapp", `{ "name": "" }`, http.StatusInternalServerError, models.ErrAppsValidationMissingName},
|
||||
{"/v1/apps/myapp", `{ "name": "1234567890123456789012345678901" }`, http.StatusInternalServerError, models.ErrAppsValidationTooLongName},
|
||||
{"/v1/apps/myapp", `{ "name": "&&%@!#$#@$" }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
{"/v1/apps/myapp", `{ "name": "&&%@!#$#@$" }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
|
||||
// success
|
||||
{"/v1/apps/myapp", `{ "name": "teste" }`, http.StatusOK, nil},
|
||||
} {
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, router, "PUT", test.path, body)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,5 +21,25 @@ func handleAppUpdate(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if app == nil {
|
||||
log.Debug(models.ErrAppsMissingNew)
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsMissingNew))
|
||||
return
|
||||
}
|
||||
|
||||
if err := app.Validate(); err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(err))
|
||||
return
|
||||
}
|
||||
|
||||
// app, err := store.StoreApp(wapp.App)
|
||||
// if err != nil {
|
||||
// log.WithError(err).Debug(models.ErrAppsCreate)
|
||||
// c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
|
||||
// return
|
||||
// }
|
||||
|
||||
// Nothing to update right now in apps
|
||||
c.JSON(http.StatusOK, simpleError(models.ErrAppsNothingToUpdate))
|
||||
}
|
||||
|
||||
66
api/server/router/helpers.go
Normal file
66
api/server/router/helpers.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/iron-io/functions/api/models"
|
||||
"github.com/iron-io/functions/api/server/datastore"
|
||||
)
|
||||
|
||||
func testRouter(ds models.Datastore, config *models.Config) *gin.Engine {
|
||||
r := gin.Default()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("store", ds)
|
||||
c.Set("log", logrus.WithFields(logrus.Fields{}))
|
||||
c.Set("config", config)
|
||||
c.Next()
|
||||
})
|
||||
Start(r)
|
||||
return r
|
||||
}
|
||||
|
||||
func testRouterWithDefault() *gin.Engine {
|
||||
r := gin.Default()
|
||||
r.Use(func(c *gin.Context) {
|
||||
c.Set("store", &datastore.Mock{})
|
||||
c.Set("log", logrus.WithFields(logrus.Fields{}))
|
||||
c.Set("config", &models.Config{})
|
||||
c.Next()
|
||||
})
|
||||
Start(r)
|
||||
return r
|
||||
}
|
||||
|
||||
func routerRequest(t *testing.T, router *gin.Engine, method, path string, body io.Reader) (*http.Request, *httptest.ResponseRecorder) {
|
||||
req, err := http.NewRequest(method, "http://localhost:8080"+path, body)
|
||||
if err != nil {
|
||||
t.Fatalf("Test: Could not create %s request to %s: %v", method, path, err)
|
||||
}
|
||||
|
||||
rec := httptest.NewRecorder()
|
||||
router.ServeHTTP(rec, req)
|
||||
|
||||
return req, rec
|
||||
}
|
||||
|
||||
func getErrorResponse(t *testing.T, rec *httptest.ResponseRecorder) models.Error {
|
||||
respBody, err := ioutil.ReadAll(rec.Body)
|
||||
if err != nil {
|
||||
t.Error("Test: Expected not empty response body")
|
||||
}
|
||||
|
||||
var errResp models.Error
|
||||
err = json.Unmarshal(respBody, &errResp)
|
||||
if err != nil {
|
||||
t.Error("Test: Expected response body to be a valid models.Error object")
|
||||
}
|
||||
|
||||
return errResp
|
||||
}
|
||||
@@ -16,15 +16,15 @@ func Start(engine *gin.Engine) {
|
||||
|
||||
v1.GET("/apps/:app", handleAppGet)
|
||||
v1.PUT("/apps/:app", handleAppUpdate)
|
||||
v1.DELETE("/apps/:app", handleAppDestroy)
|
||||
v1.DELETE("/apps/:app", handleAppDelete)
|
||||
|
||||
apps := v1.Group("/apps/:app")
|
||||
{
|
||||
apps.GET("/routes", handleRouteList)
|
||||
apps.POST("/routes", handleRouteCreate)
|
||||
apps.GET("/routes/:route", handleRouteGet)
|
||||
apps.POST("/routes/:route", handleRouteUpdate)
|
||||
apps.DELETE("/routes/:route", handleRouteDestroy)
|
||||
apps.PUT("/routes/:route", handleRouteUpdate)
|
||||
apps.DELETE("/routes/:route", handleRouteDelete)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -12,37 +12,44 @@ func handleRouteCreate(c *gin.Context) {
|
||||
store := c.MustGet("store").(models.Datastore)
|
||||
log := c.MustGet("log").(logrus.FieldLogger)
|
||||
|
||||
route := &models.Route{}
|
||||
var wroute models.RouteWrapper
|
||||
|
||||
err := c.BindJSON(route)
|
||||
err := c.BindJSON(&wroute)
|
||||
if err != nil {
|
||||
log.WithError(err).Error(models.ErrInvalidJSON)
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
||||
return
|
||||
}
|
||||
|
||||
if route == nil {
|
||||
if wroute.Route == nil {
|
||||
log.WithError(err).Error(models.ErrInvalidJSON)
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrRoutesMissingNew))
|
||||
return
|
||||
}
|
||||
|
||||
route.AppName = c.Param("app")
|
||||
wroute.Route.AppName = c.Param("app")
|
||||
|
||||
if err := route.Validate(); err != nil {
|
||||
if err := wroute.Validate(); err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(err))
|
||||
return
|
||||
}
|
||||
|
||||
app, err := store.GetApp(route.AppName)
|
||||
app, err := store.GetApp(wroute.Route.AppName)
|
||||
if err != nil {
|
||||
log.WithError(err).Error(models.ErrAppsGet)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsGet))
|
||||
return
|
||||
}
|
||||
if app == nil {
|
||||
app, err = store.StoreApp(&models.App{Name: route.AppName})
|
||||
newapp := &models.App{Name: wroute.Route.AppName}
|
||||
if err := newapp.Validate(); err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(err))
|
||||
return
|
||||
}
|
||||
|
||||
app, err = store.StoreApp(newapp)
|
||||
if err != nil {
|
||||
log.WithError(err).Error(models.ErrAppsCreate)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrAppsCreate))
|
||||
@@ -50,7 +57,7 @@ func handleRouteCreate(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
route, err = store.StoreRoute(route)
|
||||
route, err := store.StoreRoute(wroute.Route)
|
||||
if err != nil {
|
||||
log.WithError(err).Error(models.ErrRoutesCreate)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesCreate))
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"github.com/iron-io/functions/api/models"
|
||||
)
|
||||
|
||||
func handleRouteDestroy(c *gin.Context) {
|
||||
func handleRouteDelete(c *gin.Context) {
|
||||
store := c.MustGet("store").(models.Datastore)
|
||||
log := c.MustGet("log").(logrus.FieldLogger)
|
||||
|
||||
@@ -22,6 +22,12 @@ func handleRouteGet(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
if route == nil {
|
||||
log.Error(models.ErrRoutesNotFound)
|
||||
c.JSON(http.StatusNotFound, simpleError(models.ErrRoutesNotFound))
|
||||
return
|
||||
}
|
||||
|
||||
log.WithFields(logrus.Fields{"route": route}).Debug("Got route")
|
||||
|
||||
c.JSON(http.StatusOK, &models.RouteWrapper{route})
|
||||
|
||||
178
api/server/router/routes_test.go
Normal file
178
api/server/router/routes_test.go
Normal file
@@ -0,0 +1,178 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/iron-io/functions/api/models"
|
||||
"github.com/iron-io/functions/api/server/datastore"
|
||||
)
|
||||
|
||||
func TestRouteCreate(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{"/v1/apps/a/routes", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/v1/apps/a/routes", `{ }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{"/v1/apps/a/routes", `{ "name": "Test" }`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{"/v1/apps/a/routes", `{ "route": { "name": "" } }`, http.StatusInternalServerError, models.ErrRoutesValidationMissingName},
|
||||
{"/v1/apps/a/routes", `{ "route": { "name": "myroute" } }`, http.StatusInternalServerError, models.ErrRoutesValidationMissingImage},
|
||||
{"/v1/apps/a/routes", `{ "route": { "name": "myroute", "image": "iron/hello" } }`, http.StatusInternalServerError, models.ErrRoutesValidationMissingPath},
|
||||
{"/v1/apps/a/routes", `{ "route": { "name": "myroute", "image": "iron/hello", "path": "myroute" } }`, http.StatusInternalServerError, models.ErrRoutesValidationInvalidPath},
|
||||
{"/v1/apps/$/routes", `{ "route": { "name": "myroute", "image": "iron/hello", "path": "/myroute" } }`, http.StatusInternalServerError, models.ErrAppsValidationInvalidName},
|
||||
|
||||
// success
|
||||
{"/v1/apps/a/routes", `{ "route": { "name": "myroute", "image": "iron/hello", "path": "/myroute" } }`, http.StatusOK, nil},
|
||||
} {
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, router, "POST", test.path, body)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteDelete(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/v1/apps/a/routes", "", http.StatusNotFound, nil},
|
||||
{"/v1/apps/a/routes/myroute", "", http.StatusOK, nil},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "DELETE", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteList(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/v1/apps/a/routes", "", http.StatusOK, nil},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteGet(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/v1/apps/a/routes/myroute", "", http.StatusNotFound, nil},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteUpdate(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
// errors
|
||||
{"/v1/apps/a/routes/myroute", ``, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/v1/apps/a/routes/myroute", `{}`, http.StatusBadRequest, models.ErrRoutesMissingNew},
|
||||
{"/v1/apps/a/routes/myroute", `{ "route": {} }`, http.StatusInternalServerError, models.ErrRoutesValidationMissingImage},
|
||||
{"/v1/apps/a/routes/myroute", `{ "route": { "image": "iron/hello" } }`, http.StatusInternalServerError, models.ErrRoutesValidationMissingPath},
|
||||
{"/v1/apps/a/routes/myroute", `{ "route": { "image": "iron/hello", "path": "myroute" } }`, http.StatusInternalServerError, models.ErrRoutesValidationInvalidPath},
|
||||
|
||||
// success
|
||||
{"/v1/apps/a/routes/myroute", `{ "route": { "image": "iron/hello", "path": "/myroute" } }`, http.StatusOK, nil},
|
||||
} {
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, router, "PUT", test.path, body)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,17 +12,29 @@ func handleRouteUpdate(c *gin.Context) {
|
||||
store := c.MustGet("store").(models.Datastore)
|
||||
log := c.MustGet("log").(logrus.FieldLogger)
|
||||
|
||||
wroute := &models.RouteWrapper{}
|
||||
appName := c.Param("app")
|
||||
var wroute models.RouteWrapper
|
||||
|
||||
err := c.BindJSON(wroute)
|
||||
err := c.BindJSON(&wroute)
|
||||
if err != nil {
|
||||
log.WithError(err).Debug(models.ErrInvalidJSON)
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
||||
return
|
||||
}
|
||||
|
||||
wroute.Route.AppName = appName
|
||||
if wroute.Route == nil {
|
||||
log.WithError(err).Error(models.ErrInvalidJSON)
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrRoutesMissingNew))
|
||||
return
|
||||
}
|
||||
|
||||
wroute.Route.AppName = c.Param("app")
|
||||
wroute.Route.Name = c.Param("route")
|
||||
|
||||
if err := wroute.Validate(); err != nil {
|
||||
log.Error(err)
|
||||
c.JSON(http.StatusInternalServerError, simpleError(err))
|
||||
return
|
||||
}
|
||||
|
||||
route, err := store.StoreRoute(wroute.Route)
|
||||
if err != nil {
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/iron-io/functions/api/models"
|
||||
@@ -13,9 +15,13 @@ import (
|
||||
)
|
||||
|
||||
func handleRunner(c *gin.Context) {
|
||||
if strings.HasPrefix(c.Request.URL.Path, "/v1") {
|
||||
c.Status(http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
log := c.MustGet("log").(logrus.FieldLogger)
|
||||
store := c.MustGet("store").(models.Datastore)
|
||||
config := c.MustGet("config").(*models.Config)
|
||||
|
||||
var err error
|
||||
|
||||
@@ -29,6 +35,15 @@ func handleRunner(c *gin.Context) {
|
||||
}
|
||||
}
|
||||
|
||||
if len(payload) > 0 {
|
||||
var emptyJSON map[string]interface{}
|
||||
if err := json.Unmarshal(payload, &emptyJSON); err != nil {
|
||||
log.WithError(err).Error(models.ErrInvalidJSON)
|
||||
c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.WithField("payload", string(payload)).Debug("Got payload")
|
||||
|
||||
appName := c.Param("app")
|
||||
@@ -55,16 +70,20 @@ func handleRunner(c *gin.Context) {
|
||||
c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesList))
|
||||
}
|
||||
|
||||
if routes == nil || len(routes) == 0 {
|
||||
log.WithError(err).Error(models.ErrRunnerRouteNotFound)
|
||||
c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
|
||||
}
|
||||
|
||||
log.WithField("routes", routes).Debug("Got routes from datastore")
|
||||
|
||||
for _, el := range routes {
|
||||
if el.Path == route {
|
||||
run := runner.New(&runner.Config{
|
||||
Ctx: c,
|
||||
Route: el,
|
||||
Endpoint: config.API,
|
||||
Payload: string(payload),
|
||||
Timeout: 30 * time.Second,
|
||||
Ctx: c,
|
||||
Route: el,
|
||||
Payload: string(payload),
|
||||
Timeout: 30 * time.Second,
|
||||
})
|
||||
|
||||
if err := run.Run(); err != nil {
|
||||
|
||||
112
api/server/router/runner_test.go
Normal file
112
api/server/router/runner_test.go
Normal file
@@ -0,0 +1,112 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/iron-io/functions/api/models"
|
||||
"github.com/iron-io/functions/api/server/datastore"
|
||||
)
|
||||
|
||||
func TestRouteRunnerGet(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/route", "", http.StatusNotFound, models.ErrRunnerRouteNotFound},
|
||||
{"/r/app/route", "", http.StatusNotFound, models.ErrRunnerRouteNotFound},
|
||||
{"/route?payload=test", "", http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/r/app/route?payload=test", "", http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
} {
|
||||
_, rec := routerRequest(t, router, "GET", test.path, nil)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRunnerPost(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedError error
|
||||
}{
|
||||
{"/route", `payload`, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/r/app/route", `payload`, http.StatusBadRequest, models.ErrInvalidJSON},
|
||||
{"/route", `{ "payload": "" }`, http.StatusNotFound, models.ErrRunnerRouteNotFound},
|
||||
{"/r/app/route", `{ "payload": "" }`, http.StatusNotFound, models.ErrRunnerRouteNotFound},
|
||||
} {
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, router, "POST", test.path, body)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedError != nil {
|
||||
resp := getErrorResponse(t, rec)
|
||||
|
||||
if !strings.Contains(resp.Error.Message, test.expectedError.Error()) {
|
||||
t.Errorf("Test %d: Expected error message to have `%s`",
|
||||
i, test.expectedError.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestRouteRunnerExecution(t *testing.T) {
|
||||
router := testRouter(&datastore.Mock{
|
||||
FakeRoutes: []*models.Route{
|
||||
{Path: "/myroute", Image: "iron/hello", Headers: map[string][]string{"X-Function": []string{"Test"}}},
|
||||
{Path: "/myerror", Image: "iron/error", Headers: map[string][]string{"X-Function": []string{"Test"}}},
|
||||
},
|
||||
}, &models.Config{})
|
||||
|
||||
for i, test := range []struct {
|
||||
path string
|
||||
body string
|
||||
expectedCode int
|
||||
expectedHeaders map[string][]string
|
||||
}{
|
||||
{"/r/myapp/myroute", ``, http.StatusOK, map[string][]string{"X-Function": []string{"Test"}}},
|
||||
{"/r/myapp/myerror", ``, http.StatusInternalServerError, map[string][]string{"X-Function": []string{"Test"}}},
|
||||
} {
|
||||
body := bytes.NewBuffer([]byte(test.body))
|
||||
_, rec := routerRequest(t, router, "GET", test.path, body)
|
||||
|
||||
if rec.Code != test.expectedCode {
|
||||
t.Errorf("Test %d: Expected status code to be %d but was %d",
|
||||
i, test.expectedCode, rec.Code)
|
||||
}
|
||||
|
||||
if test.expectedHeaders != nil {
|
||||
for name, header := range test.expectedHeaders {
|
||||
if header[0] != rec.Header().Get(name) {
|
||||
t.Errorf("Test %d: Expected header `%s` to be %s but was %s",
|
||||
i, name, header[0], rec.Header().Get(name))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package server
|
||||
package api
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
@@ -128,9 +128,8 @@ paths:
|
||||
|
||||
/apps/{app}/routes:
|
||||
post:
|
||||
summary: Enqueue Route
|
||||
description: |
|
||||
Enqueues route(s). If any of the routes is invalid, none of the routes are enqueued.
|
||||
summary: Create new Route
|
||||
description: Create a new route
|
||||
tags:
|
||||
- Routes
|
||||
parameters:
|
||||
|
||||
Reference in New Issue
Block a user