refactor broker_test.go, extract cstest/filenotfound*.go (#1815)

This commit is contained in:
mmetc 2022-10-17 14:17:23 +02:00 committed by GitHub
parent a96b3e077d
commit ec0d2a5ed2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 274 additions and 340 deletions

View file

@ -6,16 +6,17 @@ import (
"testing" "testing"
"time" "time"
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cstest"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
tomb "gopkg.in/tomb.v2" tomb "gopkg.in/tomb.v2"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"gotest.tools/v3/assert" "gotest.tools/v3/assert"
"github.com/crowdsecurity/crowdsec/pkg/acquisition/configuration"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cstest"
"github.com/crowdsecurity/crowdsec/pkg/types"
) )
type MockSource struct { type MockSource struct {
@ -226,7 +227,7 @@ func TestLoadAcquisitionFromFile(t *testing.T) {
Config: csconfig.CrowdsecServiceCfg{ Config: csconfig.CrowdsecServiceCfg{
AcquisitionFiles: []string{"does_not_exist"}, AcquisitionFiles: []string{"does_not_exist"},
}, },
ExpectedError: "can't open does_not_exist", ExpectedError: cstest.FileNotFoundMessage,
ExpectedLen: 0, ExpectedLen: 0,
}, },
{ {
@ -282,18 +283,8 @@ func TestLoadAcquisitionFromFile(t *testing.T) {
} }
for _, test := range tests { for _, test := range tests {
dss, err := LoadAcquisitionFromFile(&test.Config) dss, err := LoadAcquisitionFromFile(&test.Config)
if test.ExpectedError != "" { cstest.RequireErrorContains(t, err, test.ExpectedError)
if err == nil {
t.Fatalf("expected error %s, got none", test.ExpectedError)
}
if !strings.Contains(err.Error(), test.ExpectedError) {
t.Fatalf("%s : expected error '%s' in '%s'", test.TestName, test.ExpectedError, err)
}
continue
}
if err != nil {
t.Fatalf("%s : unexpected error '%s'", test.TestName, err)
}
if len(dss) != test.ExpectedLen { if len(dss) != test.ExpectedLen {
t.Fatalf("%s : expected %d datasources got %d", test.TestName, test.ExpectedLen, len(dss)) t.Fatalf("%s : expected %d datasources got %d", test.TestName, test.ExpectedLen, len(dss))
} }

View file

@ -1,27 +1,20 @@
package csconfig package csconfig
import ( import (
"fmt"
"log"
"runtime"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/crowdsecurity/crowdsec/pkg/cstest"
) )
func TestNormalLoad(t *testing.T) { func TestNormalLoad(t *testing.T) {
_, err := NewConfig("./tests/config.yaml", false, false) _, err := NewConfig("./tests/config.yaml", false, false)
if err != nil { require.NoError(t, err)
t.Fatalf("unexpected error %s", err)
}
_, err = NewConfig("./tests/xxx.yaml", false, false) _, err = NewConfig("./tests/xxx.yaml", false, false)
if runtime.GOOS != "windows" { assert.EqualError(t, err, "while reading yaml file: open ./tests/xxx.yaml: "+cstest.FileNotFoundMessage)
assert.EqualError(t, err, "while reading yaml file: open ./tests/xxx.yaml: no such file or directory")
} else {
assert.EqualError(t, err, "while reading yaml file: open ./tests/xxx.yaml: The system cannot find the file specified.")
}
_, err = NewConfig("./tests/simulation.yaml", false, false) _, err = NewConfig("./tests/simulation.yaml", false, false)
assert.EqualError(t, err, "./tests/simulation.yaml: yaml: unmarshal errors:\n line 1: field simulation not found in type csconfig.Config") assert.EqualError(t, err, "./tests/simulation.yaml: yaml: unmarshal errors:\n line 1: field simulation not found in type csconfig.Config")
@ -31,29 +24,23 @@ func TestNewCrowdSecConfig(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
expectedResult *Config expectedResult *Config
err string
}{ }{
{ {
name: "new configuration: basic", name: "new configuration: basic",
expectedResult: &Config{}, expectedResult: &Config{},
err: "",
}, },
} }
for _, test := range tests { for _, tc := range tests {
result := &Config{} tc := tc
isOk := assert.Equal(t, test.expectedResult, result) t.Run(tc.name, func(t *testing.T) {
if !isOk { result := &Config{}
t.Fatalf("TEST '%s': NOK", test.name) assert.Equal(t, tc.expectedResult, result)
} else { })
fmt.Printf("TEST '%s': OK\n", test.name)
}
} }
} }
func TestDefaultConfig(t *testing.T) { func TestDefaultConfig(t *testing.T) {
x := NewDefaultConfig() x := NewDefaultConfig()
if err := x.Dump(); err != nil { err := x.Dump()
log.Fatal(err) require.NoError(t, err)
}
} }

View file

@ -3,30 +3,26 @@ package csconfig
import ( import (
"fmt" "fmt"
"path/filepath" "path/filepath"
"runtime"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/crowdsecurity/crowdsec/pkg/cstest"
) )
func TestSimulationLoading(t *testing.T) { func TestSimulationLoading(t *testing.T) {
testXXFullPath, err := filepath.Abs("./tests/xxx.yaml") testXXFullPath, err := filepath.Abs("./tests/xxx.yaml")
if err != nil { require.NoError(t, err)
panic(err)
}
badYamlFullPath, err := filepath.Abs("./tests/config.yaml") badYamlFullPath, err := filepath.Abs("./tests/config.yaml")
if err != nil { require.NoError(t, err)
panic(err)
}
tests := []struct { tests := []struct {
name string name string
Input *Config Input *Config
expectedResult *SimulationConfig expectedResult *SimulationConfig
err string expectedErr string
}{ }{
{ {
name: "basic valid simulation", name: "basic valid simulation",
@ -49,6 +45,18 @@ func TestSimulationLoading(t *testing.T) {
}, },
Crowdsec: &CrowdsecServiceCfg{}, Crowdsec: &CrowdsecServiceCfg{},
}, },
expectedErr: "simulation.yaml: "+cstest.FileNotFoundMessage,
},
{
name: "basic bad file name",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/xxx.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
expectedErr: fmt.Sprintf("while reading yaml file: open %s: %s", testXXFullPath, cstest.FileNotFoundMessage),
}, },
{ {
name: "basic bad file content", name: "basic bad file content",
@ -59,7 +67,7 @@ func TestSimulationLoading(t *testing.T) {
}, },
Crowdsec: &CrowdsecServiceCfg{}, Crowdsec: &CrowdsecServiceCfg{},
}, },
err: fmt.Sprintf("while unmarshaling simulation file '%s' : yaml: unmarshal errors", badYamlFullPath), expectedErr: fmt.Sprintf("while unmarshaling simulation file '%s' : yaml: unmarshal errors", badYamlFullPath),
}, },
{ {
name: "basic bad file content", name: "basic bad file content",
@ -70,66 +78,18 @@ func TestSimulationLoading(t *testing.T) {
}, },
Crowdsec: &CrowdsecServiceCfg{}, Crowdsec: &CrowdsecServiceCfg{},
}, },
err: fmt.Sprintf("while unmarshaling simulation file '%s' : yaml: unmarshal errors", badYamlFullPath), expectedErr: fmt.Sprintf("while unmarshaling simulation file '%s' : yaml: unmarshal errors", badYamlFullPath),
}, },
} }
if runtime.GOOS == "windows" { for _, tc := range tests {
tests = append(tests, struct { tc := tc
name string t.Run(tc.name, func(t *testing.T) {
Input *Config err := tc.Input.LoadSimulation()
expectedResult *SimulationConfig cstest.RequireErrorContains(t, err, tc.expectedErr)
err string
}{
name: "basic bad file name",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/xxx.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: fmt.Sprintf("while reading yaml file: open %s: The system cannot find the file specified.", testXXFullPath),
})
} else {
tests = append(tests, struct {
name string
Input *Config
expectedResult *SimulationConfig
err string
}{
name: "basic bad file name",
Input: &Config{
ConfigPaths: &ConfigurationPaths{
SimulationFilePath: "./tests/xxx.yaml",
DataDir: "./data",
},
Crowdsec: &CrowdsecServiceCfg{},
},
err: fmt.Sprintf("while reading yaml file: open %s: no such file or directory", testXXFullPath),
})
}
for idx, test := range tests { assert.Equal(t, tc.expectedResult, tc.Input.Crowdsec.SimulationConfig)
err := test.Input.LoadSimulation() })
if err == nil && test.err != "" {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected error, didn't get it", idx, len(tests))
}
if test.err != "" {
if !strings.HasPrefix(fmt.Sprintf("%s", err), test.err) {
fmt.Printf("TEST '%s': NOK\n", test.name)
t.Fatalf("%d/%d expected '%s' got '%s'", idx, len(tests),
test.err,
fmt.Sprintf("%s", err))
}
}
isOk := assert.Equal(t, test.expectedResult, test.Input.Crowdsec.SimulationConfig)
if !isOk {
t.Fatalf("TEST '%s': NOK\n", test.name)
} else {
fmt.Printf("TEST '%s': OK\n", test.name)
}
} }
} }
@ -150,7 +110,6 @@ func TestIsSimulated(t *testing.T) {
SimulationConfig *SimulationConfig SimulationConfig *SimulationConfig
Input string Input string
expectedResult bool expectedResult bool
err string
}{ }{
{ {
name: "No simulation except (in exclusion)", name: "No simulation except (in exclusion)",
@ -171,13 +130,11 @@ func TestIsSimulated(t *testing.T) {
expectedResult: false, expectedResult: false,
}, },
} }
for _, test := range tests { for _, tc := range tests {
IsSimulated := test.SimulationConfig.IsSimulated(test.Input) tc := tc
isOk := assert.Equal(t, test.expectedResult, IsSimulated) t.Run(tc.name, func(t *testing.T) {
if !isOk { IsSimulated := tc.SimulationConfig.IsSimulated(tc.Input)
fmt.Printf("TEST: '%v' failed", test.name) require.Equal(t, tc.expectedResult, IsSimulated)
t.Fatal() })
}
} }
} }

View file

@ -14,31 +14,32 @@ import (
"time" "time"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/tomb.v2" "gopkg.in/tomb.v2"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cstest"
"github.com/crowdsecurity/crowdsec/pkg/models"
) )
var testPath string var testPath string
func setPluginPermTo744() { func setPluginPermTo744(t *testing.T) {
setPluginPermTo("744") setPluginPermTo(t, "744")
} }
func setPluginPermTo722() { func setPluginPermTo722(t *testing.T) {
setPluginPermTo("722") setPluginPermTo(t, "722")
} }
func setPluginPermTo724() { func setPluginPermTo724(t *testing.T) {
setPluginPermTo("724") setPluginPermTo(t, "724")
} }
func TestGetPluginNameAndTypeFromPath(t *testing.T) { func TestGetPluginNameAndTypeFromPath(t *testing.T) {
setUp() setUp(t)
defer tearDown() defer tearDown(t)
type args struct { type args struct {
path string path string
} }
@ -47,7 +48,7 @@ func TestGetPluginNameAndTypeFromPath(t *testing.T) {
args args args args
want string want string
want1 string want1 string
wantErr bool expectedErr string
}{ }{
{ {
name: "valid plugin name, single dash", name: "valid plugin name, single dash",
@ -56,16 +57,13 @@ func TestGetPluginNameAndTypeFromPath(t *testing.T) {
}, },
want: "notification", want: "notification",
want1: "gitter", want1: "gitter",
wantErr: false,
}, },
{ {
name: "invalid plugin name", name: "invalid plugin name",
args: args{ args: args{
path: "./tests/gitter", path: "./tests/gitter",
}, },
want: "", expectedErr: "plugin name ./tests/gitter is invalid. Name should be like {type-name}",
want1: "",
wantErr: true,
}, },
{ {
name: "valid plugin name, multiple dash", name: "valid plugin name, multiple dash",
@ -74,30 +72,23 @@ func TestGetPluginNameAndTypeFromPath(t *testing.T) {
}, },
want: "notification-instant", want: "notification-instant",
want1: "slack", want1: "slack",
wantErr: false,
}, },
} }
for _, tt := range tests { for _, tc := range tests {
tt := tt tc := tc
t.Run(tt.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got, got1, err := getPluginTypeAndSubtypeFromPath(tt.args.path) got, got1, err := getPluginTypeAndSubtypeFromPath(tc.args.path)
if (err != nil) != tt.wantErr { cstest.RequireErrorContains(t, err, tc.expectedErr)
t.Errorf("getPluginNameAndTypeFromPath() error = %v, wantErr %v", err, tt.wantErr)
return assert.Equal(t, tc.want, got)
} assert.Equal(t, tc.want1, got1)
if got != tt.want {
t.Errorf("getPluginNameAndTypeFromPath() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("getPluginNameAndTypeFromPath() got1 = %v, want %v", got1, tt.want1)
}
}) })
} }
} }
func TestListFilesAtPath(t *testing.T) { func TestListFilesAtPath(t *testing.T) {
setUp() setUp(t)
defer tearDown() defer tearDown(t)
type args struct { type args struct {
path string path string
} }
@ -105,7 +96,7 @@ func TestListFilesAtPath(t *testing.T) {
name string name string
args args args args
want []string want []string
wantErr bool expectedErr string
}{ }{
{ {
name: "valid directory", name: "valid directory",
@ -122,19 +113,17 @@ func TestListFilesAtPath(t *testing.T) {
args: args{ args: args{
path: "./foo/bar/", path: "./foo/bar/",
}, },
wantErr: true, expectedErr: "open ./foo/bar/: " + cstest.FileNotFoundMessage,
}, },
} }
for _, tt := range tests { for _, tc := range tests {
tt := tt tc := tc
t.Run(tt.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
got, err := listFilesAtPath(tt.args.path) got, err := listFilesAtPath(tc.args.path)
if (err != nil) != tt.wantErr { cstest.RequireErrorContains(t, err, tc.expectedErr)
t.Errorf("listFilesAtPath() error = %v, wantErr %v", err, tt.wantErr)
return if !reflect.DeepEqual(got, tc.want) {
} t.Errorf("listFilesAtPath() = %v, want %v", got, tc.want)
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("listFilesAtPath() = %v, want %v", got, tt.want)
} }
}) })
} }
@ -147,49 +136,40 @@ func TestBrokerInit(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
action func() action func(*testing.T)
errContains string
wantErr bool
procCfg csconfig.PluginCfg procCfg csconfig.PluginCfg
expectedErr string
}{ }{
{ {
name: "valid config", name: "valid config",
action: setPluginPermTo744, action: setPluginPermTo744,
wantErr: false,
}, },
{ {
name: "group writable binary", name: "group writable binary",
wantErr: true, expectedErr: "notification-dummy is world writable",
errContains: "notification-dummy is world writable",
action: setPluginPermTo722, action: setPluginPermTo722,
}, },
{ {
name: "group writable binary", name: "group writable binary",
wantErr: true, expectedErr: "notification-dummy is group writable",
errContains: "notification-dummy is group writable",
action: setPluginPermTo724, action: setPluginPermTo724,
}, },
{ {
name: "no plugin dir", name: "no plugin dir",
wantErr: true, expectedErr: cstest.FileNotFoundMessage,
errContains: "no such file or directory",
action: tearDown, action: tearDown,
}, },
{ {
name: "no plugin binary", name: "no plugin binary",
wantErr: true, expectedErr: "binary for plugin dummy_default not found",
errContains: "binary for plugin dummy_default not found", action: func(t *testing.T) {
action: func() {
err := os.Remove(path.Join(testPath, "notification-dummy")) err := os.Remove(path.Join(testPath, "notification-dummy"))
if err != nil { require.NoError(t, err)
t.Fatal(err)
}
}, },
}, },
{ {
name: "only specify user", name: "only specify user",
wantErr: true, expectedErr: "both plugin user and group must be set",
errContains: "both plugin user and group must be set",
procCfg: csconfig.PluginCfg{ procCfg: csconfig.PluginCfg{
User: "123445555551122toto", User: "123445555551122toto",
}, },
@ -197,8 +177,7 @@ func TestBrokerInit(t *testing.T) {
}, },
{ {
name: "only specify group", name: "only specify group",
wantErr: true, expectedErr: "both plugin user and group must be set",
errContains: "both plugin user and group must be set",
procCfg: csconfig.PluginCfg{ procCfg: csconfig.PluginCfg{
Group: "123445555551122toto", Group: "123445555551122toto",
}, },
@ -206,8 +185,7 @@ func TestBrokerInit(t *testing.T) {
}, },
{ {
name: "Fails to run as root", name: "Fails to run as root",
wantErr: true, expectedErr: "operation not permitted",
errContains: "operation not permitted",
procCfg: csconfig.PluginCfg{ procCfg: csconfig.PluginCfg{
User: "root", User: "root",
Group: "root", Group: "root",
@ -216,8 +194,7 @@ func TestBrokerInit(t *testing.T) {
}, },
{ {
name: "Invalid user and group", name: "Invalid user and group",
wantErr: true, expectedErr: "unknown user toto1234",
errContains: "unknown user toto1234",
procCfg: csconfig.PluginCfg{ procCfg: csconfig.PluginCfg{
User: "toto1234", User: "toto1234",
Group: "toto1234", Group: "toto1234",
@ -226,8 +203,7 @@ func TestBrokerInit(t *testing.T) {
}, },
{ {
name: "Valid user and invalid group", name: "Valid user and invalid group",
wantErr: true, expectedErr: "unknown group toto1234",
errContains: "unknown group toto1234",
procCfg: csconfig.PluginCfg{ procCfg: csconfig.PluginCfg{
User: "nobody", User: "nobody",
Group: "toto1234", Group: "toto1234",
@ -236,30 +212,25 @@ func TestBrokerInit(t *testing.T) {
}, },
} }
for _, test := range tests { for _, tc := range tests {
test := test tc := tc
t.Run(test.name, func(t *testing.T) { t.Run(tc.name, func(t *testing.T) {
defer tearDown() defer tearDown(t)
buildDummyPlugin() buildDummyPlugin(t)
if test.action != nil { if tc.action != nil {
test.action() tc.action(t)
} }
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{ profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"}, Notifications: []string{"dummy_default"},
}) })
err := pb.Init(&test.procCfg, profiles, &csconfig.ConfigurationPaths{ err := pb.Init(&tc.procCfg, profiles, &csconfig.ConfigurationPaths{
PluginDir: testPath, PluginDir: testPath,
NotificationDir: "./tests/notifications", NotificationDir: "./tests/notifications",
}) })
defer pb.Kill() defer pb.Kill()
if test.wantErr { cstest.RequireErrorContains(t, err, tc.expectedErr)
assert.ErrorContains(t, err, test.errContains)
} else {
assert.NoError(t, err)
}
}) })
} }
} }
@ -267,91 +238,95 @@ func TestBrokerInit(t *testing.T) {
func readconfig(t *testing.T, path string) ([]byte, PluginConfig) { func readconfig(t *testing.T, path string) ([]byte, PluginConfig) {
var config PluginConfig var config PluginConfig
orig, err := os.ReadFile("tests/notifications/dummy.yaml") orig, err := os.ReadFile("tests/notifications/dummy.yaml")
if err != nil { require.NoError(t, err,"unable to read config file %s", path)
t.Fatalf("unable to read config file %s : %s", path, err)
} err = yaml.Unmarshal(orig, &config)
if err := yaml.Unmarshal(orig, &config); err != nil { require.NoError(t, err,"unable to unmarshal config file")
t.Fatalf("unable to unmarshal config file : %s", err)
}
return orig, config return orig, config
} }
func writeconfig(t *testing.T, config PluginConfig, path string) { func writeconfig(t *testing.T, config PluginConfig, path string) {
data, err := yaml.Marshal(&config) data, err := yaml.Marshal(&config)
if err != nil { require.NoError(t, err,"unable to marshal config file")
t.Fatalf("unable to marshal config file : %s", err)
} err = os.WriteFile(path, data, 0644)
if err := os.WriteFile(path, data, 0644); err != nil { require.NoError(t, err,"unable to write config file %s", path)
t.Fatalf("unable to write config file %s : %s", path, err)
}
} }
func TestBrokerNoThreshold(t *testing.T) { func TestBrokerNoThreshold(t *testing.T) {
var alerts []models.Alert var alerts []models.Alert
DefaultEmptyTicker = 50 * time.Millisecond DefaultEmptyTicker = 50 * time.Millisecond
buildDummyPlugin() buildDummyPlugin(t)
setPluginPermTo744() setPluginPermTo744(t)
defer tearDown() defer tearDown(t)
//init
// init
pluginCfg := csconfig.PluginCfg{} pluginCfg := csconfig.PluginCfg{}
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{ profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"}, Notifications: []string{"dummy_default"},
}) })
//default config
// default config
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{ err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
PluginDir: testPath, PluginDir: testPath,
NotificationDir: "./tests/notifications", NotificationDir: "./tests/notifications",
}) })
assert.NoError(t, err) assert.NoError(t, err)
tomb := tomb.Tomb{} tomb := tomb.Tomb{}
go pb.Run(&tomb) go pb.Run(&tomb)
defer pb.Kill() defer pb.Kill()
//send one item, it should be processed right now
// send one item, it should be processed right now
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
//we expect one now
// we expect one now
content, err := os.ReadFile("./out") content, err := os.ReadFile("./out")
if err != nil { require.NoError(t, err, "Error reading file")
log.Errorf("Error reading file: %s", err)
}
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, len(alerts)) assert.Len(t, alerts, 1)
//remove it
// remove it
os.Remove("./out") os.Remove("./out")
//and another one
// and another one
log.Printf("second send") log.Printf("second send")
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
//we expect one again, as we cleaned the file
// we expect one again, as we cleaned the file
content, err = os.ReadFile("./out") content, err = os.ReadFile("./out")
if err != nil { require.NoError(t, err, "Error reading file")
log.Errorf("Error reading file: %s", err)
}
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
log.Printf("content-> %s", content) log.Printf("content-> %s", content)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, len(alerts)) assert.Len(t, alerts, 1)
} }
func TestBrokerRunGroupAndTimeThreshold_TimeFirst(t *testing.T) { func TestBrokerRunGroupAndTimeThreshold_TimeFirst(t *testing.T) {
//test grouping by "time" // test grouping by "time"
DefaultEmptyTicker = 50 * time.Millisecond DefaultEmptyTicker = 50 * time.Millisecond
buildDummyPlugin() buildDummyPlugin(t)
setPluginPermTo744() setPluginPermTo744(t)
defer tearDown() defer tearDown(t)
//init // init
pluginCfg := csconfig.PluginCfg{} pluginCfg := csconfig.PluginCfg{}
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{ profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"}, Notifications: []string{"dummy_default"},
}) })
//set groupwait and groupthreshold, should honor whichever comes first // set groupwait and groupthreshold, should honor whichever comes first
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml") raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
cfg.GroupThreshold = 4 cfg.GroupThreshold = 4
cfg.GroupWait = 1 * time.Second cfg.GroupWait = 1 * time.Second
@ -362,42 +337,46 @@ func TestBrokerRunGroupAndTimeThreshold_TimeFirst(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
tomb := tomb.Tomb{} tomb := tomb.Tomb{}
go pb.Run(&tomb) go pb.Run(&tomb)
defer pb.Kill() defer pb.Kill()
//send data // send data
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
//because of group threshold, we shouldn't have data yet // because of group threshold, we shouldn't have data yet
assert.NoFileExists(t, "./out") assert.NoFileExists(t, "./out")
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
//after 1 seconds, we should have data // after 1 seconds, we should have data
content, err := os.ReadFile("./out") content, err := os.ReadFile("./out")
assert.NoError(t, err) assert.NoError(t, err)
var alerts []models.Alert var alerts []models.Alert
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 3, len(alerts)) assert.Len(t, alerts, 3)
//restore config
if err := os.WriteFile("tests/notifications/dummy.yaml", raw, 0644); err != nil { // restore config
t.Fatalf("unable to write config file %s", err) err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
} require.NoError(t, err,"unable to write config file")
} }
func TestBrokerRunGroupAndTimeThreshold_CountFirst(t *testing.T) { func TestBrokerRunGroupAndTimeThreshold_CountFirst(t *testing.T) {
DefaultEmptyTicker = 50 * time.Millisecond DefaultEmptyTicker = 50 * time.Millisecond
buildDummyPlugin() buildDummyPlugin(t)
setPluginPermTo744() setPluginPermTo(t, "744")
defer tearDown() defer tearDown(t)
//init
// init
pluginCfg := csconfig.PluginCfg{} pluginCfg := csconfig.PluginCfg{}
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{ profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"}, Notifications: []string{"dummy_default"},
}) })
//set groupwait and groupthreshold, should honor whichever comes first
// set groupwait and groupthreshold, should honor whichever comes first
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml") raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
cfg.GroupThreshold = 4 cfg.GroupThreshold = 4
cfg.GroupWait = 4 * time.Second cfg.GroupWait = 4 * time.Second
@ -408,46 +387,51 @@ func TestBrokerRunGroupAndTimeThreshold_CountFirst(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
tomb := tomb.Tomb{} tomb := tomb.Tomb{}
go pb.Run(&tomb) go pb.Run(&tomb)
defer pb.Kill() defer pb.Kill()
//send data
// send data
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
//because of group threshold, we shouldn't have data yet
// because of group threshold, we shouldn't have data yet
assert.NoFileExists(t, "./out") assert.NoFileExists(t, "./out")
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
//and now we should
// and now we should
content, err := os.ReadFile("./out") content, err := os.ReadFile("./out")
if err != nil { require.NoError(t, err, "Error reading file")
log.Errorf("Error reading file: %s", err)
}
var alerts []models.Alert var alerts []models.Alert
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 4, len(alerts)) assert.Len(t, alerts, 4)
//restore config
if err := os.WriteFile("tests/notifications/dummy.yaml", raw, 0644); err != nil { // restore config
t.Fatalf("unable to write config file %s", err) err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
} require.NoError(t, err,"unable to write config file")
} }
func TestBrokerRunGroupThreshold(t *testing.T) { func TestBrokerRunGroupThreshold(t *testing.T) {
//test grouping by "size" // test grouping by "size"
DefaultEmptyTicker = 50 * time.Millisecond DefaultEmptyTicker = 50 * time.Millisecond
buildDummyPlugin() buildDummyPlugin(t)
setPluginPermTo744() setPluginPermTo(t, "744")
defer tearDown() defer tearDown(t)
//init
// init
pluginCfg := csconfig.PluginCfg{} pluginCfg := csconfig.PluginCfg{}
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{ profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"}, Notifications: []string{"dummy_default"},
}) })
//set groupwait
// set groupwait
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml") raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
cfg.GroupThreshold = 4 cfg.GroupThreshold = 4
writeconfig(t, cfg, "tests/notifications/dummy.yaml") writeconfig(t, cfg, "tests/notifications/dummy.yaml")
@ -455,49 +439,55 @@ func TestBrokerRunGroupThreshold(t *testing.T) {
PluginDir: testPath, PluginDir: testPath,
NotificationDir: "./tests/notifications", NotificationDir: "./tests/notifications",
}) })
assert.NoError(t, err) assert.NoError(t, err)
tomb := tomb.Tomb{} tomb := tomb.Tomb{}
go pb.Run(&tomb) go pb.Run(&tomb)
defer pb.Kill() defer pb.Kill()
//send data
// send data
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
//because of group threshold, we shouldn't have data yet
// because of group threshold, we shouldn't have data yet
assert.NoFileExists(t, "./out") assert.NoFileExists(t, "./out")
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(100 * time.Millisecond) time.Sleep(100 * time.Millisecond)
//and now we should
// and now we should
content, err := os.ReadFile("./out") content, err := os.ReadFile("./out")
if err != nil { require.NoError(t, err, "Error reading file")
log.Errorf("Error reading file: %s", err)
}
var alerts []models.Alert var alerts []models.Alert
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 4, len(alerts)) assert.Len(t, alerts, 4)
//restore config
if err := os.WriteFile("tests/notifications/dummy.yaml", raw, 0644); err != nil { // restore config
t.Fatalf("unable to write config file %s", err) err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
} require.NoError(t, err, "unable to write config file")
} }
func TestBrokerRunTimeThreshold(t *testing.T) { func TestBrokerRunTimeThreshold(t *testing.T) {
DefaultEmptyTicker = 50 * time.Millisecond DefaultEmptyTicker = 50 * time.Millisecond
buildDummyPlugin() buildDummyPlugin(t)
setPluginPermTo744() setPluginPermTo(t, "744")
defer tearDown() defer tearDown(t)
//init
// init
pluginCfg := csconfig.PluginCfg{} pluginCfg := csconfig.PluginCfg{}
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
profiles = append(profiles, &csconfig.ProfileCfg{ profiles = append(profiles, &csconfig.ProfileCfg{
Notifications: []string{"dummy_default"}, Notifications: []string{"dummy_default"},
}) })
//set groupwait
// set groupwait
raw, cfg := readconfig(t, "tests/notifications/dummy.yaml") raw, cfg := readconfig(t, "tests/notifications/dummy.yaml")
cfg.GroupWait = time.Duration(1 * time.Second) //nolint:unconvert cfg.GroupWait = 1 * time.Second
writeconfig(t, cfg, "tests/notifications/dummy.yaml") writeconfig(t, cfg, "tests/notifications/dummy.yaml")
err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{ err := pb.Init(&pluginCfg, profiles, &csconfig.ConfigurationPaths{
PluginDir: testPath, PluginDir: testPath,
@ -505,34 +495,37 @@ func TestBrokerRunTimeThreshold(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
tomb := tomb.Tomb{} tomb := tomb.Tomb{}
go pb.Run(&tomb) go pb.Run(&tomb)
defer pb.Kill() defer pb.Kill()
//send data
// send data
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
//we shouldn't have data yet
// we shouldn't have data yet
assert.NoFileExists(t, "./out") assert.NoFileExists(t, "./out")
time.Sleep(1 * time.Second) time.Sleep(1 * time.Second)
//and now we should
// and now we should
content, err := os.ReadFile("./out") content, err := os.ReadFile("./out")
if err != nil { require.NoError(t, err, "Error reading file")
log.Errorf("Error reading file: %s", err)
}
var alerts []models.Alert var alerts []models.Alert
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 1, len(alerts)) assert.Len(t, alerts, 1)
//restore config
if err := os.WriteFile("tests/notifications/dummy.yaml", raw, 0644); err != nil { // restore config
t.Fatalf("unable to write config file %s", err) err = os.WriteFile("tests/notifications/dummy.yaml", raw, 0644)
} require.NoError(t, err, "unable to write config file %s", err)
} }
func TestBrokerRunSimple(t *testing.T) { func TestBrokerRunSimple(t *testing.T) {
DefaultEmptyTicker = 50 * time.Millisecond DefaultEmptyTicker = 50 * time.Millisecond
buildDummyPlugin() buildDummyPlugin(t)
setPluginPermTo744() setPluginPermTo(t, "744")
defer tearDown() defer tearDown(t)
pluginCfg := csconfig.PluginCfg{} pluginCfg := csconfig.PluginCfg{}
pb := PluginBroker{} pb := PluginBroker{}
profiles := csconfig.NewDefaultConfig().API.Server.Profiles profiles := csconfig.NewDefaultConfig().API.Server.Profiles
@ -545,10 +538,12 @@ func TestBrokerRunSimple(t *testing.T) {
}) })
assert.NoError(t, err) assert.NoError(t, err)
tomb := tomb.Tomb{} tomb := tomb.Tomb{}
go pb.Run(&tomb) go pb.Run(&tomb)
defer pb.Kill() defer pb.Kill()
assert.NoFileExists(t, "./out") assert.NoFileExists(t, "./out")
defer os.Remove("./out") defer os.Remove("./out")
pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}} pb.PluginChannel <- ProfileAlert{ProfileID: uint(0), Alert: &models.Alert{}}
@ -556,62 +551,54 @@ func TestBrokerRunSimple(t *testing.T) {
time.Sleep(time.Millisecond * 200) time.Sleep(time.Millisecond * 200)
content, err := os.ReadFile("./out") content, err := os.ReadFile("./out")
if err != nil { require.NoError(t, err, "Error reading file")
log.Errorf("Error reading file: %s", err)
}
var alerts []models.Alert var alerts []models.Alert
err = json.Unmarshal(content, &alerts) err = json.Unmarshal(content, &alerts)
assert.NoError(t, err) assert.NoError(t, err)
assert.Equal(t, 2, len(alerts)) assert.Len(t, alerts, 2)
} }
func buildDummyPlugin() { func buildDummyPlugin(t *testing.T) {
dir, err := os.MkdirTemp("./tests", "cs_plugin_test") dir, err := os.MkdirTemp("./tests", "cs_plugin_test")
if err != nil { require.NoError(t, err)
log.Fatal(err)
}
cmd := exec.Command("go", "build", "-o", path.Join(dir, "notification-dummy"), "../../plugins/notifications/dummy/") cmd := exec.Command("go", "build", "-o", path.Join(dir, "notification-dummy"), "../../plugins/notifications/dummy/")
if err := cmd.Run(); err != nil { err = cmd.Run()
log.Fatal(err) require.NoError(t, err, "while building dummy plugin")
}
testPath = dir testPath = dir
os.Remove("./out") os.Remove("./out")
} }
func setPluginPermTo(perm string) { func setPluginPermTo(t *testing.T, perm string) {
if runtime.GOOS != "windows" { if runtime.GOOS != "windows" {
if err := exec.Command("chmod", perm, path.Join(testPath, "notification-dummy")).Run(); err != nil { err := exec.Command("chmod", perm, path.Join(testPath, "notification-dummy")).Run()
log.Fatal(errors.Wrapf(err, "chmod 744 %s", path.Join(testPath, "notification-dummy"))) require.NoError(t, err, "chmod 744 %s", path.Join(testPath, "notification-dummy"))
}
} }
} }
func setUp() { func setUp(t *testing.T) {
dir, err := os.MkdirTemp("./", "cs_plugin_test") dir, err := os.MkdirTemp("./", "cs_plugin_test")
if err != nil { require.NoError(t, err)
log.Fatal(err)
}
f, err := os.Create(path.Join(dir, "slack")) f, err := os.Create(path.Join(dir, "slack"))
if err != nil { require.NoError(t, err)
log.Fatal(err)
}
f.Close() f.Close()
f, err = os.Create(path.Join(dir, "notification-gitter")) f, err = os.Create(path.Join(dir, "notification-gitter"))
if err != nil { require.NoError(t, err)
log.Fatal(err)
}
f.Close() f.Close()
err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666) err = os.Mkdir(path.Join(dir, "dummy_dir"), 0666)
if err != nil { require.NoError(t, err)
log.Fatal(err)
}
testPath = dir testPath = dir
} }
func tearDown() { func tearDown(t *testing.T) {
err := os.RemoveAll(testPath) err := os.RemoveAll(testPath)
if err != nil { require.NoError(t, err)
log.Fatal(err)
}
os.Remove("./out") os.Remove("./out")
} }

View file

@ -12,11 +12,13 @@ import (
"testing" "testing"
"time" "time"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/crowdsecurity/crowdsec/pkg/types"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"gopkg.in/tomb.v2" "gopkg.in/tomb.v2"
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
"github.com/crowdsecurity/crowdsec/pkg/cstest"
"github.com/crowdsecurity/crowdsec/pkg/models"
"github.com/crowdsecurity/crowdsec/pkg/types"
) )
/* /*
@ -145,7 +147,7 @@ func TestBrokerInit(t *testing.T) {
{ {
name: "no plugin dir", name: "no plugin dir",
wantErr: true, wantErr: true,
errContains: "The system cannot find the file specified.", errContains: cstest.FileNotFoundMessage,
action: tearDown, action: tearDown,
}, },
{ {

View file

@ -0,0 +1,5 @@
//go:build unix
package cstest
const FileNotFoundMessage = "no such file or directory"

View file

@ -0,0 +1,5 @@
//go:build windows
package cstest
const FileNotFoundMessage = "The system cannot find the file specified."