From d6b7e0c9bf4eecf6821f88628992c780f7dc0faf Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Wed, 11 Sep 2019 13:40:06 +0200 Subject: [PATCH] Add integ. tests for automation scripts & triggers --- compose/rest/automation_trigger.go | 18 ++- compose/service/automation_script.go | 9 +- compose/service/automation_trigger.go | 13 +- compose/service/service.go | 48 +++--- pkg/automation/script.go | 4 + pkg/automation/script_repository_test.go | 57 ------- pkg/automation/trigger_repository_test.go | 82 ---------- system/rest/automation_trigger.go | 15 +- system/service/automation_script.go | 6 +- system/service/error.go | 1 + system/service/service.go | 61 ++++--- tests/compose/automation_script_test.go | 171 +++++++++++++++++++- tests/compose/automation_trigger_test.go | 186 +++++++++++++++++++++- tests/system/automation_script_test.go | 157 +++++++++++++++++- tests/system/automation_trigger_test.go | 169 +++++++++++++++++++- 15 files changed, 779 insertions(+), 218 deletions(-) delete mode 100644 pkg/automation/script_repository_test.go delete mode 100644 pkg/automation/trigger_repository_test.go diff --git a/compose/rest/automation_trigger.go b/compose/rest/automation_trigger.go index 7e1179281..e1d36acde 100644 --- a/compose/rest/automation_trigger.go +++ b/compose/rest/automation_trigger.go @@ -77,7 +77,7 @@ func (ctrl AutomationTrigger) List(ctx context.Context, r *request.AutomationTri func (ctrl AutomationTrigger) Create(ctx context.Context, r *request.AutomationTriggerCreate) (interface{}, error) { s, _, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, 0) if err != nil { - return nil, errors.Wrap(err, "can not create trigger") + return nil, err } var ( @@ -94,9 +94,9 @@ func (ctrl AutomationTrigger) Create(ctx context.Context, r *request.AutomationT } func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTriggerRead) (interface{}, error) { - _, t, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, 0) + _, t, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, r.TriggerID) if err != nil { - return nil, errors.Wrap(err, "can not read trigger") + return nil, err } return ctrl.makePayload(ctx, t, err) @@ -105,7 +105,7 @@ func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTri func (ctrl AutomationTrigger) Update(ctx context.Context, r *request.AutomationTriggerUpdate) (interface{}, error) { s, t, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, r.TriggerID) if err != nil { - return nil, errors.Wrap(err, "can not update trigger") + return nil, err } t.Event = r.Event @@ -120,7 +120,7 @@ func (ctrl AutomationTrigger) Update(ctx context.Context, r *request.AutomationT func (ctrl AutomationTrigger) Delete(ctx context.Context, r *request.AutomationTriggerDelete) (interface{}, error) { s, t, err := ctrl.loadCombo(ctx, r.NamespaceID, r.ScriptID, r.TriggerID) if err != nil { - return nil, errors.Wrap(err, "can not update trigger") + return nil, err } return resputil.OK(), ctrl.triggers.Delete(ctx, s, t) @@ -128,13 +128,15 @@ func (ctrl AutomationTrigger) Delete(ctx context.Context, r *request.AutomationT func (ctrl AutomationTrigger) loadCombo(ctx context.Context, namespaceID, scriptID, triggerID uint64) (s *automation.Script, t *automation.Trigger, err error) { if triggerID > 0 { - t, err = ctrl.triggers.FindByID(ctx, triggerID) - return + if t, err = ctrl.triggers.FindByID(ctx, triggerID); err != nil { + return + } } if scriptID > 0 { s, err = ctrl.scripts.FindByID(ctx, namespaceID, scriptID) - if err != nil && s.NamespaceID != namespaceID { + + if err == nil && s.NamespaceID != namespaceID { err = repository.ErrNamespaceNotFound } diff --git a/compose/service/automation_script.go b/compose/service/automation_script.go index 996dea95f..5bae6d6b3 100644 --- a/compose/service/automation_script.go +++ b/compose/service/automation_script.go @@ -58,6 +58,8 @@ func AutomationScript(sm automationScriptManager) automationScript { func (svc automationScript) FindByID(ctx context.Context, namespaceID, scriptID uint64) (*automation.Script, error) { if _, s, err := svc.loadCombo(ctx, namespaceID, scriptID); err != nil { return nil, err + } else if !svc.ac.CanReadAutomationScript(ctx, s) { + return nil, ErrNoReadPermissions } else { return s, nil } @@ -114,7 +116,7 @@ func (svc automationScript) Update(ctx context.Context, namespaceID uint64, mod } if !svc.ac.CanUpdateAutomationScript(ctx, s) { - return ErrNoCreatePermissions.withStack() + return ErrNoUpdatePermissions.withStack() } // Users need to have grant privileges to @@ -152,7 +154,7 @@ func (svc automationScript) Delete(ctx context.Context, namespaceID, scriptID ui if _, s, err := svc.loadCombo(ctx, namespaceID, scriptID); err != nil { return err } else if !svc.ac.CanDeleteAutomationScript(ctx, s) { - return ErrNoCreatePermissions.withStack() + return ErrNoDeletePermissions.withStack() } else { return svc.scriptManager.DeleteScript(ctx, s) } @@ -174,9 +176,6 @@ func (svc automationScript) loadCombo(ctx context.Context, namespaceID, scriptID if scriptID > 0 { if s, err = svc.scriptManager.FindScriptByID(ctx, scriptID); err != nil { return - } else if !svc.ac.CanReadAutomationScript(ctx, s) { - err = ErrNoCreatePermissions.withStack() - return } } diff --git a/compose/service/automation_trigger.go b/compose/service/automation_trigger.go index fd858856b..68ef09037 100644 --- a/compose/service/automation_trigger.go +++ b/compose/service/automation_trigger.go @@ -3,6 +3,7 @@ package service import ( "context" + "github.com/pkg/errors" "go.uber.org/zap" "github.com/cortezaproject/corteza-server/compose/types" @@ -93,13 +94,17 @@ func (svc automationTrigger) Delete(ctx context.Context, s *automation.Script, t // Validates trigger (in compose context) func (svc automationTrigger) isValid(ctx context.Context, s *automation.Script, t *automation.Trigger) error { + if s == nil { + return errors.WithStack(automation.ErrAutomationScriptInvalid) + } + if !t.Enabled { return nil } if t.Resource != "compose:record" { // Accepting only compose:record resources - return automation.ErrAutomationTriggerInvalidResource + return errors.WithStack(automation.ErrAutomationTriggerInvalidResource) } if t.IsDeferred() { @@ -114,18 +119,18 @@ func (svc automationTrigger) isValid(ctx context.Context, s *automation.Script, var moduleID = t.Uint64Condition() if t.Event != "manual" && moduleID == 0 { - return automation.ErrAutomationTriggerInvalidCondition + return errors.WithStack(automation.ErrAutomationTriggerInvalidCondition) } if moduleID > 0 { if m, err := svc.mod.With(ctx).FindByID(s.NamespaceID, moduleID); err != nil { return err } else if !svc.ac.CanManageAutomationTriggersOnModule(ctx, m) { - return ErrNoTriggerManagementPermissions + return errors.WithStack(ErrNoTriggerManagementPermissions) } } default: - return automation.ErrAutomationTriggerInvalidEvent + return errors.WithStack(automation.ErrAutomationTriggerInvalidEvent) } return nil diff --git a/compose/service/service.go b/compose/service/service.go index 20be0e6f2..956b5c6e2 100644 --- a/compose/service/service.go +++ b/compose/service/service.go @@ -22,6 +22,12 @@ type ( Watch(ctx context.Context) } + automationManager interface { + automationScriptManager + automationTriggerManager + automationScriptsFinder + } + Config struct { Storage options.StorageOpt Corredor options.CorredorOpt @@ -40,10 +46,13 @@ var ( // DefaultAccessControl Access control checking DefaultAccessControl *accessControl - // DefaultAutomationScriptManager manages scripts + // DefaultInternalAutomationManager manages automation scripts, triggers, runnable scripts + DefaultInternalAutomationManager automationManager + + // DefaultAutomationScriptManager manages compose automation scripts DefaultAutomationScriptManager automationScript - // DefaultAutomationTriggerManager manages triggerManager + // DefaultAutomationTriggerManager manages compose automation triggers DefaultAutomationTriggerManager automationTrigger // DefaultAutomationRunner runs automation scripts by listening to triggerManager and invoking Corredor service @@ -95,23 +104,24 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) { DefaultSystemUser = SystemUser(systemProto.NewUsersClient(systemClientConn)) } - // ias: Internal Automatinon Service - // handles script & trigger management & keeping runnables cripts in internal cache - ias := automation.Service(automation.AutomationServiceConfig{ - Logger: DefaultLogger, - DbTablePrefix: "compose", - DB: db, - TokenMaker: func(ctx context.Context, userID uint64) (s string, e error) { - ctx = auth.SetSuperUserContext(ctx) - return DefaultSystemUser.MakeJWT(ctx, userID) - }, - }) - - // Pass automation manager to - DefaultAutomationTriggerManager = AutomationTrigger(ias) - DefaultAutomationScriptManager = AutomationScript(ias) - { + if DefaultInternalAutomationManager == nil { + // handles script & trigger management & keeping runnable scripts in internal cache + DefaultInternalAutomationManager = automation.Service(automation.AutomationServiceConfig{ + Logger: DefaultLogger, + DbTablePrefix: "compose", + DB: db, + TokenMaker: func(ctx context.Context, userID uint64) (s string, e error) { + ctx = auth.SetSuperUserContext(ctx) + return DefaultSystemUser.MakeJWT(ctx, userID) + }, + }) + } + + // Pass internal automation manager to compose's script & trigger managers + DefaultAutomationTriggerManager = AutomationTrigger(DefaultInternalAutomationManager) + DefaultAutomationScriptManager = AutomationScript(DefaultInternalAutomationManager) + var scriptRunnerClient corredor.ScriptRunnerClient if c.Corredor.Enabled { @@ -131,7 +141,7 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) { ApiBaseURLMessaging: c.Corredor.ApiBaseURLMessaging, ApiBaseURLCompose: c.Corredor.ApiBaseURLCompose, }, - ias, + DefaultInternalAutomationManager, scriptRunnerClient, ) } diff --git a/pkg/automation/script.go b/pkg/automation/script.go index a7e6d67f3..f55f348f4 100644 --- a/pkg/automation/script.go +++ b/pkg/automation/script.go @@ -98,6 +98,10 @@ const ( STMS_REPLACE ) +var ( + ErrAutomationScriptInvalid = errors.New("AutomationScriptInvalid") +) + // IsValid - enabled, deleted? func (s *Script) IsValid() bool { return s != nil && s.Enabled && s.DeletedAt == nil diff --git a/pkg/automation/script_repository_test.go b/pkg/automation/script_repository_test.go deleted file mode 100644 index 5e5ebd83a..000000000 --- a/pkg/automation/script_repository_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build integration - -package automation - -import ( - "os" - "testing" - "time" - - "github.com/titpetric/factory" - - "github.com/cortezaproject/corteza-server/internal/test" -) - -func TestScriptRepository_findByID(t *testing.T) { - factory.Database.Add("compose", os.Getenv("COMPOSE_DB_DSN")) - - var ( - nsID = factory.Sonyflake.NextID() - scriptID = factory.Sonyflake.NextID() - - err error - ss ScriptSet - script *Script - - db = factory.Database.MustGet("compose") - srepo = ScriptRepository("compose") - ) - - db.Begin() - defer db.Rollback() - - _, err = db.Exec("insert into compose_namespace (id, name, slug, meta, enabled) values (?, 'test ns', 'test slug', '{}', true)", nsID) - test.NoError(t, err, "unexpected error") - - _, err = srepo.findByID(db, scriptID) - test.Error(t, err, "expecting error") - - // runnable script - script = &Script{ID: scriptID, CreatedAt: time.Now(), Enabled: true, NamespaceID: nsID} - err = srepo.create(db, script) - test.NoError(t, err, "unexpected error: %v") - - // find this script now - script, err = srepo.findByID(db, scriptID) - test.NoError(t, err, "script not found") - test.Assert(t, script != nil && script.ID == scriptID, "script not found (in runnable scripts)") - - // find the script through find func - ss, _, err = srepo.find(db, ScriptFilter{NamespaceID: nsID}) - test.NoError(t, err, "unexpected error: %v") - test.Assert(t, len(ss) > 0, "could not find the script") - test.Assert(t, ss.FindByID(scriptID) != nil, "could not find the script") - - err = srepo.update(db, script) - test.NoError(t, err, "unexpected error when updating the script: %v") -} diff --git a/pkg/automation/trigger_repository_test.go b/pkg/automation/trigger_repository_test.go deleted file mode 100644 index d55eaf270..000000000 --- a/pkg/automation/trigger_repository_test.go +++ /dev/null @@ -1,82 +0,0 @@ -// +build integration - -package automation - -import ( - "os" - "testing" - "time" - - "github.com/titpetric/factory" - - "github.com/cortezaproject/corteza-server/internal/test" -) - -func TestTriggerRepository_findByID(t *testing.T) { - factory.Database.Add("compose", os.Getenv("COMPOSE_DB_DSN")) - - var ( - nsID = factory.Sonyflake.NextID() - scriptID = factory.Sonyflake.NextID() - triggerID = factory.Sonyflake.NextID() - - err error - trigger *Trigger - tt TriggerSet - script *Script - - db = factory.Database.MustGet("compose") - trepo = TriggerRepository("compose") - srepo = ScriptRepository("compose") - ) - - db.Begin() - defer db.Rollback() - - _, err = db.Exec("insert into compose_namespace (id, name, slug, meta, enabled) values (?, 'test ns', 'test slug', '{}', true)", nsID) - test.NoError(t, err, "unexpected error") - - _, err = trepo.findByID(db, triggerID) - test.Error(t, err, "expecting error") - - // should have script present - script = &Script{ID: scriptID, CreatedAt: time.Now(), NamespaceID: nsID} - err = srepo.create(db, script) - test.NoError(t, err, "unexpected error: %v") - - // runnable trigger - trigger = &Trigger{ID: triggerID, Resource: "test", Event: "test", CreatedAt: time.Now(), Enabled: true, ScriptID: scriptID} - err = trepo.replace(db, trigger) - test.NoError(t, err, "unexpected error: %v") - - // find this trigger now - trigger, err = trepo.findByID(db, triggerID) - test.NoError(t, err, "trigger not found") - test.Assert(t, trigger != nil && trigger.ID == triggerID, "trigger not found") - - tt, err = trepo.findRunnable(db) - test.NoError(t, err, "unexpected error: %v") - test.Assert(t, tt.FindByID(triggerID) != nil, "trigger not found (in runnable)") - - // find the trigger through find func - tt, _, err = trepo.find(db, TriggerFilter{ - ScriptID: script.ID, - Resource: "test", - Event: "test", - }) - - test.NoError(t, err, "unexpected error: %v") - test.Assert(t, len(tt) == 1, "could not find the trigger") - test.Assert(t, tt[0].ID == triggerID, "unexpected trigger") - - err = trepo.replace(db, trigger) - test.NoError(t, err, "unexpected error when replacing the trigger: %v") - - err = trepo.deleteByScriptID(db, scriptID) - test.NoError(t, err, "unexpected error when updating the trigger: %v") - - // find this trigger now - trigger, err = trepo.findByID(db, triggerID) - test.Error(t, err, "expecting error") - -} diff --git a/system/rest/automation_trigger.go b/system/rest/automation_trigger.go index eb0fbae1a..037355941 100644 --- a/system/rest/automation_trigger.go +++ b/system/rest/automation_trigger.go @@ -76,7 +76,7 @@ func (ctrl AutomationTrigger) List(ctx context.Context, r *request.AutomationTri func (ctrl AutomationTrigger) Create(ctx context.Context, r *request.AutomationTriggerCreate) (interface{}, error) { s, _, err := ctrl.loadCombo(ctx, r.ScriptID, 0) if err != nil { - return nil, errors.Wrap(err, "can not create trigger") + return nil, err } var ( @@ -93,9 +93,9 @@ func (ctrl AutomationTrigger) Create(ctx context.Context, r *request.AutomationT } func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTriggerRead) (interface{}, error) { - _, t, err := ctrl.loadCombo(ctx, r.ScriptID, 0) + _, t, err := ctrl.loadCombo(ctx, r.ScriptID, r.TriggerID) if err != nil { - return nil, errors.Wrap(err, "can not read trigger") + return nil, err } return ctrl.makePayload(ctx, t, err) @@ -104,7 +104,7 @@ func (ctrl AutomationTrigger) Read(ctx context.Context, r *request.AutomationTri func (ctrl AutomationTrigger) Update(ctx context.Context, r *request.AutomationTriggerUpdate) (interface{}, error) { s, t, err := ctrl.loadCombo(ctx, r.ScriptID, r.TriggerID) if err != nil { - return nil, errors.Wrap(err, "can not update trigger") + return nil, err } t.Event = r.Event @@ -119,7 +119,7 @@ func (ctrl AutomationTrigger) Update(ctx context.Context, r *request.AutomationT func (ctrl AutomationTrigger) Delete(ctx context.Context, r *request.AutomationTriggerDelete) (interface{}, error) { s, t, err := ctrl.loadCombo(ctx, r.ScriptID, r.TriggerID) if err != nil { - return nil, errors.Wrap(err, "can not update trigger") + return nil, err } return resputil.OK(), ctrl.triggers.Delete(ctx, s, t) @@ -127,8 +127,9 @@ func (ctrl AutomationTrigger) Delete(ctx context.Context, r *request.AutomationT func (ctrl AutomationTrigger) loadCombo(ctx context.Context, scriptID, triggerID uint64) (s *automation.Script, t *automation.Trigger, err error) { if triggerID > 0 { - t, err = ctrl.triggers.FindByID(ctx, triggerID) - return + if t, err = ctrl.triggers.FindByID(ctx, triggerID); err != nil { + return + } } if scriptID > 0 { diff --git a/system/service/automation_script.go b/system/service/automation_script.go index 3e96de5d7..1649dc064 100644 --- a/system/service/automation_script.go +++ b/system/service/automation_script.go @@ -97,7 +97,7 @@ func (svc automationScript) Update(ctx context.Context, mod *automation.Script) } if !svc.ac.CanUpdateAutomationScript(ctx, s) { - return ErrNoCreatePermissions.withStack() + return ErrNoUpdatePermissions.withStack() } // Users need to have grant privileges to @@ -137,7 +137,7 @@ func (svc automationScript) Delete(ctx context.Context, scriptID uint64) (err er if s, err := svc.loadCombo(ctx, scriptID); err != nil { return err } else if !svc.ac.CanDeleteAutomationScript(ctx, s) { - return ErrNoCreatePermissions.withStack() + return ErrNoDeletePermissions.withStack() } else { return svc.scriptManager.DeleteScript(ctx, s) } @@ -149,7 +149,7 @@ func (svc automationScript) loadCombo(ctx context.Context, scriptID uint64) (s * if s, err = svc.scriptManager.FindScriptByID(ctx, scriptID); err != nil { return } else if !svc.ac.CanReadAutomationScript(ctx, s) { - err = ErrNoCreatePermissions.withStack() + err = ErrNoReadPermissions.withStack() return } } diff --git a/system/service/error.go b/system/service/error.go index 0a67d14ee..3d0fe8717 100644 --- a/system/service/error.go +++ b/system/service/error.go @@ -14,6 +14,7 @@ const ( ErrNoGrantPermissions serviceError = "NoGrantPermissions" ErrNoCreatePermissions serviceError = "NoCreatePermissions" ErrNoUpdatePermissions serviceError = "NoUpdatePermissions" + ErrNoDeletePermissions serviceError = "NoDeletePermissions" ErrNoReadPermissions serviceError = "NoReadPermissions" ErrNoTriggerManagementPermissions serviceError = "NoTriggerManagementPermissions" ErrNoScriptCreatePermissions serviceError = "NoScriptCreatePermissions" diff --git a/system/service/service.go b/system/service/service.go index e9260a565..3958b69b2 100644 --- a/system/service/service.go +++ b/system/service/service.go @@ -19,11 +19,18 @@ type ( db interface { Transaction(callback func() error) error } + permissionServicer interface { accessControlPermissionServicer Watch(ctx context.Context) } + automationManager interface { + automationScriptManager + automationTriggerManager + automationScriptsFinder + } + Config struct { Storage options.StorageOpt Corredor options.CorredorOpt @@ -43,6 +50,9 @@ var ( // DefaultAccessControl Access control checking DefaultAccessControl *accessControl + // DefaultInternalAutomationManager manages automation scripts, triggers, runnable scripts + DefaultInternalAutomationManager automationManager + // DefaultAutomationScriptManager manages scripts DefaultAutomationScriptManager automationScript @@ -91,31 +101,32 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) { DefaultAuthNotification = AuthNotification(ctx) DefaultAuth = Auth(ctx) - // ias: Internal Automatinon Service - // handles script & trigger management & keeping runnables cripts in internal cache - ias := automation.Service(automation.AutomationServiceConfig{ - Logger: DefaultLogger, - DbTablePrefix: "sys", - DB: repository.DB(ctx), - TokenMaker: func(ctx context.Context, userID uint64) (jwt string, err error) { - var u *types.User - - ctx = intAuth.SetSuperUserContext(ctx) - if u, err = DefaultUser.FindByID(userID); err != nil { - return - } else if err = DefaultAuth.LoadRoleMemberships(u); err != nil { - return - } - - return intAuth.DefaultJwtHandler.Encode(u), nil - }, - }) - - // Pass automation manager to - DefaultAutomationTriggerManager = AutomationTrigger(ias) - DefaultAutomationScriptManager = AutomationScript(ias) - { + if DefaultInternalAutomationManager == nil { + // handles script & trigger management & keeping runnables cripts in internal cache + DefaultInternalAutomationManager = automation.Service(automation.AutomationServiceConfig{ + Logger: DefaultLogger, + DbTablePrefix: "sys", + DB: repository.DB(ctx), + TokenMaker: func(ctx context.Context, userID uint64) (jwt string, err error) { + var u *types.User + + ctx = intAuth.SetSuperUserContext(ctx) + if u, err = DefaultUser.FindByID(userID); err != nil { + return + } else if err = DefaultAuth.LoadRoleMemberships(u); err != nil { + return + } + + return intAuth.DefaultJwtHandler.Encode(u), nil + }, + }) + } + + // Pass automation manager to + DefaultAutomationTriggerManager = AutomationTrigger(DefaultInternalAutomationManager) + DefaultAutomationScriptManager = AutomationScript(DefaultInternalAutomationManager) + var scriptRunnerClient corredor.ScriptRunnerClient if c.Corredor.Enabled { @@ -135,7 +146,7 @@ func Init(ctx context.Context, log *zap.Logger, c Config) (err error) { ApiBaseURLMessaging: c.Corredor.ApiBaseURLMessaging, ApiBaseURLCompose: c.Corredor.ApiBaseURLCompose, }, - ias, + DefaultInternalAutomationManager, scriptRunnerClient, ) } diff --git a/tests/compose/automation_script_test.go b/tests/compose/automation_script_test.go index 603fe8d54..719c61d59 100644 --- a/tests/compose/automation_script_test.go +++ b/tests/compose/automation_script_test.go @@ -1,9 +1,176 @@ package compose import ( + "context" + "fmt" + "net/http" "testing" + + jsonpath "github.com/steinfletcher/apitest-jsonpath" + + "github.com/cortezaproject/corteza-server/compose/service" + "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/pkg/automation" + "github.com/cortezaproject/corteza-server/tests/helpers" ) -func TestAutomationScript(t *testing.T) { - t.Skip("pending implementation") +// We're using pkg/automation service layer for low-level tasks +func (h helper) svcMakeAutomationScript(namespace *types.Namespace, name string) *automation.Script { + script := &automation.Script{ + NamespaceID: namespace.ID, + Name: name, + } + + h.a.NoError(service.DefaultInternalAutomationManager.CreateScript( + context.Background(), + script, + )) + + return script +} + +func TestAutomationScriptRead(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "my little automation script") + + h.apiInit(). + Get(fmt.Sprintf("/namespace/%d/automation/script/%d", ns.ID, script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + Assert(jsonpath.Equal(`$.response.name`, script.Name)). + Assert(jsonpath.Equal(`$.response.scriptID`, fmt.Sprintf("%d", script.ID))). + End() +} + +func TestAutomationScriptList(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + + h.svcMakeAutomationScript(ns, "app") + h.svcMakeAutomationScript(ns, "app") + + h.apiInit(). + Get(fmt.Sprintf("/namespace/%d/automation/script/", ns.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationScriptCreateForbidden(t *testing.T) { + h := newHelper(t) + + ns := h.repoMakeNamespace("some-namespace") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/", ns.ID)). + FormData("name", "my little automation script"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("compose.service.NoCreatePermissions")). + End() +} + +func TestAutomationScriptCreate(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.NamespacePermissionResource.AppendWildcard(), "automation-script.create") + + ns := h.repoMakeNamespace("some-namespace") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/", ns.ID)). + FormData("name", "my little automation script"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationScriptUpdateForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "my little automation script") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/%d", ns.ID, script.ID)). + FormData("name", "changed-name"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("compose.service.NoUpdatePermissions")). + End() +} + +func TestAutomationScriptUpdate(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "my little automation script") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/%d", ns.ID, script.ID)). + FormData("name", "changed-name"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + script, err := service.DefaultInternalAutomationManager.FindScriptByID(context.Background(), script.ID) + h.a.NoError(err) + h.a.NotNil(script) + h.a.Equal("changed-name", script.Name) +} + +func TestAutomationScriptDeleteForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "my little automation script") + + h.apiInit(). + Delete(fmt.Sprintf("/namespace/%d/automation/script/%d", ns.ID, script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("compose.service.NoDeletePermissions")). + End() +} + +func TestAutomationScriptDelete(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "delete") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "my little automation script") + + h.apiInit(). + Delete(fmt.Sprintf("/namespace/%d/automation/script/%d", ns.ID, script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + script, err := service.DefaultInternalAutomationManager.FindScriptByID(context.Background(), script.ID) + h.a.NoError(err) + h.a.NotNil(script) + h.a.NotNil(script.DeletedAt) } diff --git a/tests/compose/automation_trigger_test.go b/tests/compose/automation_trigger_test.go index 1d9511143..3c269abf9 100644 --- a/tests/compose/automation_trigger_test.go +++ b/tests/compose/automation_trigger_test.go @@ -1,9 +1,191 @@ package compose import ( + "context" + "fmt" + "net/http" "testing" + + jsonpath "github.com/steinfletcher/apitest-jsonpath" + + "github.com/cortezaproject/corteza-server/compose/service" + "github.com/cortezaproject/corteza-server/compose/types" + "github.com/cortezaproject/corteza-server/pkg/automation" + "github.com/cortezaproject/corteza-server/tests/helpers" ) -func TestAutomationTrigger(t *testing.T) { - t.Skip("pending implementation") +// We're using pkg/automation service layer for low-level tasks +func (h helper) svcMakeAutomationTrigger(script *automation.Script, event string) *automation.Trigger { + trigger := &automation.Trigger{ + ScriptID: script.ID, + Event: event, + } + + h.a.NoError(service.DefaultInternalAutomationManager.CreateTrigger( + context.Background(), + script, + trigger, + )) + + return trigger +} + +func TestAutomationTriggerRead(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + trigger := h.svcMakeAutomationTrigger(script, "manual") + + h.apiInit(). + Get(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/%d", ns.ID, script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + Assert(jsonpath.Equal(`$.response.event`, trigger.Event)). + Assert(jsonpath.Equal(`$.response.triggerID`, fmt.Sprintf("%d", trigger.ID))). + End() +} + +func TestAutomationTriggerList(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + + h.svcMakeAutomationTrigger(script, "app") + h.svcMakeAutomationTrigger(script, "app") + + h.apiInit(). + Get(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/", ns.ID, script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationTriggerCreateForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/", ns.ID, script.ID)). + FormData("event", "my-event"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("compose.service.NoTriggerManagementPermissions")). + End() +} + +func TestAutomationTriggerCreate(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/", ns.ID, script.ID)). + FormData("event", "my-event"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationTriggerUpdateForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/%d", ns.ID, script.ID, trigger.ID)). + FormData("name", "manual"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("compose.service.NoTriggerManagementPermissions")). + End() +} + +func TestAutomationTriggerUpdate(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Post(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/%d", ns.ID, script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + trigger, err := service.DefaultInternalAutomationManager.FindTriggerByID(context.Background(), trigger.ID) + h.a.NoError(err) + h.a.NotNil(trigger) +} + +func TestAutomationTriggerDeleteForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Delete(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/%d", ns.ID, script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("compose.service.NoTriggerManagementPermissions")). + End() +} + +func TestAutomationTriggerDelete(t *testing.T) { + h := newHelper(t) + + h.allow(types.NamespacePermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + ns := h.repoMakeNamespace("some-namespace") + script := h.svcMakeAutomationScript(ns, "dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Delete(fmt.Sprintf("/namespace/%d/automation/script/%d/trigger/%d", ns.ID, script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + trigger, err := service.DefaultInternalAutomationManager.FindTriggerByID(context.Background(), trigger.ID) + h.a.NoError(err) + h.a.NotNil(trigger) + h.a.NotNil(trigger.DeletedAt) } diff --git a/tests/system/automation_script_test.go b/tests/system/automation_script_test.go index 8b3ead789..f39f26e0b 100644 --- a/tests/system/automation_script_test.go +++ b/tests/system/automation_script_test.go @@ -1,9 +1,162 @@ package system import ( + "context" + "fmt" + "net/http" "testing" + + jsonpath "github.com/steinfletcher/apitest-jsonpath" + + "github.com/cortezaproject/corteza-server/pkg/automation" + "github.com/cortezaproject/corteza-server/system/service" + "github.com/cortezaproject/corteza-server/system/types" + "github.com/cortezaproject/corteza-server/tests/helpers" ) -func TestAutomationScript(t *testing.T) { - t.Skip("pending implementation") +// We're using pkg/automation service layer for low-level tasks +func (h helper) svcMakeAutomationScript(name string) *automation.Script { + script := &automation.Script{ + Name: name, + } + + h.a.NoError(service.DefaultInternalAutomationManager.CreateScript( + context.Background(), + script, + )) + + return script +} + +func TestAutomationScriptRead(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("my little automation script") + + h.apiInit(). + Get(fmt.Sprintf("/automation/script/%d", script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + Assert(jsonpath.Equal(`$.response.name`, script.Name)). + Assert(jsonpath.Equal(`$.response.scriptID`, fmt.Sprintf("%d", script.ID))). + End() +} + +func TestAutomationScriptList(t *testing.T) { + h := newHelper(t) + + h.svcMakeAutomationScript("app") + h.svcMakeAutomationScript("app") + + h.apiInit(). + Get(fmt.Sprintf("/automation/script/")). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationScriptCreateForbidden(t *testing.T) { + h := newHelper(t) + + h.deny(types.SystemPermissionResource, "automation-script.create") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/")). + FormData("name", "my little automation script"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("system.service.NoCreatePermissions")). + End() +} + +func TestAutomationScriptCreate(t *testing.T) { + h := newHelper(t) + + h.allow(types.SystemPermissionResource, "automation-script.create") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/")). + FormData("name", "my little automation script"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationScriptUpdateForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("my little automation script") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/%d", script.ID)). + FormData("name", "changed-name"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("system.service.NoUpdatePermissions")). + End() +} + +func TestAutomationScriptUpdate(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + script := h.svcMakeAutomationScript("my little automation script") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/%d", script.ID)). + FormData("name", "changed-name"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + script, err := service.DefaultInternalAutomationManager.FindScriptByID(context.Background(), script.ID) + h.a.NoError(err) + h.a.NotNil(script) + h.a.Equal("changed-name", script.Name) +} + +func TestAutomationScriptDeleteForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("my little automation script") + + h.apiInit(). + Delete(fmt.Sprintf("/automation/script/%d", script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("system.service.NoDeletePermissions")). + End() +} + +func TestAutomationScriptDelete(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "delete") + + script := h.svcMakeAutomationScript("my little automation script") + + h.apiInit(). + Delete(fmt.Sprintf("/automation/script/%d", script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + script, err := service.DefaultInternalAutomationManager.FindScriptByID(context.Background(), script.ID) + h.a.NoError(err) + h.a.NotNil(script) + h.a.NotNil(script.DeletedAt) } diff --git a/tests/system/automation_trigger_test.go b/tests/system/automation_trigger_test.go index 9a89a5a24..f5ad25b99 100644 --- a/tests/system/automation_trigger_test.go +++ b/tests/system/automation_trigger_test.go @@ -1,9 +1,174 @@ package system import ( + "context" + "fmt" + "net/http" "testing" + + jsonpath "github.com/steinfletcher/apitest-jsonpath" + + "github.com/cortezaproject/corteza-server/pkg/automation" + "github.com/cortezaproject/corteza-server/system/service" + "github.com/cortezaproject/corteza-server/system/types" + "github.com/cortezaproject/corteza-server/tests/helpers" ) -func TestAutomationTrigger(t *testing.T) { - t.Skip("pending implementation") +// We're using pkg/automation service layer for low-level tasks +func (h helper) svcMakeAutomationTrigger(script *automation.Script, event string) *automation.Trigger { + trigger := &automation.Trigger{ + ScriptID: script.ID, + Event: event, + } + + h.a.NoError(service.DefaultInternalAutomationManager.CreateTrigger( + context.Background(), + script, + trigger, + )) + + return trigger +} + +func TestAutomationTriggerRead(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("dummy") + trigger := h.svcMakeAutomationTrigger(script, "manual") + + h.apiInit(). + Get(fmt.Sprintf("/automation/script/%d/trigger/%d", script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + Assert(jsonpath.Equal(`$.response.event`, trigger.Event)). + Assert(jsonpath.Equal(`$.response.triggerID`, fmt.Sprintf("%d", trigger.ID))). + End() +} + +func TestAutomationTriggerList(t *testing.T) { + h := newHelper(t) + + script := h.svcMakeAutomationScript("dummy") + + h.svcMakeAutomationTrigger(script, "app") + h.svcMakeAutomationTrigger(script, "app") + + h.apiInit(). + Get(fmt.Sprintf("/automation/script/%d/trigger/", script.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationTriggerCreateForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("dummy") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/%d/trigger/", script.ID)). + FormData("event", "my-event"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("system.service.NoTriggerManagementPermissions")). + End() +} + +func TestAutomationTriggerCreate(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + script := h.svcMakeAutomationScript("dummy") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/%d/trigger/", script.ID)). + FormData("event", "my-event"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() +} + +func TestAutomationTriggerUpdateForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/%d/trigger/%d", script.ID, trigger.ID)). + FormData("name", "manual"). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("system.service.NoTriggerManagementPermissions")). + End() +} + +func TestAutomationTriggerUpdate(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + script := h.svcMakeAutomationScript("dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Post(fmt.Sprintf("/automation/script/%d/trigger/%d", script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + trigger, err := service.DefaultInternalAutomationManager.FindTriggerByID(context.Background(), trigger.ID) + h.a.NoError(err) + h.a.NotNil(trigger) +} + +func TestAutomationTriggerDeleteForbidden(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + + script := h.svcMakeAutomationScript("dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Delete(fmt.Sprintf("/automation/script/%d/trigger/%d", script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertError("system.service.NoTriggerManagementPermissions")). + End() +} + +func TestAutomationTriggerDelete(t *testing.T) { + h := newHelper(t) + + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "read") + h.allow(types.AutomationScriptPermissionResource.AppendWildcard(), "update") + + script := h.svcMakeAutomationScript("dummy") + trigger := h.svcMakeAutomationTrigger(script, "my little automation trigger") + + h.apiInit(). + Delete(fmt.Sprintf("/automation/script/%d/trigger/%d", script.ID, trigger.ID)). + Expect(t). + Status(http.StatusOK). + Assert(helpers.AssertNoErrors). + End() + + trigger, err := service.DefaultInternalAutomationManager.FindTriggerByID(context.Background(), trigger.ID) + h.a.NoError(err) + h.a.NotNil(trigger) + h.a.NotNil(trigger.DeletedAt) }