From e2c5f094f8b53d5849fb681da62e26593b4f79b2 Mon Sep 17 00:00:00 2001 From: Pedro Nasser Date: Fri, 5 Aug 2016 20:00:16 -0300 Subject: [PATCH] small refactor datastore and added tests --- api/datastore/bolt/bolt.go | 41 ++++++++-- api/datastore/bolt_test.go | 160 +++++++++++++++++++++++++++++++++++++ api/models/datastore.go | 9 +++ 3 files changed, 205 insertions(+), 5 deletions(-) create mode 100644 api/datastore/bolt_test.go diff --git a/api/datastore/bolt/bolt.go b/api/datastore/bolt/bolt.go index d9ae94b38..e2e5d2a7f 100644 --- a/api/datastore/bolt/bolt.go +++ b/api/datastore/bolt/bolt.go @@ -68,6 +68,10 @@ func New(url *url.URL) (models.Datastore, error) { } func (ds *BoltDatastore) StoreApp(app *models.App) (*models.App, error) { + if app == nil { + return nil, models.ErrDatastoreEmptyApp + } + err := ds.db.Update(func(tx *bolt.Tx) error { bIm := tx.Bucket(ds.appsBucket) buf, err := json.Marshal(app) @@ -89,6 +93,10 @@ func (ds *BoltDatastore) StoreApp(app *models.App) (*models.App, error) { } func (ds *BoltDatastore) RemoveApp(appName string) error { + if appName == "" { + return models.ErrDatastoreEmptyAppName + } + err := ds.db.Update(func(tx *bolt.Tx) error { bIm := tx.Bucket(ds.appsBucket) err := bIm.Delete([]byte(appName)) @@ -130,6 +138,10 @@ func (ds *BoltDatastore) GetApps(filter *models.AppFilter) ([]*models.App, error } func (ds *BoltDatastore) GetApp(name string) (*models.App, error) { + if name == "" { + return nil, models.ErrDatastoreEmptyAppName + } + var res *models.App err := ds.db.View(func(tx *bolt.Tx) error { b := tx.Bucket(ds.appsBucket) @@ -164,6 +176,10 @@ func (ds *BoltDatastore) getRouteBucketForApp(tx *bolt.Tx, appName string) (*bol } func (ds *BoltDatastore) StoreRoute(route *models.Route) (*models.Route, error) { + if route == nil { + return nil, models.ErrDatastoreEmptyApp + } + err := ds.db.Update(func(tx *bolt.Tx) error { b, err := ds.getRouteBucketForApp(tx, route.AppName) if err != nil { @@ -188,6 +204,14 @@ func (ds *BoltDatastore) StoreRoute(route *models.Route) (*models.Route, error) } func (ds *BoltDatastore) RemoveRoute(appName, routeName string) error { + if appName == "" { + return models.ErrDatastoreEmptyAppName + } + + if routeName == "" { + return models.ErrDatastoreEmptyRouteName + } + err := ds.db.Update(func(tx *bolt.Tx) error { b, err := ds.getRouteBucketForApp(tx, appName) if err != nil { @@ -207,7 +231,15 @@ func (ds *BoltDatastore) RemoveRoute(appName, routeName string) error { } func (ds *BoltDatastore) GetRoute(appName, routeName string) (*models.Route, error) { - var route models.Route + if appName == "" { + return nil, models.ErrDatastoreEmptyAppName + } + + if routeName == "" { + return nil, models.ErrDatastoreEmptyRouteName + } + + var route *models.Route err := ds.db.View(func(tx *bolt.Tx) error { b, err := ds.getRouteBucketForApp(tx, appName) if err != nil { @@ -215,13 +247,12 @@ func (ds *BoltDatastore) GetRoute(appName, routeName string) (*models.Route, err } v := b.Get([]byte(routeName)) - if v == nil { - return models.ErrRoutesNotFound + if v != nil { + err = json.Unmarshal(v, &route) } - err = json.Unmarshal(v, &route) return err }) - return &route, err + return route, err } func (ds *BoltDatastore) GetRoutes(filter *models.RouteFilter) ([]*models.Route, error) { diff --git a/api/datastore/bolt_test.go b/api/datastore/bolt_test.go new file mode 100644 index 000000000..50d1830cc --- /dev/null +++ b/api/datastore/bolt_test.go @@ -0,0 +1,160 @@ +package datastore + +import ( + "os" + "testing" + + "github.com/iron-io/functions/api/models" +) + +var tmpBolt = "/tmp/func_test_bolt.db" + +func prepareBolt(t *testing.T) (models.Datastore, func()) { + ds, err := New("bolt://" + tmpBolt) + if err != nil { + t.Fatal("Error when creating datastore: %s", err) + } + return ds, func() { + os.Remove(tmpBolt) + } +} + +func TestBolt(t *testing.T) { + ds, close := prepareBolt(t) + defer close() + + testApp := &models.App{ + Name: "Test", + } + + testRoute := &models.Route{ + Name: "test", + AppName: testApp.Name, + Path: "/test", + Image: "iron/hello", + } + + // Testing store app + _, err := ds.StoreApp(nil) + if err == nil { + t.Fatalf("Test StoreApp: expected error when using nil app", err) + } + + _, err = ds.StoreApp(testApp) + if err != nil { + t.Fatalf("Test StoreApp: error when Bolt was storing new app: %s", err) + } + + // Testing get app + _, err = ds.GetApp("") + if err == nil { + t.Fatalf("Test GetApp: expected error when using empty app name", err) + } + + app, err := ds.GetApp(testApp.Name) + if err != nil { + t.Fatalf("Test GetApp: error: %s", err) + } + if app.Name != testApp.Name { + 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(&models.AppFilter{}) + if err != nil { + t.Fatalf("Test GetApps: error: %s", err) + } + if len(apps) == 0 { + t.Fatal("Test GetApps: expected result count to be greater than 0") + } + if apps[0].Name != testApp.Name { + t.Fatalf("Test GetApps: expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name) + } + + // Testing app delete + err = ds.RemoveApp("") + if err == nil { + t.Fatalf("Test RemoveApp: expected error when using empty app name", err) + } + + err = ds.RemoveApp(testApp.Name) + if err != nil { + t.Fatalf("Test RemoveApp: error: %s", err) + } + app, err = ds.GetApp(testApp.Name) + if err != nil { + t.Fatalf("Test GetApp: error: %s", err) + } + if app != nil { + t.Fatalf("Test RemoveApp: failed to remove the app") + } + + // Store app again to test routes + ds.StoreApp(testApp) + + // Testing store route + _, err = ds.StoreRoute(nil) + if err == nil { + t.Fatalf("Test StoreRoute: expected error when using nil route", err) + } + + _, err = ds.StoreRoute(testRoute) + if err != nil { + t.Fatalf("Test StoreReoute: error when Bolt was storing new route: %s", err) + } + + // Testing get + _, err = ds.GetRoute("a", "") + if err == nil { + t.Fatalf("Test GetRoute: expected error when using empty route name", err) + } + + _, err = ds.GetRoute("", "a") + if err == nil { + t.Fatalf("Test GetRoute: expected error when using empty app name", err) + } + + route, err := ds.GetRoute(testApp.Name, testRoute.Name) + if err != nil { + t.Fatalf("Test GetRoute: error: %s", err) + } + if route.Name != testRoute.Name { + t.Fatalf("Test GetRoute: expected `route.Name` to be `%s` but it was `%s`", route.Name, testRoute.Name) + } + + // Testing list routes + routes, err := ds.GetRoutes(&models.RouteFilter{AppName: testApp.Name}) + if err != nil { + 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].Name != testRoute.Name { + t.Fatalf("Test GetRoutes: expected `app.Name` to be `%s` but it was `%s`", testRoute.Name, routes[0].Name) + } + + // Testing app delete + err = ds.RemoveRoute("", "") + if err == nil { + t.Fatalf("Test RemoveRoute: expected error when using empty app name", err) + } + + err = ds.RemoveRoute("a", "") + if err == nil { + t.Fatalf("Test RemoveRoute: expected error when using empty route name", err) + } + + err = ds.RemoveRoute(testRoute.AppName, testRoute.Name) + if err != nil { + t.Fatalf("Test RemoveApp: error: %s", err) + } + + route, err = ds.GetRoute(testRoute.AppName, testRoute.Name) + if err != nil { + t.Fatalf("Test GetRoute: error: %s", err) + } + if route != nil { + t.Fatalf("Test RemoveApp: failed to remove the route") + } +} diff --git a/api/models/datastore.go b/api/models/datastore.go index 4ffa3fb8a..8f6df3234 100644 --- a/api/models/datastore.go +++ b/api/models/datastore.go @@ -1,5 +1,7 @@ package models +import "errors" + type Datastore interface { GetApp(appName string) (*App, error) GetApps(*AppFilter) ([]*App, error) @@ -12,6 +14,13 @@ type Datastore interface { RemoveRoute(appName, routeName string) error } +var ( + ErrDatastoreEmptyAppName = errors.New("Missing app name") + ErrDatastoreEmptyRouteName = errors.New("Missing route name") + ErrDatastoreEmptyApp = errors.New("Missing app") + ErrDatastoreEmptyRoute = errors.New("Missing route") +) + func ApplyAppFilter(app *App, filter *AppFilter) bool { return true }