mirror of
https://github.com/fnproject/fn.git
synced 2022-10-28 21:29:17 +03:00
* get rid of old format stuff, utils usage, fix up for fdk2.0 interface * pure agent format removal, TODO remove format field, fix up all tests * shitter's clogged * fix agent tests * start rolling through server tests * tests compile, some failures * remove json / content type detection on invoke/httptrigger, fix up tests * remove hello, fixup system tests the fucking status checker test just hangs and it's testing that it doesn't work so the test passes but the test doesn't pass fuck life it's not worth it * fix migration * meh * make dbhelper shut up about dbhelpers not being used * move fail status at least into main thread, jfc * fix status call to have FN_LISTENER also turns off the stdout/stderr blocking between calls, because it's impossible to debug without that (without syslog), now that stdout and stderr go to the same place (either to host stderr or nowhere) and isn't used for function output this shouldn't be a big fuss really * remove stdin * cleanup/remind: fixed bug where watcher would leak if container dies first * silence system-test logs until fail, fix datastore tests postgres does weird things with constraints when renaming tables, took the easy way out system-tests were loud as fuck and made you download a circleci text file of the logs, made them only yell when they goof * fix fdk-go dep for test image. fun * fix swagger and remove test about format * update all the gopkg files * add back FN_FORMAT for fdks that assert things. pfft * add useful error for functions that exit this error is really confounding because containers can exit for all manner of reason, we're just guessing that this is the most likely cause for now, and this error message should very likely change or be removed from the client path anyway (context.Canceled wasn't all that useful either, but anyway, I'd been hunting for this... so found it). added a test to avoid being publicly shamed for 1 line commits (beware...).
1228 lines
37 KiB
Go
1228 lines
37 KiB
Go
package datastoretest
|
|
|
|
// Data store correctness tests -
|
|
// These tests run validation tests on an underlying data store implementation and can be re-used for new data stores.
|
|
// TODO: Generalize some tests around metadata (updated_created,ids)
|
|
// TODO: Generalize tests around pagination and filtering
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"log"
|
|
"math/rand"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/fnproject/fn/api/models"
|
|
"github.com/gin-gonic/gin"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
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
|
|
}
|
|
|
|
//ResourceProvider provides an abstraction for supplying data store tests with
|
|
// appropriate initial testing objects for running tests
|
|
// Use the resource calls to supply objects with (e.g.) middleware enforced annotations set on them
|
|
// Use DefaultCtx to override custom middleware-supplied context variables
|
|
type ResourceProvider interface {
|
|
// ValidApp returns a valid app to use for inserts
|
|
ValidApp() *models.App
|
|
// ValidFn returns a valid fn to use for inserts
|
|
ValidFn(appId string) *models.Fn
|
|
// ValidTrigger returns a valid trigger to use for inserts
|
|
ValidTrigger(appId string, fnId string) *models.Trigger
|
|
|
|
// DefaultCtx returns a context object (which may have custom attributes set)
|
|
// this may be used (e.g.) to pass on tenancy and user details that would originate from a middleware to your data store
|
|
DefaultCtx() context.Context
|
|
}
|
|
|
|
// BasicResourceProvider supplies simple objects and can be used as a base for custom resource providers
|
|
type BasicResourceProvider struct {
|
|
rand *rand.Rand
|
|
}
|
|
|
|
// DataStoreFunc provides an instance of a data store
|
|
type DataStoreFunc func(*testing.T) models.Datastore
|
|
|
|
//NewBasicResourceProvider creates a dumb resource provider that generates resources that have valid, random names (and other unique attributes)
|
|
func NewBasicResourceProvider() ResourceProvider {
|
|
return &BasicResourceProvider{
|
|
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
|
|
}
|
|
}
|
|
|
|
func (brp *BasicResourceProvider) NextID() uint32 {
|
|
return brp.rand.Uint32()
|
|
}
|
|
|
|
func (brp *BasicResourceProvider) DefaultCtx() context.Context {
|
|
return context.Background()
|
|
}
|
|
|
|
// Creates a valid app which always has a sequential named
|
|
func (brp *BasicResourceProvider) ValidApp() *models.App {
|
|
|
|
app := &models.App{
|
|
Name: fmt.Sprintf("app_%09d", brp.NextID()),
|
|
}
|
|
return app
|
|
}
|
|
|
|
func (brp *BasicResourceProvider) ValidTrigger(appId, funcId string) *models.Trigger {
|
|
|
|
trigger := &models.Trigger{
|
|
Name: fmt.Sprintf("trigger_%09d", brp.NextID()),
|
|
AppID: appId,
|
|
FnID: funcId,
|
|
Type: "http",
|
|
Source: fmt.Sprintf("/source_%09d", brp.NextID()),
|
|
}
|
|
|
|
return trigger
|
|
}
|
|
|
|
func (brp *BasicResourceProvider) ValidFn(appId string) *models.Fn {
|
|
return &models.Fn{
|
|
AppID: appId,
|
|
Name: fmt.Sprintf("test_%09d", brp.NextID()),
|
|
Image: "fnproject/fn-test-utils",
|
|
ResourceConfig: models.ResourceConfig{
|
|
Timeout: models.DefaultTimeout,
|
|
IdleTimeout: models.DefaultIdleTimeout,
|
|
Memory: models.DefaultMemory,
|
|
},
|
|
}
|
|
}
|
|
|
|
type Harness struct {
|
|
ctx context.Context
|
|
t *testing.T
|
|
ds models.Datastore
|
|
appIds []string
|
|
}
|
|
|
|
func (h *Harness) GivenAppInDb(app *models.App) *models.App {
|
|
a, err := h.ds.InsertApp(h.ctx, app)
|
|
if err != nil {
|
|
h.t.Fatal("failed to create app", err)
|
|
return nil
|
|
}
|
|
h.AppForDeletion(a)
|
|
return a
|
|
}
|
|
|
|
func (h *Harness) Cleanup() {
|
|
for _, appId := range h.appIds {
|
|
err := h.ds.RemoveApp(h.ctx, appId)
|
|
if err != nil && err != models.ErrAppsNotFound {
|
|
h.t.Fatalf("Failed to cleanup app %s %s", appId, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (h *Harness) GivenFnInDb(validFunc *models.Fn) *models.Fn {
|
|
fn, err := h.ds.InsertFn(h.ctx, validFunc)
|
|
if err != nil {
|
|
h.t.Fatalf("Failed to insert function %s", err)
|
|
return nil
|
|
}
|
|
return fn
|
|
|
|
}
|
|
|
|
func (h *Harness) GivenTriggerInDb(validTrigger *models.Trigger) *models.Trigger {
|
|
trigger, err := h.ds.InsertTrigger(h.ctx, validTrigger)
|
|
if err != nil {
|
|
h.t.Fatalf("Failed to insert trigger %s", err)
|
|
return nil
|
|
}
|
|
return trigger
|
|
|
|
}
|
|
|
|
func (h *Harness) AppForDeletion(app *models.App) {
|
|
h.appIds = append(h.appIds, app.ID)
|
|
}
|
|
|
|
func NewHarness(t *testing.T, ctx context.Context, ds models.Datastore) *Harness {
|
|
return &Harness{
|
|
ctx: ctx,
|
|
t: t,
|
|
ds: ds,
|
|
}
|
|
}
|
|
|
|
type AppByName []*models.App
|
|
|
|
func (a AppByName) Len() int { return len(a) }
|
|
func (a AppByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
func (a AppByName) Less(i, j int) bool { return a[i].Name < a[j].Name }
|
|
|
|
type FnByName []*models.Fn
|
|
|
|
func (f FnByName) Len() int { return len(f) }
|
|
func (f FnByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
|
func (f FnByName) Less(i, j int) bool { return f[i].Name < f[j].Name }
|
|
|
|
type TriggerByName []*models.Trigger
|
|
|
|
func (f TriggerByName) Len() int { return len(f) }
|
|
func (f TriggerByName) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
|
func (f TriggerByName) Less(i, j int) bool { return f[i].Name < f[j].Name }
|
|
|
|
func RunAppsTest(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
|
|
|
ds := dsf(t)
|
|
ctx := rp.DefaultCtx()
|
|
|
|
t.Run("apps", func(t *testing.T) {
|
|
// Testing insert app
|
|
|
|
t.Run("insert missing app name fails", func(t *testing.T) {
|
|
_, err := ds.InsertApp(ctx, &models.App{})
|
|
if err != models.ErrMissingName {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrMissingName, err)
|
|
}
|
|
})
|
|
|
|
t.Run("insert sets created time and updated time ", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
start := time.Now()
|
|
returnedApp, err := ds.InsertApp(ctx, rp.ValidApp())
|
|
if err != nil {
|
|
t.Fatalf("Expected succcess, got %s", err)
|
|
}
|
|
h.AppForDeletion(returnedApp)
|
|
|
|
if !time.Time(returnedApp.CreatedAt).After(start) {
|
|
t.Fatalf("expected created to be set %s", returnedApp.CreatedAt)
|
|
}
|
|
|
|
if !time.Time(returnedApp.UpdatedAt).After(start) {
|
|
t.Fatalf("expected updated to be set %s", returnedApp.UpdatedAt)
|
|
}
|
|
})
|
|
|
|
t.Run("update sets update time ", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
// Set a config var
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
testApp.Config = map[string]string{"TEST": "1"}
|
|
updated, err := ds.UpdateApp(ctx, testApp)
|
|
|
|
if err != nil {
|
|
t.Fatalf("didn't update app %s", err)
|
|
}
|
|
|
|
if !time.Time(updated.UpdatedAt).After(time.Time(testApp.UpdatedAt)) {
|
|
t.Errorf("Expected updated time to be after original %s !> %s", updated.UpdatedAt, testApp.UpdatedAt)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("update no-op", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
// Set a config var
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
time.Sleep(1 * time.Millisecond)
|
|
updated, err := ds.UpdateApp(ctx, testApp)
|
|
if err != nil {
|
|
t.Fatalf("Expected succes got %s", err)
|
|
}
|
|
if updated == testApp {
|
|
t.Fatalf("Update should return a new value")
|
|
}
|
|
if updated.UpdatedAt.String() != testApp.UpdatedAt.String() {
|
|
t.Fatalf("Expected app not to be updated but update times weren't equal %s != %s", updated.UpdatedAt, testApp.UpdatedAt)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("update with new config var", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
// Set a config var
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testApp.Config = map[string]string{"TEST": "1"}
|
|
updated, err := ds.UpdateApp(ctx, testApp)
|
|
if err != nil {
|
|
t.Fatalf("error when updating app: %v", err)
|
|
}
|
|
expected := &models.App{ID: testApp.ID, Name: testApp.Name, Config: map[string]string{"TEST": "1"}}
|
|
if !expected.EqualsWithAnnotationSubset(updated) {
|
|
t.Fatalf("expected updated `%v` but got `%v`", expected, updated)
|
|
}
|
|
})
|
|
|
|
t.Run("set multiple config vars", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testApp.Config = map[string]string{"TEST": "1"}
|
|
updated, err := ds.UpdateApp(ctx, testApp)
|
|
if err != nil {
|
|
t.Fatalf("error when updating app: %v", err)
|
|
}
|
|
// Set a different var (without clearing the existing)
|
|
another := testApp.Clone()
|
|
another.Config = map[string]string{"OTHER": "TEST"}
|
|
updated, err = ds.UpdateApp(ctx, another)
|
|
if err != nil {
|
|
t.Fatalf("error when updating app: %v", err)
|
|
}
|
|
expected := &models.App{Name: testApp.Name, ID: testApp.ID, Config: map[string]string{"TEST": "1", "OTHER": "TEST"}}
|
|
if !expected.EqualsWithAnnotationSubset(updated) {
|
|
t.Fatalf("expected updated `%v` but got `%v`", expected, updated)
|
|
}
|
|
})
|
|
|
|
t.Run("Insert duplicate named app", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
|
|
testApp2 := rp.ValidApp()
|
|
testApp2.Name = testApp.Name
|
|
|
|
_, err := ds.InsertApp(ctx, testApp2)
|
|
if err != models.ErrAppsAlreadyExists {
|
|
t.Fatalf("Expecting duplicate error got %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Update name is immutable", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testApp.Name = "other"
|
|
|
|
_, err := ds.UpdateApp(ctx, testApp)
|
|
if err != models.ErrAppsNameImmutable {
|
|
t.Fatalf("Expecting name immutable %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("remove config var", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
app := rp.ValidApp()
|
|
app.Config = map[string]string{"OTHER": "TEST", "TEST": "1"}
|
|
|
|
// Delete a var
|
|
testApp := h.GivenAppInDb(app)
|
|
testApp.Config = map[string]string{"TEST": ""}
|
|
updated, err := ds.UpdateApp(ctx, testApp)
|
|
if err != nil {
|
|
t.Fatalf("error when updating app: %v", err)
|
|
}
|
|
expected := &models.App{Name: testApp.Name, ID: testApp.ID, Config: map[string]string{"OTHER": "TEST"}}
|
|
if !expected.EqualsWithAnnotationSubset(updated) {
|
|
t.Fatalf("expected updated `%#v` but got `%#v`", expected, updated)
|
|
}
|
|
})
|
|
|
|
// Testing get app
|
|
|
|
t.Run("Get with empty App ID", func(t *testing.T) {
|
|
_, err := ds.GetAppByID(ctx, "")
|
|
if err != models.ErrAppsMissingID {
|
|
t.Fatalf("expected error to be %v, but it was %s", models.ErrAppsMissingID, err)
|
|
}
|
|
})
|
|
|
|
t.Run("Get app by ID ", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
app, err := ds.GetAppByID(ctx, testApp.ID)
|
|
if err != nil {
|
|
t.Fatalf("error: %s", err)
|
|
}
|
|
if app.Name != testApp.Name {
|
|
t.Fatalf("expected `app.Name` to be `%s` but it was `%s`", app.Name, testApp.Name)
|
|
}
|
|
})
|
|
|
|
t.Run("List apps", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
apps, err := ds.GetApps(ctx, &models.AppFilter{PerPage: 100})
|
|
if err != nil {
|
|
t.Fatalf("not expecting err %s", err)
|
|
}
|
|
|
|
if len(apps.Items) != 0 {
|
|
t.Fatalf("expecting 0 results, got %d", len(apps.Items))
|
|
}
|
|
if apps.Items == nil {
|
|
t.Fatalf("response items must not be nil")
|
|
}
|
|
|
|
a1 := h.GivenAppInDb(rp.ValidApp())
|
|
h.GivenAppInDb(rp.ValidApp())
|
|
|
|
// Testing list apps
|
|
apps, err = ds.GetApps(ctx, &models.AppFilter{PerPage: 100})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error %v", err)
|
|
}
|
|
if len(apps.Items) != 2 {
|
|
t.Fatalf("expected result count to be 2, got %d", len(apps.Items))
|
|
}
|
|
apps, err = ds.GetApps(ctx, &models.AppFilter{PerPage: 100, Name: a1.Name})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error %v", err)
|
|
}
|
|
if len(apps.Items) != 1 {
|
|
t.Fatalf("expected result count to be 1, got %d", len(apps.Items))
|
|
}
|
|
for _, app := range apps.Items {
|
|
if app.Name == a1.Name {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("expected app list to contain app %s, got %#v", a1.Name, apps)
|
|
})
|
|
|
|
t.Run("Simple Pagination", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
// test pagination stuff (ordering / limits / cursoring)
|
|
a1 := h.GivenAppInDb(rp.ValidApp())
|
|
a2 := h.GivenAppInDb(rp.ValidApp())
|
|
a3 := h.GivenAppInDb(rp.ValidApp())
|
|
|
|
gendApps := []*models.App{a1, a2, a3}
|
|
sort.Sort(AppByName(gendApps))
|
|
|
|
apps, err := ds.GetApps(ctx, &models.AppFilter{PerPage: 1})
|
|
if err != nil {
|
|
t.Fatalf(" error: %s", err)
|
|
}
|
|
if len(apps.Items) != 1 {
|
|
t.Fatalf(" expected result count to be 1 but got %d", len(apps.Items))
|
|
} else if apps.Items[0].Name != gendApps[0].Name {
|
|
t.Fatalf(" expected `app.Name` to be `%s` but it was `%s`", gendApps[0].Name, apps.Items[0].Name)
|
|
}
|
|
|
|
apps, err = ds.GetApps(ctx, &models.AppFilter{PerPage: 100, Cursor: apps.NextCursor})
|
|
if err != nil {
|
|
t.Fatalf(" error: %s", err)
|
|
}
|
|
if len(apps.Items) != 2 {
|
|
t.Fatalf(" expected result count to be 2 but got %d", len(apps.Items))
|
|
} else if apps.Items[0].Name != gendApps[1].Name {
|
|
t.Fatalf(" expected `app.Name` to be `%s` but it was `%s`", gendApps[1].Name, apps.Items[0].Name)
|
|
} else if apps.Items[1].Name != gendApps[2].Name {
|
|
t.Fatalf(" expected `app.Name` to be `%s` but it was `%s`", gendApps[2].Name, apps.Items[1].Name)
|
|
}
|
|
|
|
a4 := h.GivenAppInDb(rp.ValidApp())
|
|
gendApps = append(gendApps, a4)
|
|
sort.Sort(AppByName(gendApps))
|
|
|
|
apps, err = ds.GetApps(ctx, &models.AppFilter{PerPage: 100})
|
|
if err != nil {
|
|
t.Fatalf(" error: %s", err)
|
|
}
|
|
if len(apps.Items) != 4 {
|
|
t.Fatalf(" expected result count to be 4 but got %d", len(apps.Items))
|
|
} else if apps.Items[3].Name != gendApps[3].Name {
|
|
t.Fatalf(" expected `app.Name` to be `%s` but it was `%s`", gendApps[4].Name, apps.Items[0].Name)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("delete app with empty Id", func(t *testing.T) {
|
|
// Testing app delete
|
|
err := ds.RemoveApp(ctx, "")
|
|
if err != models.ErrAppsMissingID {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrAppsMissingID, err)
|
|
}
|
|
})
|
|
|
|
t.Run("delete app results in app not existing", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
err := ds.RemoveApp(ctx, testApp.ID)
|
|
if err != nil {
|
|
t.Fatalf("error: %s", err)
|
|
}
|
|
app, err := ds.GetAppByID(ctx, testApp.ID)
|
|
if err != models.ErrAppsNotFound {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
|
|
}
|
|
if app != nil {
|
|
t.Log(err.Error())
|
|
t.Fatal("failed to remove the app, app should be gone already")
|
|
}
|
|
})
|
|
|
|
t.Run("cannot update non-existant app ", func(t *testing.T) {
|
|
missingApp := &models.App{
|
|
ID: "nonexistant",
|
|
Name: "nonexistant",
|
|
}
|
|
_, err := ds.UpdateApp(ctx, missingApp)
|
|
if err != models.ErrAppsNotFound {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
|
|
}
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
func RunFnsTest(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
|
|
|
ds := dsf(t)
|
|
ctx := rp.DefaultCtx()
|
|
|
|
t.Run("Fns", func(t *testing.T) {
|
|
|
|
// Testing insert fn
|
|
t.Run("empty function", func(t *testing.T) {
|
|
_, err := ds.InsertFn(ctx, nil)
|
|
if err != models.ErrDatastoreEmptyFn {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyFn, err)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("invalid app ID", func(t *testing.T) {
|
|
testFn := rp.ValidFn("notreal")
|
|
_, err := ds.InsertFn(ctx, testFn)
|
|
if err != models.ErrAppsNotFound {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("non-empty function ID", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
|
|
testFn := rp.ValidFn(testApp.ID)
|
|
testFn.ID = "abc"
|
|
|
|
_, err := ds.InsertFn(ctx, testFn)
|
|
if err != models.ErrFnsIDProvided {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrFnsIDProvided, err)
|
|
}
|
|
})
|
|
|
|
t.Run("insert valid func", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
|
|
testFn := rp.ValidFn(testApp.ID)
|
|
testFn, err := ds.InsertFn(ctx, testFn)
|
|
if err != nil {
|
|
t.Fatalf("error when storing perfectly good fn: %s", err)
|
|
}
|
|
})
|
|
|
|
// Testing get
|
|
t.Run("Get with empty function ID", func(t *testing.T) {
|
|
_, err := ds.GetFnByID(ctx, "")
|
|
if err != models.ErrDatastoreEmptyFnID {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyFnID, err)
|
|
}
|
|
})
|
|
|
|
t.Run("Get with valid function", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
fn, err := ds.GetFnByID(ctx, testFn.ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error %v : %s", err, testFn.ID)
|
|
}
|
|
if !testFn.EqualsWithAnnotationSubset(fn) {
|
|
t.Fatalf("expected to get the right func:\n%v\nbut got:\n%v", testFn, fn)
|
|
}
|
|
})
|
|
|
|
// Testing update
|
|
t.Run("Update function add values", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
|
|
// Update some fields, and add 3 configs
|
|
updated, err := ds.UpdateFn(ctx, &models.Fn{
|
|
ID: testFn.ID,
|
|
Name: testFn.Name,
|
|
AppID: testFn.AppID,
|
|
Config: map[string]string{
|
|
"FIRST": "1",
|
|
"SECOND": "2",
|
|
"THIRD": "3",
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
expected := &models.Fn{
|
|
// unchanged
|
|
ID: testFn.ID,
|
|
Name: testFn.Name,
|
|
AppID: testApp.ID,
|
|
Image: "fnproject/fn-test-utils",
|
|
ResourceConfig: models.ResourceConfig{
|
|
Timeout: testFn.Timeout,
|
|
IdleTimeout: testFn.IdleTimeout,
|
|
Memory: testFn.Memory,
|
|
},
|
|
// updated
|
|
Config: map[string]string{
|
|
"FIRST": "1",
|
|
"SECOND": "2",
|
|
"THIRD": "3",
|
|
},
|
|
}
|
|
if !expected.EqualsWithAnnotationSubset(updated) {
|
|
t.Fatalf("expected updated `%#v` but got `%#v`", expected, updated)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("Update function modify and remove values", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
fn := rp.ValidFn(testApp.ID)
|
|
|
|
fn.Config = map[string]string{
|
|
"FIRST": "1",
|
|
"SECOND": "2",
|
|
"THIRD": "3",
|
|
}
|
|
|
|
testFn := h.GivenFnInDb(fn)
|
|
|
|
// Update a config var, remove another. Add one Header, remove another.
|
|
updated, err := ds.UpdateFn(ctx, &models.Fn{
|
|
ID: testFn.ID,
|
|
Name: testFn.Name,
|
|
AppID: testFn.AppID,
|
|
Config: map[string]string{
|
|
"FIRST": "first",
|
|
"SECOND": "",
|
|
"THIRD": "3",
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
expected := &models.Fn{
|
|
// unchanged
|
|
ID: testFn.ID,
|
|
Name: testFn.Name,
|
|
AppID: testApp.ID,
|
|
Image: "fnproject/fn-test-utils",
|
|
ResourceConfig: models.ResourceConfig{
|
|
Timeout: testFn.Timeout,
|
|
IdleTimeout: testFn.IdleTimeout,
|
|
Memory: testFn.Memory,
|
|
},
|
|
// updated
|
|
Config: map[string]string{
|
|
"FIRST": "first",
|
|
"THIRD": "3",
|
|
},
|
|
}
|
|
if !expected.EqualsWithAnnotationSubset(updated) {
|
|
t.Fatalf("expected updated:\n`%v`\nbut got:\n`%v`", expected, updated)
|
|
}
|
|
})
|
|
|
|
t.Run("basic pagination no functions", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
// Testing list fns
|
|
fns, err := ds.GetFns(ctx, &models.FnFilter{AppID: testApp.ID, PerPage: 1})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error %v", err)
|
|
}
|
|
if len(fns.Items) != 0 {
|
|
t.Fatal("expected result count to be 0")
|
|
}
|
|
if fns.Items == nil {
|
|
t.Fatal("response items must not be nil")
|
|
}
|
|
})
|
|
|
|
t.Run("basic pagination with funcs", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
f1 := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
f2 := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
f3 := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
|
|
gendFns := []*models.Fn{f1, f2, f3}
|
|
sort.Sort(FnByName(gendFns))
|
|
|
|
// Testing list fns
|
|
fns, err := ds.GetFns(ctx, &models.FnFilter{AppID: testApp.ID})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error %v", err)
|
|
}
|
|
if len(fns.Items) != 3 {
|
|
t.Fatalf("expected result count to be 3, but was %d", len(fns.Items))
|
|
}
|
|
fns, err = ds.GetFns(ctx, &models.FnFilter{AppID: testApp.ID, PerPage: 1})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error %v", err)
|
|
}
|
|
if len(fns.Items) != 1 {
|
|
t.Fatalf("expected result count to be 1, but was %d", len(fns.Items))
|
|
}
|
|
|
|
if !gendFns[0].EqualsWithAnnotationSubset(fns.Items[0]) {
|
|
t.Fatalf("Expecting function to be %#v but was %#v", gendFns[0], fns.Items[0])
|
|
}
|
|
|
|
fns, err = ds.GetFns(ctx, &models.FnFilter{AppID: testApp.ID, PerPage: 2, Cursor: fns.NextCursor})
|
|
if err != nil {
|
|
t.Fatalf("error: %s", err)
|
|
}
|
|
if len(fns.Items) != 2 {
|
|
t.Fatalf("expected result count to be 2 but got %d", len(fns.Items))
|
|
} else if !gendFns[1].EqualsWithAnnotationSubset(fns.Items[0]) {
|
|
t.Fatalf("expected `func.Name` to be `%#v` but it was `%#v`", gendFns[1].Name, fns.Items[0].Name)
|
|
} else if !gendFns[2].EqualsWithAnnotationSubset(fns.Items[1]) {
|
|
t.Fatalf("expected `func.Name` to be `%#v` but it was `%#v`", gendFns[2], fns.Items[1])
|
|
}
|
|
})
|
|
|
|
t.Run("delete with empty fn name", func(t *testing.T) {
|
|
// Testing func delete
|
|
err := ds.RemoveFn(ctx, "")
|
|
if err != models.ErrDatastoreEmptyFnID {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrDatastoreEmptyFnID, err)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("delete valid fn", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
err := ds.RemoveFn(ctx, testFn.ID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
fn, err := ds.GetFnByID(ctx, testFn.ID)
|
|
if err != nil && err != models.ErrFnsNotFound {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrFnsNotFound, err)
|
|
}
|
|
if fn != nil {
|
|
t.Fatalf("failed to remove the func: %v", fn)
|
|
}
|
|
})
|
|
|
|
})
|
|
}
|
|
|
|
func RunTriggersTest(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
|
t.Run("triggers", func(t *testing.T) {
|
|
ds := dsf(t)
|
|
ctx := rp.DefaultCtx()
|
|
|
|
// Testing insert trigger
|
|
t.Run("insert invalid app ID", func(t *testing.T) {
|
|
newTestTrigger := rp.ValidTrigger("notreal", "fnId")
|
|
_, err := ds.InsertTrigger(ctx, newTestTrigger)
|
|
if err != models.ErrAppsNotFound {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrAppsNotFound, err)
|
|
}
|
|
})
|
|
|
|
t.Run("invalid func ID", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
_, err := ds.InsertTrigger(ctx, rp.ValidTrigger(testApp.ID, "notReal"))
|
|
if err != models.ErrFnsNotFound {
|
|
t.Fatalf("expected error `%v`, but it was `%v`", models.ErrFnsNotFound, err)
|
|
}
|
|
})
|
|
|
|
t.Run("duplicate name", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
newTrigger := rp.ValidTrigger(testApp.ID, testFn.ID)
|
|
insertedTrigger, err := ds.InsertTrigger(ctx, newTrigger)
|
|
if err != nil {
|
|
t.Fatalf("error when storing new trigger: %s", err)
|
|
}
|
|
newTrigger.ID = insertedTrigger.ID
|
|
if !newTrigger.EqualsWithAnnotationSubset(insertedTrigger) {
|
|
t.Errorf("Expecting returned trigger %#v to equal %#v", insertedTrigger, newTrigger)
|
|
}
|
|
|
|
repeatTrigger := rp.ValidTrigger(testApp.ID, testFn.ID)
|
|
repeatTrigger.Name = newTrigger.Name
|
|
_, err = ds.InsertTrigger(ctx, repeatTrigger)
|
|
if err != models.ErrTriggerExists {
|
|
t.Errorf("Expected ErrTriggerExists, not %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("valid trigger", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
newTrigger := rp.ValidTrigger(testApp.ID, testFn.ID)
|
|
insertedTrigger, err := ds.InsertTrigger(ctx, newTrigger)
|
|
if err != nil {
|
|
t.Fatalf("error when storing new trigger: %s", err)
|
|
}
|
|
if insertedTrigger.ID == "" {
|
|
t.Fatalf("No ID ")
|
|
}
|
|
newTrigger.ID = insertedTrigger.ID
|
|
if !newTrigger.EqualsWithAnnotationSubset(insertedTrigger) {
|
|
t.Errorf("Expecting returned trigger %#v to equal %#v", insertedTrigger, newTrigger)
|
|
}
|
|
})
|
|
|
|
t.Run("get trigger invalid ID", func(t *testing.T) {
|
|
_, err := ds.GetTriggerByID(ctx, "notreal")
|
|
if err != models.ErrTriggerNotFound {
|
|
t.Fatalf("was expecting models.ErrTriggerNotFound : %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("get existing trigger", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
newTrigger := rp.ValidTrigger(testApp.ID, testFn.ID)
|
|
insertedTrigger := h.GivenTriggerInDb(newTrigger)
|
|
|
|
gotTrigger, err := ds.GetTriggerByID(ctx, insertedTrigger.ID)
|
|
if err != nil {
|
|
t.Fatalf("expecting no error, got: %s", err)
|
|
}
|
|
|
|
newTrigger.ID = insertedTrigger.ID
|
|
if !newTrigger.EqualsWithAnnotationSubset(gotTrigger) {
|
|
t.Errorf("Expecting returned trigger %#v to equal %#v", gotTrigger, newTrigger)
|
|
}
|
|
})
|
|
|
|
t.Run("missing app Id", func(t *testing.T) {
|
|
emptyFilter := &models.TriggerFilter{}
|
|
_, err := ds.GetTriggers(ctx, emptyFilter)
|
|
if err != models.ErrTriggerMissingAppID {
|
|
t.Fatalf("expected models.ErrTriggerMissingAppID, but got %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("non-existant app", func(t *testing.T) {
|
|
nonMatchingFilter := &models.TriggerFilter{AppID: "notexist"}
|
|
triggers, err := ds.GetTriggers(ctx, nonMatchingFilter)
|
|
if err != nil {
|
|
t.Fatalf("expecting no error, got: %s", err)
|
|
}
|
|
if len(triggers.Items) != 0 && err == nil {
|
|
t.Fatalf("expected empty trigger list and no error, but got list [%v] and err %s", triggers.Items, err)
|
|
}
|
|
})
|
|
t.Run("duplicate trigger source of same type on same app", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
app := h.GivenAppInDb(rp.ValidApp())
|
|
fn := h.GivenFnInDb(rp.ValidFn(app.ID))
|
|
origT := h.GivenTriggerInDb(rp.ValidTrigger(app.ID, fn.ID))
|
|
|
|
newT := rp.ValidTrigger(app.ID, fn.ID)
|
|
|
|
newT.Source = origT.Source
|
|
|
|
_, err := ds.InsertTrigger(ctx, newT)
|
|
|
|
if err != models.ErrTriggerSourceExists {
|
|
t.Errorf("Expecting to fail with duplicate source on same app, got %s", err)
|
|
}
|
|
//todo ensure this doesn't apply when type is not equal
|
|
})
|
|
t.Run("app id not same as fn id ", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp1 := h.GivenAppInDb(rp.ValidApp())
|
|
testApp2 := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp1.ID))
|
|
|
|
tr := rp.ValidTrigger(testApp2.ID, testFn.ID)
|
|
|
|
_, err := ds.InsertTrigger(ctx, tr)
|
|
if err != models.ErrTriggerFnIDNotSameApp {
|
|
t.Errorf("expected error when Fn ID did not match Trigger App ID, got %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("page triggers", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
|
|
var storedTriggers []*models.Trigger
|
|
|
|
for i := 0; i < 10; i++ {
|
|
trigger := rp.ValidTrigger(testApp.ID, testFn.ID)
|
|
trigger.Source = fmt.Sprintf("/src_%v", i)
|
|
storedTriggers = append(storedTriggers, h.GivenTriggerInDb(trigger))
|
|
}
|
|
|
|
sort.Sort(TriggerByName(storedTriggers))
|
|
|
|
appIDFilter := &models.TriggerFilter{AppID: testApp.ID}
|
|
triggers, err := ds.GetTriggers(ctx, appIDFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(page triggers), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 10 {
|
|
t.Fatalf("Test GetTriggers(page triggers), expecting 10 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
for i := 1; i < 10; i++ {
|
|
if triggers.Items[i-1].Name > triggers.Items[i].Name {
|
|
t.Fatalf("Test GetTriggers(page triggers), names out of order, %s, %s", triggers.Items[i-1], triggers.Items[i])
|
|
}
|
|
}
|
|
|
|
fiveFilter := &models.TriggerFilter{AppID: testApp.ID, PerPage: 5}
|
|
triggers, err = ds.GetTriggers(ctx, fiveFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(page triggers), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 5 {
|
|
t.Fatalf("Test GetTriggers(page triggers), expecting 5 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
if !triggers.Items[i].EqualsWithAnnotationSubset(storedTriggers[i]) {
|
|
t.Fatalf("Test GetTriggers(first five page triggers), expect equal, %s, %s", triggers.Items[i], storedTriggers[i])
|
|
}
|
|
}
|
|
|
|
if triggers.NextCursor == "" {
|
|
t.Fatalf("Test GetTriggers(first five page triggers), expected Cursor but got nothing")
|
|
}
|
|
|
|
secondFiveFilter := &models.TriggerFilter{AppID: testApp.ID, PerPage: 5, Cursor: triggers.NextCursor}
|
|
triggers, err = ds.GetTriggers(ctx, secondFiveFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(second five page triggers), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 5 {
|
|
t.Fatalf("Test GetTriggers(second five page triggers), expecting 5 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
for i := 0; i < 5; i++ {
|
|
if !triggers.Items[i].EqualsWithAnnotationSubset(storedTriggers[i+5]) {
|
|
t.Fatalf("Test GetTriggers(second five page triggers), expect equal, %s, %s", triggers.Items[i], storedTriggers[i+5])
|
|
}
|
|
}
|
|
|
|
zeroFilter := &models.TriggerFilter{AppID: testApp.ID, PerPage: 0}
|
|
triggers, err = ds.GetTriggers(ctx, zeroFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(zero page triggers), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 10 {
|
|
t.Fatalf("Test GetTriggers(zero page triggers), expecting 10 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
if triggers.NextCursor != "" {
|
|
t.Fatalf("Test GetTriggers(zero page triggers), expected no NextCursor, got %s", triggers.NextCursor)
|
|
}
|
|
|
|
negativeFilter := &models.TriggerFilter{AppID: testApp.ID, PerPage: -10}
|
|
triggers, err = ds.GetTriggers(ctx, negativeFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(negative page triggers), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 10 {
|
|
t.Fatalf("Test GetTriggers(negative page triggers), expecting 10 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
if triggers.NextCursor != "" {
|
|
t.Fatalf("Test GetTriggers(negative page triggers), expected no NextCursor, got %s", triggers.NextCursor)
|
|
}
|
|
|
|
emptyListFilter := &models.TriggerFilter{AppID: "notexist"}
|
|
triggers, err = ds.GetTriggers(ctx, emptyListFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(notexist page triggers), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 0 {
|
|
t.Fatalf("Test GetTriggers(notexist page triggers), expecting 0 results, got %d", len(triggers.Items))
|
|
}
|
|
if triggers.Items == nil {
|
|
t.Fatalf("Test GetTriggers(notexist page triggers), response items must not be nil")
|
|
}
|
|
})
|
|
|
|
t.Run("filter triggers", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
testFn2 := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
|
|
var storedTriggers []*models.Trigger
|
|
|
|
for i := 0; i < 10; i++ {
|
|
trigger := rp.ValidTrigger(testApp.ID, testFn.ID)
|
|
trigger.Source = fmt.Sprintf("/src_%v", i)
|
|
storedTriggers = append(storedTriggers, h.GivenTriggerInDb(trigger))
|
|
}
|
|
|
|
trigger := rp.ValidTrigger(testApp.ID, testFn2.ID)
|
|
trigger.Source = fmt.Sprintf("/src_%v", 11)
|
|
trigger = h.GivenTriggerInDb(trigger)
|
|
storedTriggers = append(storedTriggers, trigger)
|
|
|
|
sort.Sort(TriggerByName(storedTriggers))
|
|
|
|
appIDFilter := &models.TriggerFilter{AppID: testApp.ID}
|
|
triggers, err := ds.GetTriggers(ctx, appIDFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(get all triggers for app), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 11 {
|
|
t.Fatalf("Test GetTriggers(get all triggers for app), expecting 10 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
for i := 0; i < 11; i++ {
|
|
if !storedTriggers[i].EqualsWithAnnotationSubset(triggers.Items[i]) {
|
|
t.Fatalf("Test GetTriggers(get all triggers for app), expecting ordered by names, but aren't: %+v, %+v", storedTriggers[i], triggers.Items[i])
|
|
}
|
|
}
|
|
|
|
NameFilter := &models.TriggerFilter{AppID: testApp.ID, Name: storedTriggers[0].Name}
|
|
triggers, err = ds.GetTriggers(ctx, NameFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(filter by name), not expecting err %s", err)
|
|
}
|
|
|
|
if len(triggers.Items) != 1 {
|
|
t.Fatalf("Test GetTriggers(filter by name), expecting 1 results, got %d", len(triggers.Items))
|
|
}
|
|
|
|
if !storedTriggers[0].EqualsWithAnnotationSubset(triggers.Items[0]) {
|
|
t.Fatalf("expect single result to equal first stored result : %#v != %#v", triggers.Items[4], storedTriggers[4])
|
|
}
|
|
|
|
// components are AND'd
|
|
findNothingFilter := &models.TriggerFilter{AppID: testApp.ID, FnID: testFn.ID}
|
|
triggers, err = ds.GetTriggers(ctx, findNothingFilter)
|
|
if err != nil {
|
|
t.Fatalf("Test GetTriggers(AND filtering), not expecting err %s", err)
|
|
}
|
|
if len(triggers.Items) != 10 {
|
|
t.Fatalf("Test GetTriggers(AND filtering), expecting 10 results, got %d", len(triggers.Items))
|
|
}
|
|
})
|
|
|
|
t.Run("update triggers", func(t *testing.T) {
|
|
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
testTrigger := h.GivenTriggerInDb(rp.ValidTrigger(testApp.ID, testFn.ID))
|
|
|
|
testTrigger.Name = "newName"
|
|
testTrigger.Source = "/newSource"
|
|
|
|
time.Sleep(10 * time.Millisecond)
|
|
gotTrigger, err := ds.UpdateTrigger(ctx, testTrigger)
|
|
if err != nil {
|
|
t.Fatalf("error when updating trigger: %s", err)
|
|
}
|
|
|
|
if !testTrigger.EqualsWithAnnotationSubset(gotTrigger) {
|
|
t.Fatalf("expecting returned triggers equal, got : %#v : %#v", testTrigger, gotTrigger)
|
|
}
|
|
|
|
gotTrigger, err = ds.GetTriggerByID(ctx, testTrigger.ID)
|
|
if err != nil {
|
|
t.Fatalf("wasn't expecting an error : %s", err)
|
|
}
|
|
if !testTrigger.EqualsWithAnnotationSubset(gotTrigger) {
|
|
t.Fatalf("expecting fetch trigger to be updated got : %v : %v", testTrigger, gotTrigger)
|
|
}
|
|
|
|
if testTrigger.CreatedAt.String() != gotTrigger.CreatedAt.String() {
|
|
t.Fatalf("create timestamps should match : %v : %v", testTrigger.CreatedAt, gotTrigger.CreatedAt)
|
|
}
|
|
|
|
if testTrigger.UpdatedAt.String() == gotTrigger.UpdatedAt.String() {
|
|
t.Fatalf("update timestamps shouldn't match : %v : %v", testTrigger, gotTrigger)
|
|
}
|
|
|
|
})
|
|
|
|
t.Run("remove non-existant", func(t *testing.T) {
|
|
err := ds.RemoveTrigger(ctx, "nonexistant")
|
|
|
|
if err != models.ErrTriggerNotFound {
|
|
t.Fatalf("Expecting trigger not found , got %v ", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Remove existing", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
testTrigger := h.GivenTriggerInDb(rp.ValidTrigger(testApp.ID, testFn.ID))
|
|
err := ds.RemoveTrigger(ctx, testTrigger.ID)
|
|
|
|
if err != nil {
|
|
t.Fatalf("expecting no error, got %s", err)
|
|
}
|
|
|
|
_, err = ds.GetTriggerByID(ctx, testTrigger.ID)
|
|
if err != models.ErrTriggerNotFound {
|
|
t.Fatalf("was expecting ErrTriggerNotFound : %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("Remove function should remove triggers", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
testTrigger := h.GivenTriggerInDb(rp.ValidTrigger(testApp.ID, testFn.ID))
|
|
err := ds.RemoveFn(ctx, testFn.ID)
|
|
if err != nil {
|
|
t.Fatalf("expecting no error, got %s", err)
|
|
}
|
|
|
|
tr, err := ds.GetTriggerByID(ctx, testTrigger.ID)
|
|
if err != models.ErrTriggerNotFound {
|
|
t.Fatalf("was expecting ErrTriggerNotFound got %s %#v", err, tr)
|
|
}
|
|
})
|
|
|
|
t.Run("Remove app should remove triggers", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
testTrigger := h.GivenTriggerInDb(rp.ValidTrigger(testApp.ID, testFn.ID))
|
|
err := ds.RemoveApp(ctx, testFn.AppID)
|
|
if err != nil {
|
|
t.Fatalf("expecting no error, got %s", err)
|
|
}
|
|
|
|
tr, err := ds.GetTriggerByID(ctx, testTrigger.ID)
|
|
if err != models.ErrTriggerNotFound {
|
|
t.Fatalf("was expecting ErrTriggerNotFound got %s %#v", err, tr)
|
|
}
|
|
})
|
|
|
|
})
|
|
}
|
|
|
|
func RunTriggerBySourceTests(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
|
|
|
t.Run("http_trigger_access", func(t *testing.T) {
|
|
ds := dsf(t)
|
|
ctx := rp.DefaultCtx()
|
|
t.Run("get_non_existant_trigger", func(t *testing.T) {
|
|
_, err := ds.GetTriggerBySource(ctx, "none", "http", "/source")
|
|
if err != models.ErrTriggerNotFound {
|
|
t.Fatalf("Expecting trigger not found, got %s", err)
|
|
}
|
|
})
|
|
|
|
t.Run("get_trigger_specific_http_route", func(t *testing.T) {
|
|
h := NewHarness(t, ctx, ds)
|
|
defer h.Cleanup()
|
|
testApp := h.GivenAppInDb(rp.ValidApp())
|
|
testFn := h.GivenFnInDb(rp.ValidFn(testApp.ID))
|
|
testTrigger := h.GivenTriggerInDb(rp.ValidTrigger(testApp.ID, testFn.ID))
|
|
trigger, err := ds.GetTriggerBySource(ctx, testApp.ID, testTrigger.Type, testTrigger.Source)
|
|
|
|
if err != nil {
|
|
t.Fatalf("Expecting trigger, got error %s", err)
|
|
}
|
|
|
|
if !trigger.Equals(testTrigger) {
|
|
t.Errorf("Expecting trigger %#v got %#v", testTrigger, trigger)
|
|
}
|
|
})
|
|
|
|
})
|
|
|
|
}
|
|
|
|
func RunAllTests(t *testing.T, dsf DataStoreFunc, rp ResourceProvider) {
|
|
buf := setLogBuffer()
|
|
defer func() {
|
|
if t.Failed() {
|
|
t.Log(buf.String())
|
|
}
|
|
}()
|
|
|
|
RunAppsTest(t, dsf, rp)
|
|
RunFnsTest(t, dsf, rp)
|
|
RunTriggersTest(t, dsf, rp)
|
|
RunTriggerBySourceTests(t, dsf, rp)
|
|
|
|
}
|