sftpgo/internal/common/eventmanager_test.go

799 lines
22 KiB
Go
Raw Normal View History

// Copyright (C) 2019-2022 Nicola Murino
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
package common
import (
"fmt"
"net/http"
"os"
"path"
"path/filepath"
"testing"
"time"
"github.com/sftpgo/sdk"
sdkkms "github.com/sftpgo/sdk/kms"
"github.com/stretchr/testify/assert"
"github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/kms"
"github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/vfs"
)
func TestEventRuleMatch(t *testing.T) {
conditions := dataprovider.EventConditions{
ProviderEvents: []string{"add", "update"},
Options: dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: "user1",
InverseMatch: true,
},
},
},
}
res := eventManager.checkProviderEventMatch(conditions, EventParams{
Name: "user1",
Event: "add",
})
assert.False(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{
Name: "user2",
Event: "update",
})
assert.True(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{
Name: "user2",
Event: "delete",
})
assert.False(t, res)
conditions.Options.ProviderObjects = []string{"api_key"}
res = eventManager.checkProviderEventMatch(conditions, EventParams{
Name: "user2",
Event: "update",
ObjectType: "share",
})
assert.False(t, res)
res = eventManager.checkProviderEventMatch(conditions, EventParams{
Name: "user2",
Event: "update",
ObjectType: "api_key",
})
assert.True(t, res)
// now test fs events
conditions = dataprovider.EventConditions{
FsEvents: []string{operationUpload, operationDownload},
Options: dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: "user*",
},
{
Pattern: "tester*",
},
},
FsPaths: []dataprovider.ConditionPattern{
{
Pattern: "*.txt",
},
},
Protocols: []string{ProtocolSFTP},
MinFileSize: 10,
MaxFileSize: 30,
},
}
params := EventParams{
Name: "tester4",
Event: operationDelete,
VirtualPath: "/path.txt",
Protocol: ProtocolSFTP,
ObjectName: "path.txt",
FileSize: 20,
}
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
params.Event = operationDownload
res = eventManager.checkFsEventMatch(conditions, params)
assert.True(t, res)
params.Name = "name"
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
params.Name = "user5"
res = eventManager.checkFsEventMatch(conditions, params)
assert.True(t, res)
params.VirtualPath = "/sub/f.jpg"
params.ObjectName = path.Base(params.VirtualPath)
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
params.VirtualPath = "/sub/f.txt"
params.ObjectName = path.Base(params.VirtualPath)
res = eventManager.checkFsEventMatch(conditions, params)
assert.True(t, res)
params.Protocol = ProtocolHTTP
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
params.Protocol = ProtocolSFTP
params.FileSize = 5
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
params.FileSize = 50
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
params.FileSize = 25
res = eventManager.checkFsEventMatch(conditions, params)
assert.True(t, res)
// bad pattern
conditions.Options.Names = []dataprovider.ConditionPattern{
{
Pattern: "[-]",
},
}
res = eventManager.checkFsEventMatch(conditions, params)
assert.False(t, res)
}
func TestEventManager(t *testing.T) {
startEventScheduler()
action := &dataprovider.BaseEventAction{
Name: "test_action",
Type: dataprovider.ActionTypeHTTP,
Options: dataprovider.BaseEventActionOptions{
HTTPConfig: dataprovider.EventActionHTTPConfig{
Endpoint: "http://localhost",
Timeout: 20,
Method: http.MethodGet,
},
},
}
err := dataprovider.AddEventAction(action, "", "")
assert.NoError(t, err)
rule := &dataprovider.EventRule{
Name: "rule",
Trigger: dataprovider.EventTriggerFsEvent,
Conditions: dataprovider.EventConditions{
FsEvents: []string{operationUpload},
},
Actions: []dataprovider.EventAction{
{
BaseEventAction: dataprovider.BaseEventAction{
Name: action.Name,
},
Order: 1,
},
},
}
err = dataprovider.AddEventRule(rule, "", "")
assert.NoError(t, err)
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 1)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 0)
assert.Len(t, eventManager.schedulesMapping, 0)
eventManager.RUnlock()
rule.Trigger = dataprovider.EventTriggerProviderEvent
rule.Conditions = dataprovider.EventConditions{
ProviderEvents: []string{"add"},
}
err = dataprovider.UpdateEventRule(rule, "", "")
assert.NoError(t, err)
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 1)
assert.Len(t, eventManager.Schedules, 0)
assert.Len(t, eventManager.schedulesMapping, 0)
eventManager.RUnlock()
rule.Trigger = dataprovider.EventTriggerSchedule
rule.Conditions = dataprovider.EventConditions{
Schedules: []dataprovider.Schedule{
{
Hours: "0",
DayOfWeek: "*",
DayOfMonth: "*",
Month: "*",
},
},
}
rule.DeletedAt = util.GetTimeAsMsSinceEpoch(time.Now().Add(-12 * time.Hour))
eventManager.addUpdateRuleInternal(*rule)
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 0)
assert.Len(t, eventManager.schedulesMapping, 0)
eventManager.RUnlock()
assert.Eventually(t, func() bool {
_, err = dataprovider.EventRuleExists(rule.Name)
_, ok := err.(*util.RecordNotFoundError)
return ok
}, 2*time.Second, 100*time.Millisecond)
rule.DeletedAt = 0
err = dataprovider.AddEventRule(rule, "", "")
assert.NoError(t, err)
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 1)
assert.Len(t, eventManager.schedulesMapping, 1)
eventManager.RUnlock()
err = dataprovider.DeleteEventRule(rule.Name, "", "")
assert.NoError(t, err)
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 0)
assert.Len(t, eventManager.schedulesMapping, 0)
eventManager.RUnlock()
err = dataprovider.DeleteEventAction(action.Name, "", "")
assert.NoError(t, err)
stopEventScheduler()
}
func TestEventManagerErrors(t *testing.T) {
startEventScheduler()
providerConf := dataprovider.GetProviderConfig()
err := dataprovider.Close()
assert.NoError(t, err)
err = executeUsersQuotaResetRuleAction(dataprovider.ConditionOptions{})
assert.Error(t, err)
err = executeFoldersQuotaResetRuleAction(dataprovider.ConditionOptions{})
assert.Error(t, err)
err = executeTransferQuotaResetRuleAction(dataprovider.ConditionOptions{})
assert.Error(t, err)
err = executeQuotaResetForUser(dataprovider.User{
Groups: []sdk.GroupMapping{
{
Name: "agroup",
Type: sdk.GroupTypePrimary,
},
},
})
assert.Error(t, err)
err = executeDataRetentionCheckForUser(dataprovider.User{
Groups: []sdk.GroupMapping{
{
Name: "agroup",
Type: sdk.GroupTypePrimary,
},
},
}, nil)
assert.Error(t, err)
dataRetentionAction := dataprovider.BaseEventAction{
Type: dataprovider.ActionTypeDataRetentionCheck,
Options: dataprovider.BaseEventActionOptions{
RetentionConfig: dataprovider.EventActionDataRetentionConfig{
Folders: []dataprovider.FolderRetention{
{
Path: "/",
Retention: 24,
},
},
},
},
}
err = executeRuleAction(dataRetentionAction, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: "username1",
},
},
})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "unable to get users")
}
eventManager.loadRules()
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 0)
eventManager.RUnlock()
// rule with invalid trigger
eventManager.addUpdateRuleInternal(dataprovider.EventRule{
Name: "test rule",
Trigger: -1,
})
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 0)
eventManager.RUnlock()
// rule with invalid cronspec
eventManager.addUpdateRuleInternal(dataprovider.EventRule{
Name: "test rule",
Trigger: dataprovider.EventTriggerSchedule,
Conditions: dataprovider.EventConditions{
Schedules: []dataprovider.Schedule{
{
Hours: "1000",
},
},
},
})
eventManager.RLock()
assert.Len(t, eventManager.FsEvents, 0)
assert.Len(t, eventManager.ProviderEvents, 0)
assert.Len(t, eventManager.Schedules, 0)
eventManager.RUnlock()
err = dataprovider.Initialize(providerConf, configDir, true)
assert.NoError(t, err)
stopEventScheduler()
}
func TestEventRuleActions(t *testing.T) {
actionName := "test rule action"
action := dataprovider.BaseEventAction{
Name: actionName,
Type: dataprovider.ActionTypeBackup,
}
err := executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{})
assert.NoError(t, err)
action.Type = -1
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{})
assert.Error(t, err)
action = dataprovider.BaseEventAction{
Name: actionName,
Type: dataprovider.ActionTypeHTTP,
Options: dataprovider.BaseEventActionOptions{
HTTPConfig: dataprovider.EventActionHTTPConfig{
Endpoint: "http://foo\x7f.com/", // invalid URL
SkipTLSVerify: true,
Body: "{{ObjectData}}",
Method: http.MethodPost,
QueryParameters: []dataprovider.KeyValue{
{
Key: "param",
Value: "value",
},
},
Timeout: 5,
Headers: []dataprovider.KeyValue{
{
Key: "Content-Type",
Value: "application/json",
},
},
Username: "httpuser",
},
},
}
action.Options.SetEmptySecretsIfNil()
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "invalid endpoint")
}
action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v", httpAddr)
params := EventParams{
Name: "a",
Object: &dataprovider.User{
BaseUser: sdk.BaseUser{
Username: "test user",
},
},
}
err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
assert.NoError(t, err)
action.Options.HTTPConfig.Endpoint = fmt.Sprintf("http://%v/404", httpAddr)
err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
if assert.Error(t, err) {
assert.Equal(t, err.Error(), "unexpected status code: 404")
}
action.Options.HTTPConfig.Endpoint = "http://invalid:1234"
err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
assert.Error(t, err)
action.Options.HTTPConfig.QueryParameters = nil
action.Options.HTTPConfig.Endpoint = "http://bar\x7f.com/"
err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
assert.Error(t, err)
action.Options.HTTPConfig.Password = kms.NewSecret(sdkkms.SecretStatusSecretBox, "payload", "key", "data")
err = executeRuleAction(action, params, dataprovider.ConditionOptions{})
if assert.Error(t, err) {
assert.Contains(t, err.Error(), "unable to decrypt password")
}
// test disk and transfer quota reset
username1 := "user1"
username2 := "user2"
user1 := dataprovider.User{
BaseUser: sdk.BaseUser{
Username: username1,
HomeDir: filepath.Join(os.TempDir(), username1),
Status: 1,
Permissions: map[string][]string{
"/": {dataprovider.PermAny},
},
},
}
user2 := dataprovider.User{
BaseUser: sdk.BaseUser{
Username: username2,
HomeDir: filepath.Join(os.TempDir(), username2),
Status: 1,
Permissions: map[string][]string{
"/": {dataprovider.PermAny},
},
},
}
err = dataprovider.AddUser(&user1, "", "")
assert.NoError(t, err)
err = dataprovider.AddUser(&user2, "", "")
assert.NoError(t, err)
action = dataprovider.BaseEventAction{
Type: dataprovider.ActionTypeUserQuotaReset,
}
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.Error(t, err) // no home dir
// create the home dir
err = os.MkdirAll(user1.GetHomeDir(), os.ModePerm)
assert.NoError(t, err)
err = os.WriteFile(filepath.Join(user1.GetHomeDir(), "file.txt"), []byte("user"), 0666)
assert.NoError(t, err)
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.NoError(t, err)
userGet, err := dataprovider.UserExists(username1)
assert.NoError(t, err)
assert.Equal(t, 1, userGet.UsedQuotaFiles)
assert.Equal(t, int64(4), userGet.UsedQuotaSize)
// simulate another quota scan in progress
assert.True(t, QuotaScans.AddUserQuotaScan(username1))
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.Error(t, err)
assert.True(t, QuotaScans.RemoveUserQuotaScan(username1))
dataRetentionAction := dataprovider.BaseEventAction{
Type: dataprovider.ActionTypeDataRetentionCheck,
Options: dataprovider.BaseEventActionOptions{
RetentionConfig: dataprovider.EventActionDataRetentionConfig{
Folders: []dataprovider.FolderRetention{
{
Path: "",
Retention: 24,
},
},
},
},
}
err = executeRuleAction(dataRetentionAction, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.Error(t, err) // invalid config, no folder path specified
retentionDir := "testretention"
dataRetentionAction = dataprovider.BaseEventAction{
Type: dataprovider.ActionTypeDataRetentionCheck,
Options: dataprovider.BaseEventActionOptions{
RetentionConfig: dataprovider.EventActionDataRetentionConfig{
Folders: []dataprovider.FolderRetention{
{
Path: path.Join("/", retentionDir),
Retention: 24,
DeleteEmptyDirs: true,
},
},
},
},
}
// create some test files
file1 := filepath.Join(user1.GetHomeDir(), "file1.txt")
file2 := filepath.Join(user1.GetHomeDir(), retentionDir, "file2.txt")
file3 := filepath.Join(user1.GetHomeDir(), retentionDir, "file3.txt")
file4 := filepath.Join(user1.GetHomeDir(), retentionDir, "sub", "file4.txt")
err = os.MkdirAll(filepath.Dir(file4), os.ModePerm)
assert.NoError(t, err)
for _, f := range []string{file1, file2, file3, file4} {
err = os.WriteFile(f, []byte(""), 0666)
assert.NoError(t, err)
}
timeBeforeRetention := time.Now().Add(-48 * time.Hour)
err = os.Chtimes(file1, timeBeforeRetention, timeBeforeRetention)
assert.NoError(t, err)
err = os.Chtimes(file2, timeBeforeRetention, timeBeforeRetention)
assert.NoError(t, err)
err = os.Chtimes(file4, timeBeforeRetention, timeBeforeRetention)
assert.NoError(t, err)
err = executeRuleAction(dataRetentionAction, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.NoError(t, err)
assert.FileExists(t, file1)
assert.NoFileExists(t, file2)
assert.FileExists(t, file3)
assert.NoDirExists(t, filepath.Dir(file4))
// simulate another check in progress
c := RetentionChecks.Add(RetentionCheck{}, &user1)
assert.NotNil(t, c)
err = executeRuleAction(dataRetentionAction, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.Error(t, err)
RetentionChecks.remove(user1.Username)
err = os.RemoveAll(user1.GetHomeDir())
assert.NoError(t, err)
err = dataprovider.UpdateUserTransferQuota(&user1, 100, 100, true)
assert.NoError(t, err)
action.Type = dataprovider.ActionTypeTransferQuotaReset
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username1,
},
},
})
assert.NoError(t, err)
userGet, err = dataprovider.UserExists(username1)
assert.NoError(t, err)
assert.Equal(t, int64(0), userGet.UsedDownloadDataTransfer)
assert.Equal(t, int64(0), userGet.UsedUploadDataTransfer)
err = dataprovider.DeleteUser(username1, "", "")
assert.NoError(t, err)
err = dataprovider.DeleteUser(username2, "", "")
assert.NoError(t, err)
// test folder quota reset
foldername1 := "f1"
foldername2 := "f2"
folder1 := vfs.BaseVirtualFolder{
Name: foldername1,
MappedPath: filepath.Join(os.TempDir(), foldername1),
}
folder2 := vfs.BaseVirtualFolder{
Name: foldername2,
MappedPath: filepath.Join(os.TempDir(), foldername2),
}
err = dataprovider.AddFolder(&folder1, "", "")
assert.NoError(t, err)
err = dataprovider.AddFolder(&folder2, "", "")
assert.NoError(t, err)
action = dataprovider.BaseEventAction{
Type: dataprovider.ActionTypeFolderQuotaReset,
}
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: foldername1,
},
},
})
assert.Error(t, err) // no home dir
err = os.MkdirAll(folder1.MappedPath, os.ModePerm)
assert.NoError(t, err)
err = os.WriteFile(filepath.Join(folder1.MappedPath, "file.txt"), []byte("folder"), 0666)
assert.NoError(t, err)
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: foldername1,
},
},
})
assert.NoError(t, err)
folderGet, err := dataprovider.GetFolderByName(foldername1)
assert.NoError(t, err)
assert.Equal(t, 1, folderGet.UsedQuotaFiles)
assert.Equal(t, int64(6), folderGet.UsedQuotaSize)
// simulate another quota scan in progress
assert.True(t, QuotaScans.AddVFolderQuotaScan(foldername1))
err = executeRuleAction(action, EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: foldername1,
},
},
})
assert.Error(t, err)
assert.True(t, QuotaScans.RemoveVFolderQuotaScan(foldername1))
err = os.RemoveAll(folder1.MappedPath)
assert.NoError(t, err)
err = dataprovider.DeleteFolder(foldername1, "", "")
assert.NoError(t, err)
err = dataprovider.DeleteFolder(foldername2, "", "")
assert.NoError(t, err)
}
func TestQuotaActionsWithQuotaTrackDisabled(t *testing.T) {
oldProviderConf := dataprovider.GetProviderConfig()
providerConf := dataprovider.GetProviderConfig()
providerConf.TrackQuota = 0
err := dataprovider.Close()
assert.NoError(t, err)
err = dataprovider.Initialize(providerConf, configDir, true)
assert.NoError(t, err)
username := "u1"
user := dataprovider.User{
BaseUser: sdk.BaseUser{
Username: username,
HomeDir: filepath.Join(os.TempDir(), username),
Status: 1,
Permissions: map[string][]string{
"/": {dataprovider.PermAny},
},
},
FsConfig: vfs.Filesystem{
Provider: sdk.LocalFilesystemProvider,
},
}
err = dataprovider.AddUser(&user, "", "")
assert.NoError(t, err)
err = os.MkdirAll(user.GetHomeDir(), os.ModePerm)
assert.NoError(t, err)
err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeUserQuotaReset},
EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username,
},
},
})
assert.Error(t, err)
err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeTransferQuotaReset},
EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: username,
},
},
})
assert.Error(t, err)
err = os.RemoveAll(user.GetHomeDir())
assert.NoError(t, err)
err = dataprovider.DeleteUser(username, "", "")
assert.NoError(t, err)
foldername := "f1"
folder := vfs.BaseVirtualFolder{
Name: foldername,
MappedPath: filepath.Join(os.TempDir(), foldername),
}
err = dataprovider.AddFolder(&folder, "", "")
assert.NoError(t, err)
err = os.MkdirAll(folder.MappedPath, os.ModePerm)
assert.NoError(t, err)
err = executeRuleAction(dataprovider.BaseEventAction{Type: dataprovider.ActionTypeFolderQuotaReset},
EventParams{}, dataprovider.ConditionOptions{
Names: []dataprovider.ConditionPattern{
{
Pattern: foldername,
},
},
})
assert.Error(t, err)
err = os.RemoveAll(folder.MappedPath)
assert.NoError(t, err)
err = dataprovider.DeleteFolder(foldername, "", "")
assert.NoError(t, err)
err = dataprovider.Close()
assert.NoError(t, err)
err = dataprovider.Initialize(oldProviderConf, configDir, true)
assert.NoError(t, err)
}
func TestScheduledActions(t *testing.T) {
startEventScheduler()
backupsPath := filepath.Join(os.TempDir(), "backups")
err := os.RemoveAll(backupsPath)
assert.NoError(t, err)
action := &dataprovider.BaseEventAction{
Name: "action",
Type: dataprovider.ActionTypeBackup,
}
err = dataprovider.AddEventAction(action, "", "")
assert.NoError(t, err)
rule := &dataprovider.EventRule{
Name: "rule",
Trigger: dataprovider.EventTriggerSchedule,
Conditions: dataprovider.EventConditions{
Schedules: []dataprovider.Schedule{
{
Hours: "11",
DayOfWeek: "*",
DayOfMonth: "*",
Month: "*",
},
},
},
Actions: []dataprovider.EventAction{
{
BaseEventAction: dataprovider.BaseEventAction{
Name: action.Name,
},
Order: 1,
},
},
}
job := eventCronJob{
ruleName: rule.Name,
}
job.Run() // rule not found
assert.NoDirExists(t, backupsPath)
err = dataprovider.AddEventRule(rule, "", "")
assert.NoError(t, err)
job.Run()
assert.DirExists(t, backupsPath)
err = dataprovider.DeleteEventRule(rule.Name, "", "")
assert.NoError(t, err)
err = dataprovider.DeleteEventAction(action.Name, "", "")
assert.NoError(t, err)
err = os.RemoveAll(backupsPath)
assert.NoError(t, err)
stopEventScheduler()
}