Datastore tests (#551)

* common datastore tests

* fix Datastore.UpdateApp

* remove extra datastore tests

* datastore test fixes
This commit is contained in:
Jordan Krage
2017-03-01 10:40:08 -06:00
committed by Travis Reeder
parent 56e43ac772
commit 3fd3da87f3
10 changed files with 852 additions and 773 deletions

View File

@@ -141,10 +141,7 @@ func (ds *BoltDatastore) UpdateApp(ctx context.Context, newapp *models.App) (*mo
return err
}
// Update app fields
if newapp.Config != nil {
app.Config = newapp.Config
}
app.UpdateConfig(newapp.Config)
buf, err := json.Marshal(app)
if err != nil {
@@ -252,7 +249,7 @@ func (ds *BoltDatastore) getRouteBucketForApp(tx *bolt.Tx, appName string) (*bol
func (ds *BoltDatastore) InsertRoute(ctx context.Context, route *models.Route) (*models.Route, error) {
if route == nil {
return nil, models.ErrDatastoreEmptyApp
return nil, models.ErrDatastoreEmptyRoute
}
if route.AppName == "" {
@@ -325,46 +322,15 @@ func (ds *BoltDatastore) UpdateRoute(ctx context.Context, newroute *models.Route
if err != nil {
return err
}
// Update route fields
if newroute.Image != "" {
route.Image = newroute.Image
}
if route.Memory != 0 {
route.Memory = newroute.Memory
}
if route.Type != "" {
route.Type = newroute.Type
}
if newroute.Timeout != 0 {
route.Timeout = newroute.Timeout
}
if newroute.Format != "" {
route.Format = newroute.Format
}
if newroute.MaxConcurrency != 0 {
route.MaxConcurrency = newroute.MaxConcurrency
}
if newroute.Headers != nil {
route.Headers = newroute.Headers
}
if newroute.Config != nil {
route.Config = newroute.Config
}
if err := route.Validate(); err != nil {
return err
}
route.Update(newroute)
buf, err := json.Marshal(route)
if err != nil {
return err
}
err = b.Put(routePath, buf)
if err != nil {
return err
}
return nil
return b.Put(routePath, buf)
})
if err != nil {
return nil, err
@@ -431,9 +397,9 @@ func (ds *BoltDatastore) GetRoute(ctx context.Context, appName, routePath string
func (ds *BoltDatastore) GetRoutesByApp(ctx context.Context, appName string, filter *models.RouteFilter) ([]*models.Route, error) {
res := []*models.Route{}
err := ds.db.View(func(tx *bolt.Tx) error {
b, err := ds.getRouteBucketForApp(tx, appName)
if err != nil {
return err
b := tx.Bucket(ds.routesBucket).Bucket([]byte(appName))
if b == nil {
return nil
}
i := 0

View File

@@ -0,0 +1,24 @@
package bolt
import (
"net/url"
"os"
"testing"
"github.com/iron-io/functions/api/datastore/internal/datastoretest"
)
const tmpBolt = "/tmp/func_test_bolt.db"
func TestDatastore(t *testing.T) {
os.Remove(tmpBolt)
u, err := url.Parse("bolt://" + tmpBolt)
if err != nil {
t.Fatalf("failed to parse url:", err)
}
ds, err := New(u)
if err != nil {
t.Fatalf("failed to create bolt datastore:", err)
}
datastoretest.Test(t, ds)
}

View File

@@ -1,286 +0,0 @@
package datastore
import (
"context"
"os"
"testing"
"github.com/iron-io/functions/api/models"
)
const tmpBolt = "/tmp/func_test_bolt.db"
func TestBolt(t *testing.T) {
buf := setLogBuffer()
ctx := context.Background()
os.Remove(tmpBolt)
ds, err := New("bolt://" + tmpBolt)
if err != nil {
t.Fatalf("Error when creating datastore: %v", err)
}
// Testing insert app
_, err = ds.InsertApp(ctx, nil)
if err != models.ErrDatastoreEmptyApp {
t.Log(buf.String())
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyApp, err)
}
_, err = ds.InsertApp(ctx, &models.App{})
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
_, err = ds.InsertApp(ctx, testApp)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test InsertApp: error when Bolt was storing new app: %s", err)
}
_, err = ds.InsertApp(ctx, testApp)
if err != models.ErrAppsAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertApp duplicated: expected error `%v`, but it was `%v`", models.ErrAppsAlreadyExists, err)
}
_, err = ds.UpdateApp(ctx, &models.App{
Name: testApp.Name,
Config: map[string]string{
"TEST": "1",
},
})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: error when Bolt was updating app: %v", err)
}
// Testing get app
_, err = ds.GetApp(ctx, "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected error to be %v, but it was %s", models.ErrDatastoreEmptyAppName, err)
}
app, err := ds.GetApp(ctx, testApp.Name)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApp: unexpected error: %s", err)
}
if app.Name != testApp.Name {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
}
// Testing list apps
apps, err := ds.GetApps(ctx, &models.AppFilter{})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApps: unexpected error %v", err)
}
if len(apps) == 0 {
t.Fatal("Test GetApps: expected result count to be greater than 0")
}
if apps[0].Name != testApp.Name {
t.Log(buf.String())
t.Fatalf("Test GetApps: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
}
// Testing app delete
err = ds.RemoveApp(ctx, "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
err = ds.RemoveApp(ctx, testApp.Name)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: error: %s", err)
}
app, err = ds.GetApp(ctx, testApp.Name)
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected error to be `%v`, but it was `%v`", models.ErrAppsNotFound, err)
}
if app != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: failed to remove the app")
}
// Test update inexistent app
_, err = ds.UpdateApp(ctx, &models.App{
Name: testApp.Name,
Config: map[string]string{
"TEST": "1",
},
})
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test UpdateApp inexistent: expected error to be %v, but it was %v", models.ErrAppsNotFound, err)
}
// Insert app again to test routes
ds.InsertApp(ctx, testApp)
// Testing insert route
_, err = ds.InsertRoute(ctx, nil)
if err == models.ErrDatastoreEmptyRoute {
t.Log(buf.String())
t.Fatalf("Test InsertRoute(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoute, err)
}
_, err = ds.InsertRoute(ctx, testRoute)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test InsertRoute: error when Bolt was storing new route: %s", err)
}
_, err = ds.InsertRoute(ctx, testRoute)
if err != models.ErrRoutesAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertRoute duplicated: expected error to be `%v`, but it was `%v`", models.ErrRoutesAlreadyExists, err)
}
_, err = ds.UpdateRoute(ctx, testRoute)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute: unexpected error: %v", err)
}
// Testing get
_, err = ds.GetRoute(ctx, "a", "")
if err != models.ErrDatastoreEmptyRoutePath {
t.Log(buf.String())
t.Fatalf("Test GetRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
}
_, err = ds.GetRoute(ctx, "", "a")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test GetRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
route, err := ds.GetRoute(ctx, testApp.Name, testRoute.Path)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoute: unexpected error %v", err)
}
if route.Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoute: expected `route.Path` to be `%s` but it was `%s`", route.Path, testRoute.Path)
}
// Testing list routes
routes, err := ds.GetRoutesByApp(ctx, testApp.Name, &models.RouteFilter{})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: unexpected error %v", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
}
if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
// Testing list routes
routes, err = ds.GetRoutes(ctx, &models.RouteFilter{Image: testRoute.Image})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: error: %s", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
}
if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
apps, err = ds.GetApps(ctx, &models.AppFilter{Name: "Tes%"})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApps(filter): unexpected error %v", err)
}
if len(apps) == 0 {
t.Fatal("Test GetApps(filter): expected result count to be greater than 0")
}
// Testing app delete
err = ds.RemoveRoute(ctx, "", "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test RemoveRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
err = ds.RemoveRoute(ctx, "a", "")
if err != models.ErrDatastoreEmptyRoutePath {
t.Log(buf.String())
t.Fatalf("Test RemoveRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
}
err = ds.RemoveRoute(ctx, testRoute.AppName, testRoute.Path)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: unexpected error: %v", err)
}
_, err = ds.UpdateRoute(ctx, &models.Route{
AppName: testRoute.AppName,
Path: testRoute.Path,
Image: "test",
})
if err != models.ErrRoutesNotFound {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute inexistent: expected error to be `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
}
route, err = ds.GetRoute(ctx, testRoute.AppName, testRoute.Path)
if err != models.ErrRoutesNotFound {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: failed to remove the route")
}
// Testing Put/Get
err = ds.Put(ctx, nil, nil)
if err != models.ErrDatastoreEmptyKey {
t.Log(buf.String())
t.Fatalf("Test Put(nil,nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyKey, err)
}
err = ds.Put(ctx, []byte("test"), []byte("success"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
val, err := ds.Get(ctx, []byte("test"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
if string(val) != "success" {
t.Log(buf.String())
t.Fatalf("Test Get: expected value to be `%v`, but it was `%v`", "success", string(val))
}
err = ds.Put(ctx, []byte("test"), nil)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
val, err = ds.Get(ctx, []byte("test"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
if string(val) != "" {
t.Log(buf.String())
t.Fatalf("Test Get: expected value to be `%v`, but it was `%v`", "", string(val))
}
}

View File

@@ -0,0 +1,477 @@
package datastoretest
import (
"bytes"
"context"
"log"
"testing"
"github.com/iron-io/functions/api/models"
"github.com/Sirupsen/logrus"
"github.com/gin-gonic/gin"
"reflect"
"net/http"
)
func setLogBuffer() *bytes.Buffer {
var buf bytes.Buffer
buf.WriteByte('\n')
logrus.SetOutput(&buf)
gin.DefaultErrorWriter = &buf
gin.DefaultWriter = &buf
log.SetOutput(&buf)
return &buf
}
func Test(t *testing.T, ds models.Datastore) {
buf := setLogBuffer()
ctx := context.Background()
t.Run("apps", func(t *testing.T) {
// Testing insert app
_, err := ds.InsertApp(ctx, nil)
if err != models.ErrDatastoreEmptyApp {
t.Log(buf.String())
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyApp, err)
}
_, err = ds.InsertApp(ctx, &models.App{})
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test InsertApp(&{}): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
inserted, err := ds.InsertApp(ctx, testApp)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test InsertApp: error when storing new app: %s", err)
}
if !reflect.DeepEqual(*inserted, *testApp) {
t.Log(buf.String())
t.Fatalf("Test InsertApp: expected to insert:\n%v\nbut got:\n%v", testApp, inserted)
}
_, err = ds.InsertApp(ctx, testApp)
if err != models.ErrAppsAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertApp duplicated: expected error `%v`, but it was `%v`", models.ErrAppsAlreadyExists, err)
}
{
// Set a config var
updated, err := ds.UpdateApp(ctx,
&models.App{Name: testApp.Name, Config: map[string]string{"TEST":"1"}})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: error when updating app: %v", err)
}
expected := &models.App{Name: testApp.Name, Config: map[string]string{"TEST":"1"}}
if !reflect.DeepEqual(*updated, *expected) {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: expected updated `%v` but got `%v`", expected, updated)
}
// Set a different var (without clearing the existing)
updated, err = ds.UpdateApp(ctx,
&models.App{Name: testApp.Name, Config: map[string]string{"OTHER":"TEST"}})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: error when updating app: %v", err)
}
expected = &models.App{Name: testApp.Name, Config: map[string]string{"TEST":"1","OTHER":"TEST"}}
if !reflect.DeepEqual(*updated, *expected) {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: expected updated `%v` but got `%v`", expected, updated)
}
// Delete a var
updated, err = ds.UpdateApp(ctx,
&models.App{Name: testApp.Name, Config: map[string]string{"TEST":""}})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: error when updating app: %v", err)
}
expected = &models.App{Name: testApp.Name, Config: map[string]string{"OTHER":"TEST"}}
if !reflect.DeepEqual(*updated, *expected) {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: expected updated `%v` but got `%v`", expected, updated)
}
}
// Testing get app
_, err = ds.GetApp(ctx, "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected error to be %v, but it was %s", models.ErrDatastoreEmptyAppName, err)
}
app, err := ds.GetApp(ctx, testApp.Name)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApp: error: %s", err)
}
if app.Name != testApp.Name {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
}
// Testing list apps
apps, err := ds.GetApps(ctx, &models.AppFilter{})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApps: unexpected error %v", err)
}
if len(apps) == 0 {
t.Fatal("Test GetApps: expected result count to be greater than 0")
}
if apps[0].Name != testApp.Name {
t.Log(buf.String())
t.Fatalf("Test GetApps: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
}
apps, err = ds.GetApps(ctx, &models.AppFilter{Name: "Tes%"})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApps(filter): unexpected error %v", err)
}
if len(apps) == 0 {
t.Fatal("Test GetApps(filter): expected result count to be greater than 0")
}
// Testing app delete
err = ds.RemoveApp(ctx, "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
err = ds.RemoveApp(ctx, testApp.Name)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: error: %s", err)
}
app, err = ds.GetApp(ctx, testApp.Name)
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test GetApp(removed): expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
}
if app != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: failed to remove the app")
}
// Test update inexistent app
_, err = ds.UpdateApp(ctx, &models.App{
Name: testApp.Name,
Config: map[string]string{
"TEST": "1",
},
})
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test UpdateApp(inexistent): expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
}
})
t.Run("routes", func(t *testing.T) {
// Insert app again to test routes
_, err := ds.InsertApp(ctx, testApp)
if err != nil && err != models.ErrAppsAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertRoute Prep: failed to insert app: ", err)
}
// Testing insert route
{
_, err = ds.InsertRoute(ctx, nil)
if err != models.ErrDatastoreEmptyRoute {
t.Log(buf.String())
t.Fatalf("Test InsertRoute(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoute, err)
}
_, err = ds.InsertRoute(ctx, &models.Route{AppName: "notreal", Path: "/test"})
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test InsertRoute: expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
}
_, err = ds.InsertRoute(ctx, testRoute)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test InsertRoute: error when storing new route: %s", err)
}
_, err = ds.InsertRoute(ctx, testRoute)
if err != models.ErrRoutesAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertRoute duplicated: expected error to be `%v`, but it was `%v`", models.ErrRoutesAlreadyExists, err)
}
}
// Testing get
{
_, err = ds.GetRoute(ctx, "a", "")
if err != models.ErrDatastoreEmptyRoutePath {
t.Log(buf.String())
t.Fatalf("Test GetRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
}
_, err = ds.GetRoute(ctx, "", "a")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test GetRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
route, err := ds.GetRoute(ctx, testApp.Name, testRoute.Path)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoute: unexpected error %v", err)
}
var expected models.Route = *testRoute
if !reflect.DeepEqual(*route, expected) {
t.Log(buf.String())
t.Fatalf("Test InsertApp: expected to insert:\n%v\nbut got:\n%v", expected, route)
}
}
// Testing update
{
// Update some fields, and add 3 configs and 3 headers.
updated, err := ds.UpdateRoute(ctx, &models.Route{
AppName: testRoute.AppName,
Path: testRoute.Path,
Timeout: 100,
Config: map[string]string{
"FIRST": "1",
"SECOND": "2",
"THIRD": "3",
},
Headers: http.Header{
"First": []string{"test"},
"Second": []string{"test", "test"},
"Third": []string{"test", "test2"},
},
})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute: unexpected error: %v", err)
}
expected := &models.Route{
// unchanged
AppName: testRoute.AppName,
Path: testRoute.Path,
Image: "iron/hello",
Type: "sync",
Format: "http",
// updated
Timeout: 100,
Config: map[string]string{
"FIRST": "1",
"SECOND": "2",
"THIRD": "3",
},
Headers: http.Header{
"First": []string{"test"},
"Second": []string{"test", "test"},
"Third": []string{"test", "test2"},
},
}
if !reflect.DeepEqual(*updated, *expected) {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute: expected updated `%v` but got `%v`", expected, updated)
}
// Update a config var, remove another. Add one Header, remove another.
updated, err = ds.UpdateRoute(ctx, &models.Route{
AppName: testRoute.AppName,
Path: testRoute.Path,
Config: map[string]string{
"FIRST": "first",
"SECOND": "",
"THIRD": "3",
},
Headers: http.Header{
"First": []string{"test2"},
"Second": nil,
},
})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute: unexpected error: %v", err)
}
expected = &models.Route{
// unchanged
AppName: testRoute.AppName,
Path: testRoute.Path,
Image: "iron/hello",
Type: "sync",
Format: "http",
Timeout: 100,
// updated
Config: map[string]string{
"FIRST": "first",
"THIRD": "3",
},
Headers: http.Header{
"First": []string{"test", "test2"},
"Third": []string{"test", "test2"},
},
}
if !reflect.DeepEqual(*updated, *expected) {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute: expected updated:\n`%v`\nbut got:\n`%v`", expected, updated)
}
}
// Testing list routes
routes, err := ds.GetRoutesByApp(ctx, testApp.Name, &models.RouteFilter{})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutesByApp: unexpected error %v", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutesByApp: expected result count to be greater than 0")
}
if routes[0] == nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected non-nil route")
} else if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
routes, err = ds.GetRoutesByApp(ctx, testApp.Name, &models.RouteFilter{Image: testRoute.Image})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutesByApp: unexpected error %v", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutesByApp: expected result count to be greater than 0")
}
if routes[0] == nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected non-nil route")
} else if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
routes, err = ds.GetRoutesByApp(ctx, "notreal", nil)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutesByApp: error: %s", err)
}
if len(routes) != 0 {
t.Fatalf("Test GetRoutesByApp: expected result count to be 0 but got %d", len(routes))
}
// Testing list routes
routes, err = ds.GetRoutes(ctx, &models.RouteFilter{Image: testRoute.Image})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: error: %s", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
}
if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
// Testing route delete
err = ds.RemoveRoute(ctx, "", "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test RemoveRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
err = ds.RemoveRoute(ctx, "a", "")
if err != models.ErrDatastoreEmptyRoutePath {
t.Log(buf.String())
t.Fatalf("Test RemoveRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
}
err = ds.RemoveRoute(ctx, testRoute.AppName, testRoute.Path)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: unexpected error: %v", err)
}
route, err := ds.GetRoute(ctx, testRoute.AppName, testRoute.Path)
if err != nil && err != models.ErrRoutesNotFound {
t.Log(buf.String())
t.Fatalf("Test GetRoute: expected error `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
}
if route != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: failed to remove the route: %v", route)
}
_, err = ds.UpdateRoute(ctx, &models.Route{
AppName: testRoute.AppName,
Path: testRoute.Path,
Image: "test",
})
if err != models.ErrRoutesNotFound {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute inexistent: expected error to be `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
}
})
t.Run("put-get", func(t *testing.T) {
// Testing Put/Get
err := ds.Put(ctx, nil, nil)
if err != models.ErrDatastoreEmptyKey {
t.Log(buf.String())
t.Fatalf("Test Put(nil,nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyKey, err)
}
err = ds.Put(ctx, []byte("test"), []byte("success"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
val, err := ds.Get(ctx, []byte("test"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
if string(val) != "success" {
t.Log(buf.String())
t.Fatalf("Test Get: expected value to be `%v`, but it was `%v`", "success", string(val))
}
err = ds.Put(ctx, []byte("test"), nil)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
val, err = ds.Get(ctx, []byte("test"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
if string(val) != "" {
t.Log(buf.String())
t.Fatalf("Test Get: expected value to be `%v`, but it was `%v`", "", string(val))
}
})
}
var testApp = &models.App{
Name: "Test",
}
var testRoute = &models.Route{
AppName: testApp.Name,
Path: "/test",
Image: "iron/hello",
Type: "sync",
Format: "http",
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/iron-io/functions/api/models"
"github.com/lib/pq"
_ "github.com/lib/pq"
"bytes"
)
const routesTableCreate = `
@@ -117,39 +118,54 @@ func (ds *PostgresDatastore) InsertApp(ctx context.Context, app *models.App) (*m
return app, nil
}
func (ds *PostgresDatastore) UpdateApp(ctx context.Context, app *models.App) (*models.App, error) {
if app == nil {
func (ds *PostgresDatastore) UpdateApp(ctx context.Context, newapp *models.App) (*models.App, error) {
if newapp == nil {
return nil, models.ErrAppsNotFound
}
cbyte, err := json.Marshal(app.Config)
if err != nil {
return nil, err
}
app := &models.App{Name: newapp.Name}
err := ds.Tx(func(tx *sql.Tx) error {
row := ds.db.QueryRow("SELECT config FROM apps WHERE name=$1", app.Name)
res, err := ds.db.Exec(`
UPDATE apps SET
config = $2
WHERE name = $1
RETURNING *;
`,
app.Name,
string(cbyte),
)
var config string
if err := row.Scan(&config); err != nil {
if err == sql.ErrNoRows {
return models.ErrAppsNotFound
}
return err
}
if config != "" {
err := json.Unmarshal([]byte(config), &app.Config)
if err != nil {
return err
}
}
app.UpdateConfig(newapp.Config)
cbyte, err := json.Marshal(app.Config)
if err != nil {
return err
}
res, err := ds.db.Exec(`UPDATE apps SET config = $2 WHERE name = $1;`, app.Name, string(cbyte))
if err != nil {
return err
}
if n, err := res.RowsAffected(); err != nil {
return err
} else if n == 0 {
return models.ErrAppsNotFound
}
return nil
})
if err != nil {
return nil, err
}
n, err := res.RowsAffected()
if err != nil {
return nil, err
}
if n == 0 {
return nil, models.ErrAppsNotFound
}
return app, nil
}
@@ -213,9 +229,8 @@ func scanApp(scanner rowScanner, app *models.App) error {
func (ds *PostgresDatastore) GetApps(ctx context.Context, filter *models.AppFilter) ([]*models.App, error) {
res := []*models.App{}
filterQuery := buildFilterAppQuery(filter)
rows, err := ds.db.Query(fmt.Sprintf("SELECT DISTINCT * FROM apps %s", filterQuery))
filterQuery, args := buildFilterAppQuery(filter)
rows, err := ds.db.Query(fmt.Sprintf("SELECT DISTINCT * FROM apps %s", filterQuery), args...)
if err != nil {
return nil, err
}
@@ -255,7 +270,26 @@ func (ds *PostgresDatastore) InsertRoute(ctx context.Context, route *models.Rout
return nil, err
}
_, err = ds.db.Exec(`
err = ds.Tx(func(tx *sql.Tx) error {
r := tx.QueryRow(`SELECT 1 FROM apps WHERE name=$1`, route.AppName)
if err := r.Scan(new(int)); err != nil {
if err == sql.ErrNoRows {
return models.ErrAppsNotFound
}
return err
}
same, err := tx.Query(`SELECT 1 FROM routes WHERE app_name=$1 AND path=$2`,
route.AppName, route.Path)
if err != nil {
return err
}
defer same.Close()
if same.Next() {
return models.ErrRoutesAlreadyExists
}
_, err = tx.Exec(`
INSERT INTO routes (
app_name,
path,
@@ -269,80 +303,93 @@ func (ds *PostgresDatastore) InsertRoute(ctx context.Context, route *models.Rout
config
)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);`,
route.AppName,
route.Path,
route.Image,
route.Format,
route.MaxConcurrency,
route.Memory,
route.Type,
route.Timeout,
string(hbyte),
string(cbyte),
)
route.AppName,
route.Path,
route.Image,
route.Format,
route.MaxConcurrency,
route.Memory,
route.Type,
route.Timeout,
string(hbyte),
string(cbyte),
)
return err
})
if err != nil {
pqErr := err.(*pq.Error)
if pqErr.Code == "23505" {
return nil, models.ErrRoutesAlreadyExists
}
return nil, err
}
return route, nil
}
func (ds *PostgresDatastore) UpdateRoute(ctx context.Context, route *models.Route) (*models.Route, error) {
if route == nil {
func (ds *PostgresDatastore) UpdateRoute(ctx context.Context, newroute *models.Route) (*models.Route, error) {
if newroute == nil {
return nil, models.ErrDatastoreEmptyRoute
}
hbyte, err := json.Marshal(route.Headers)
if err != nil {
return nil, err
}
var route models.Route
err := ds.Tx(func(tx *sql.Tx) error {
row := ds.db.QueryRow(fmt.Sprintf("%s WHERE app_name=$1 AND path=$2", routeSelector), newroute.AppName, newroute.Path)
if err := scanRoute(row, &route); err == sql.ErrNoRows {
return models.ErrRoutesNotFound
} else if err != nil {
return err
}
cbyte, err := json.Marshal(route.Config)
if err != nil {
return nil, err
}
route.Update(newroute)
res, err := ds.db.Exec(`
hbyte, err := json.Marshal(route.Headers)
if err != nil {
return err
}
cbyte, err := json.Marshal(route.Config)
if err != nil {
return err
}
res, err := tx.Exec(`
UPDATE routes SET
image = $3,
format = $4,
memory = $5,
maxc = $6,
maxc = $5,
memory = $6,
type = $7,
timeout = $8,
headers = $9,
config = $10
WHERE app_name = $1 AND path = $2;`,
route.AppName,
route.Path,
route.Image,
route.Format,
route.Memory,
route.MaxConcurrency,
route.Type,
route.Timeout,
string(hbyte),
string(cbyte),
)
route.AppName,
route.Path,
route.Image,
route.Format,
route.MaxConcurrency,
route.Memory,
route.Type,
route.Timeout,
string(hbyte),
string(cbyte),
)
if err != nil {
return err
}
if n, err := res.RowsAffected(); err != nil {
return err
} else if n == 0 {
return models.ErrRoutesNotFound
}
return nil
})
if err != nil {
return nil, err
}
n, err := res.RowsAffected()
if err != nil {
return nil, err
}
if n == 0 {
return nil, models.ErrRoutesNotFound
}
return route, nil
return &route, nil
}
func (ds *PostgresDatastore) RemoveRoute(ctx context.Context, appName, routePath string) error {
@@ -384,8 +431,8 @@ func scanRoute(scanner rowScanner, route *models.Route) error {
&route.Path,
&route.Image,
&route.Format,
&route.Memory,
&route.MaxConcurrency,
&route.Memory,
&route.Type,
&route.Timeout,
&headerStr,
@@ -426,8 +473,8 @@ func (ds *PostgresDatastore) GetRoute(ctx context.Context, appName, routePath st
func (ds *PostgresDatastore) GetRoutes(ctx context.Context, filter *models.RouteFilter) ([]*models.Route, error) {
res := []*models.Route{}
filterQuery := buildFilterRouteQuery(filter)
rows, err := ds.db.Query(fmt.Sprintf("%s %s", routeSelector, filterQuery))
filterQuery, args := buildFilterRouteQuery(filter)
rows, err := ds.db.Query(fmt.Sprintf("%s %s", routeSelector, filterQuery), args...)
// todo: check for no rows so we don't respond with a sql 500 err
if err != nil {
return nil, err
@@ -451,9 +498,17 @@ func (ds *PostgresDatastore) GetRoutes(ctx context.Context, filter *models.Route
func (ds *PostgresDatastore) GetRoutesByApp(ctx context.Context, appName string, filter *models.RouteFilter) ([]*models.Route, error) {
res := []*models.Route{}
filter.AppName = appName
filterQuery := buildFilterRouteQuery(filter)
rows, err := ds.db.Query(fmt.Sprintf("%s %s", routeSelector, filterQuery))
var filterQuery string
var args []interface{}
if filter == nil {
filterQuery = "WHERE app_name = $1"
args = []interface{}{appName}
} else {
filter.AppName = appName
filterQuery, args = buildFilterRouteQuery(filter)
}
rows, err := ds.db.Query(fmt.Sprintf("%s %s", routeSelector, filterQuery), args...)
// todo: check for no rows so we don't respond with a sql 500 err
if err != nil {
return nil, err
@@ -472,55 +527,44 @@ func (ds *PostgresDatastore) GetRoutesByApp(ctx context.Context, appName string,
if err := rows.Err(); err != nil {
return nil, err
}
return res, nil
}
func buildFilterAppQuery(filter *models.AppFilter) (string, []interface{}) {
if filter == nil {
return "", nil
}
func buildFilterAppQuery(filter *models.AppFilter) string {
filterQuery := ""
if filter.Name != "" {
return "WHERE name LIKE $1", []interface{}{filter.Name}
}
if filter != nil {
filterQueries := []string{}
if filter.Name != "" {
filterQueries = append(filterQueries, fmt.Sprintf("name LIKE '%s'", filter.Name))
}
return "", nil
}
for i, field := range filterQueries {
if i == 0 {
filterQuery = fmt.Sprintf("WHERE %s ", field)
func buildFilterRouteQuery(filter *models.RouteFilter) (string, []interface{}) {
if filter == nil {
return "", nil
}
var b bytes.Buffer
var args []interface{}
where := func(colOp, val string) {
if val != "" {
args = append(args, val)
if len(args) == 1 {
fmt.Fprintf(&b, "WHERE %s $1", colOp)
} else {
filterQuery = fmt.Sprintf("%s AND %s", filterQuery, field)
fmt.Fprintf(&b, " AND %s $%d", colOp, len(args))
}
}
}
return filterQuery
}
where("path =", filter.Path)
where("app_name =", filter.AppName)
where("image =", filter.Image)
func buildFilterRouteQuery(filter *models.RouteFilter) string {
filterQuery := ""
filterQueries := []string{}
if filter.Path != "" {
filterQueries = append(filterQueries, fmt.Sprintf("path = '%s'", filter.Path))
}
if filter.AppName != "" {
filterQueries = append(filterQueries, fmt.Sprintf("app_name = '%s'", filter.AppName))
}
if filter.Image != "" {
filterQueries = append(filterQueries, fmt.Sprintf("image = '%s'", filter.Image))
}
for i, field := range filterQueries {
if i == 0 {
filterQuery = fmt.Sprintf("WHERE %s ", field)
} else {
filterQuery = fmt.Sprintf("%s AND %s", filterQuery, field)
}
}
return filterQuery
return b.String(), args
}
func (ds *PostgresDatastore) Put(ctx context.Context, key, value []byte) error {
@@ -562,3 +606,17 @@ func (ds *PostgresDatastore) Get(ctx context.Context, key []byte) ([]byte, error
return []byte(value), nil
}
func (ds *PostgresDatastore) Tx(f func(*sql.Tx) error) error {
tx, err := ds.db.Begin()
if err != nil {
return err
}
err = f(tx)
if err != nil {
tx.Rollback()
return err
}
return tx.Commit()
}

View File

@@ -0,0 +1,98 @@
package postgres
import (
"bytes"
"database/sql"
"fmt"
"net/url"
"os/exec"
"testing"
"time"
"github.com/iron-io/functions/api/datastore/internal/datastoretest"
)
const tmpPostgres = "postgres://postgres@127.0.0.1:15432/funcs?sslmode=disable"
func preparePostgresTest(logf, fatalf func(string, ...interface{})) (func(), func()) {
fmt.Println("initializing postgres for test")
tryRun(logf, "remove old postgres container", exec.Command("docker", "rm", "-f", "iron-postgres-test"))
mustRun(fatalf, "start postgres container", exec.Command("docker", "run", "--name", "iron-postgres-test", "-p", "15432:5432", "-d", "postgres"))
wait := 1 * time.Second
for {
db, err := sql.Open("postgres", "postgres://postgres@127.0.0.1:15432?sslmode=disable")
if err != nil {
fmt.Println("failed to connect to postgres:", err)
fmt.Println("retrying in:", wait)
time.Sleep(wait)
wait = 2 * wait
continue
}
_, err = db.Exec(`CREATE DATABASE funcs;`)
if err != nil {
fmt.Println("failed to create database:", err)
fmt.Println("retrying in:", wait)
time.Sleep(wait)
wait = 2 * wait
continue
}
_, err = db.Exec(`GRANT ALL PRIVILEGES ON DATABASE funcs TO postgres;`)
if err == nil {
break
}
fmt.Println("failed to grant privileges:", err)
fmt.Println("retrying in:", wait)
time.Sleep(wait)
wait = 2 * wait
}
fmt.Println("postgres for test ready")
return func() {
db, err := sql.Open("postgres", tmpPostgres)
if err != nil {
fatalf("failed to connect for truncation: %s\n", err)
}
for _, table := range []string{"routes", "apps", "extras"} {
_, err = db.Exec(`TRUNCATE TABLE ` + table)
if err != nil {
fatalf("failed to truncate table %q: %s\n", table, err)
}
}
},
func() {
tryRun(logf, "stop postgres container", exec.Command("docker", "rm", "-f", "iron-postgres-test"))
}
}
func TestDatastore(t *testing.T) {
_, close := preparePostgresTest(t.Logf, t.Fatalf)
defer close()
u, err := url.Parse(tmpPostgres)
if err != nil {
t.Fatalf("failed to parse url:", err)
}
ds, err := New(u)
if err != nil {
t.Fatalf("failed to create postgres datastore:", err)
}
datastoretest.Test(t, ds)
}
func tryRun(logf func(string, ...interface{}), desc string, cmd *exec.Cmd) {
var b bytes.Buffer
cmd.Stderr = &b
if err := cmd.Run(); err != nil {
logf("failed to %s: %s", desc, b.String())
}
}
func mustRun(fatalf func(string, ...interface{}), desc string, cmd *exec.Cmd) {
var b bytes.Buffer
cmd.Stderr = &b
if err := cmd.Run(); err != nil {
fatalf("failed to %s: %s", desc, b.String())
}
}

View File

@@ -1,323 +0,0 @@
package datastore
import (
"context"
"database/sql"
"fmt"
"os/exec"
"testing"
"time"
"github.com/iron-io/functions/api/models"
)
const tmpPostgres = "postgres://postgres@127.0.0.1:15432/funcs?sslmode=disable"
func preparePostgresTest() func() {
fmt.Println("initializing postgres for test")
exec.Command("docker", "rm", "-f", "iron-postgres-test").Run()
exec.Command("docker", "run", "--name", "iron-postgres-test", "-p", "15432:5432", "-d", "postgres").Run()
for {
db, err := sql.Open("postgres", "postgres://postgres@127.0.0.1:15432?sslmode=disable")
if err != nil {
time.Sleep(1 * time.Second)
continue
}
db.Exec(`CREATE DATABASE funcs;`)
_, err = db.Exec(`GRANT ALL PRIVILEGES ON DATABASE funcs TO postgres;`)
if err == nil {
break
}
time.Sleep(1 * time.Second)
}
fmt.Println("postgres for test ready")
return func() {
exec.Command("docker", "rm", "-f", "iron-postgres-test").Run()
}
}
func TestPostgres(t *testing.T) {
close := preparePostgresTest()
defer close()
buf := setLogBuffer()
ctx := context.Background()
ds, err := New(tmpPostgres)
if err != nil {
t.Fatalf("Error when creating datastore: %v", err)
}
// Testing insert app
_, err = ds.InsertApp(ctx, nil)
if err != models.ErrDatastoreEmptyApp {
t.Log(buf.String())
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyApp, err)
}
_, err = ds.InsertApp(ctx, &models.App{})
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test InsertApp(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
_, err = ds.InsertApp(ctx, testApp)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test InsertApp: error when storing new app: %s", err)
}
_, err = ds.InsertApp(ctx, testApp)
if err != models.ErrAppsAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertApp duplicated: expected error `%v`, but it was `%v`", models.ErrAppsAlreadyExists, err)
}
_, err = ds.UpdateApp(ctx, &models.App{
Name: testApp.Name,
Config: map[string]string{
"TEST": "1",
},
})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateApp: error when updating app: %v", err)
}
// Testing get app
_, err = ds.GetApp(ctx, "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected error to be %v, but it was %s", models.ErrDatastoreEmptyAppName, err)
}
app, err := ds.GetApp(ctx, testApp.Name)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApp: error: %s", err)
}
if app.Name != testApp.Name {
t.Log(buf.String())
t.Fatalf("Test GetApp: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
}
// Testing list apps
apps, err := ds.GetApps(ctx, &models.AppFilter{})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApps: unexpected error %v", err)
}
if len(apps) == 0 {
t.Fatal("Test GetApps: expected result count to be greater than 0")
}
if apps[0].Name != testApp.Name {
t.Log(buf.String())
t.Fatalf("Test GetApps: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
}
apps, err = ds.GetApps(ctx, &models.AppFilter{Name: "Tes%"})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetApps(filter): unexpected error %v", err)
}
if len(apps) == 0 {
t.Fatal("Test GetApps(filter): expected result count to be greater than 0")
}
// Testing app delete
err = ds.RemoveApp(ctx, "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
err = ds.RemoveApp(ctx, testApp.Name)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: error: %s", err)
}
app, err = ds.GetApp(ctx, testApp.Name)
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test GetApp(removed): expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
}
if app != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: failed to remove the app")
}
// Test update inexistent app
_, err = ds.UpdateApp(ctx, &models.App{
Name: testApp.Name,
Config: map[string]string{
"TEST": "1",
},
})
if err != models.ErrAppsNotFound {
t.Log(buf.String())
t.Fatalf("Test UpdateApp(inexistent): expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
}
// Insert app again to test routes
ds.InsertApp(ctx, testApp)
// Testing insert route
_, err = ds.InsertRoute(ctx, nil)
if err != models.ErrDatastoreEmptyRoute {
t.Log(buf.String())
t.Fatalf("Test InsertRoute(nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoute, err)
}
_, err = ds.InsertRoute(ctx, testRoute)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test InsertRoute: error when storing new route: %s", err)
}
_, err = ds.InsertRoute(ctx, testRoute)
if err != models.ErrRoutesAlreadyExists {
t.Log(buf.String())
t.Fatalf("Test InsertRoute duplicated: expected error to be `%v`, but it was `%v`", models.ErrRoutesAlreadyExists, err)
}
_, err = ds.UpdateRoute(ctx, testRoute)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute: unexpected error: %v", err)
}
// Testing get
_, err = ds.GetRoute(ctx, "a", "")
if err != models.ErrDatastoreEmptyRoutePath {
t.Log(buf.String())
t.Fatalf("Test GetRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
}
_, err = ds.GetRoute(ctx, "", "a")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test GetRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
route, err := ds.GetRoute(ctx, testApp.Name, testRoute.Path)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoute: unexpected error %v", err)
}
if route.Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoute: expected `route.Path` to be `%s` but it was `%s`", route.Path, testRoute.Path)
}
// Testing list routes
routes, err := ds.GetRoutesByApp(ctx, testApp.Name, &models.RouteFilter{})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: unexpected error %v", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
}
if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
// Testing list routes
routes, err = ds.GetRoutes(ctx, &models.RouteFilter{Image: testRoute.Image})
if err != nil {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: error: %s", err)
}
if len(routes) == 0 {
t.Fatal("Test GetRoutes: expected result count to be greater than 0")
}
if routes[0].Path != testRoute.Path {
t.Log(buf.String())
t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Path, routes[0].Path)
}
// Testing app delete
err = ds.RemoveRoute(ctx, "", "")
if err != models.ErrDatastoreEmptyAppName {
t.Log(buf.String())
t.Fatalf("Test RemoveRoute(empty app name): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyAppName, err)
}
err = ds.RemoveRoute(ctx, "a", "")
if err != models.ErrDatastoreEmptyRoutePath {
t.Log(buf.String())
t.Fatalf("Test RemoveRoute(empty route path): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyRoutePath, err)
}
err = ds.RemoveRoute(ctx, testRoute.AppName, testRoute.Path)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: unexpected error: %v", err)
}
_, err = ds.UpdateRoute(ctx, &models.Route{
AppName: testRoute.AppName,
Path: testRoute.Path,
Image: "test",
})
if err != models.ErrRoutesNotFound {
t.Log(buf.String())
t.Fatalf("Test UpdateRoute inexistent: expected error to be `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
}
route, err = ds.GetRoute(ctx, testRoute.AppName, testRoute.Path)
if err != models.ErrRoutesNotFound {
t.Log(buf.String())
t.Fatalf("Test GetRoute: expected error `%v`, but it was `%v`", models.ErrRoutesNotFound, err)
}
if route != nil {
t.Log(buf.String())
t.Fatalf("Test RemoveApp: failed to remove the route")
}
// Testing Put/Get
err = ds.Put(ctx, nil, nil)
if err != models.ErrDatastoreEmptyKey {
t.Log(buf.String())
t.Fatalf("Test Put(nil,nil): expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyKey, err)
}
err = ds.Put(ctx, []byte("test"), []byte("success"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
val, err := ds.Get(ctx, []byte("test"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
if string(val) != "success" {
t.Log(buf.String())
t.Fatalf("Test Get: expected value to be `%v`, but it was `%v`", "success", string(val))
}
err = ds.Put(ctx, []byte("test"), nil)
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
val, err = ds.Get(ctx, []byte("test"))
if err != nil {
t.Log(buf.String())
t.Fatalf("Test Put: unexpected error: %v", err)
}
if string(val) != "" {
t.Log(buf.String())
t.Fatalf("Test Get: expected value to be `%v`, but it was `%v`", "", string(val))
}
}
func testPostgresInsert(t *testing.T, ctx context.Context, ds models.Datastore) {
}

View File

@@ -53,6 +53,22 @@ func (a *App) Validate() error {
return nil
}
// UpdateConfig adds entries from patch to a.Config, and removes entries with empty values.
func (a *App) UpdateConfig(patch Config) {
if patch != nil {
if a.Config == nil {
a.Config = make(Config)
}
for k, v := range patch {
if v == "" {
delete(a.Config, k)
} else {
a.Config[k] = v
}
}
}
}
type AppFilter struct {
Name string
}

View File

@@ -125,6 +125,55 @@ func (r *Route) Validate() error {
return nil
}
// Update updates fields in r with non-zero field values from new.
// 0-length slice Header values, and empty-string Config values trigger removal of map entry.
func (r *Route) Update(new *Route) {
if new.Image != "" {
r.Image = new.Image
}
if new.Memory != 0 {
r.Memory = new.Memory
}
if new.Type != "" {
r.Type = new.Type
}
if new.Timeout != 0 {
r.Timeout = new.Timeout
}
if new.Format != "" {
r.Format = new.Format
}
if new.MaxConcurrency != 0 {
r.MaxConcurrency = new.MaxConcurrency
}
if new.Headers != nil {
if r.Headers == nil {
r.Headers = make(http.Header)
}
for k, v := range new.Headers {
if len(v) == 0 {
r.Headers.Del(k)
} else {
for _, val := range v {
r.Headers.Add(k, val)
}
}
}
}
if new.Config != nil {
if r.Config == nil {
r.Config = make(Config)
}
for k, v := range new.Config {
if v == "" {
delete(r.Config, k)
} else {
r.Config[k] = v
}
}
}
}
type RouteFilter struct {
Path string
AppName string