|
|
@@ -0,0 +1,219 @@
|
|
|
+package alert_notifications
|
|
|
+
|
|
|
+import (
|
|
|
+ "testing"
|
|
|
+
|
|
|
+ "github.com/grafana/grafana/pkg/bus"
|
|
|
+ "github.com/grafana/grafana/pkg/log"
|
|
|
+ "github.com/grafana/grafana/pkg/models"
|
|
|
+ "github.com/grafana/grafana/pkg/services/alerting"
|
|
|
+ . "github.com/smartystreets/goconvey/convey"
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ logger = log.New("fake.log")
|
|
|
+
|
|
|
+ correct_properties = "./test-configs/correct-properties"
|
|
|
+ brokenYaml = "./test-configs/broken-yaml"
|
|
|
+ doubleNotificationsConfig = "./test-configs/double-default"
|
|
|
+ emptyFolder = "./test-configs/empty_folder"
|
|
|
+ emptyFile = "./test-configs/empty"
|
|
|
+ twoNotificationsConfig = "./test-configs/two-notifications"
|
|
|
+ unknownNotifier = "./test-configs/unknown-notifier"
|
|
|
+
|
|
|
+ fakeRepo *fakeRepository
|
|
|
+)
|
|
|
+
|
|
|
+func TestNotificationAsConfig(t *testing.T) {
|
|
|
+ Convey("Testing notification as configuration", t, func() {
|
|
|
+ fakeRepo = &fakeRepository{}
|
|
|
+ bus.ClearBusHandlers()
|
|
|
+ bus.AddHandler("test", mockDelete)
|
|
|
+ bus.AddHandler("test", mockInsert)
|
|
|
+ bus.AddHandler("test", mockUpdate)
|
|
|
+ bus.AddHandler("test", mockGet)
|
|
|
+
|
|
|
+ alerting.RegisterNotifier(&alerting.NotifierPlugin{
|
|
|
+ Type: "slack",
|
|
|
+ Name: "slack",
|
|
|
+ })
|
|
|
+ alerting.RegisterNotifier(&alerting.NotifierPlugin{
|
|
|
+ Type: "email",
|
|
|
+ Name: "email",
|
|
|
+ })
|
|
|
+ Convey("Can read correct properties", func() {
|
|
|
+ cfgProvifer := &configReader{log: log.New("test logger")}
|
|
|
+ cfg, err := cfgProvifer.readConfig(correct_properties)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("readConfig return an error %v", err)
|
|
|
+ }
|
|
|
+ So(len(cfg), ShouldEqual, 1)
|
|
|
+
|
|
|
+ ntCfg := cfg[0]
|
|
|
+ nts := ntCfg.Notifications
|
|
|
+ So(len(nts), ShouldEqual, 4)
|
|
|
+
|
|
|
+ nt := nts[0]
|
|
|
+ So(nt.Name, ShouldEqual, "default-slack-notification")
|
|
|
+ So(nt.Type, ShouldEqual, "slack")
|
|
|
+ So(nt.OrgId, ShouldEqual, 2)
|
|
|
+ So(nt.IsDefault, ShouldBeTrue)
|
|
|
+ So(nt.Settings, ShouldResemble, map[string]interface{}{
|
|
|
+ "recipient": "XXX", "token": "xoxb", "uploadImage": true,
|
|
|
+ })
|
|
|
+
|
|
|
+ nt = nts[1]
|
|
|
+ So(nt.Name, ShouldEqual, "another-not-default-notification")
|
|
|
+ So(nt.Type, ShouldEqual, "email")
|
|
|
+ So(nt.OrgId, ShouldEqual, 3)
|
|
|
+ So(nt.IsDefault, ShouldBeFalse)
|
|
|
+
|
|
|
+ nt = nts[2]
|
|
|
+ So(nt.Name, ShouldEqual, "check-unset-is_default-is-false")
|
|
|
+ So(nt.Type, ShouldEqual, "slack")
|
|
|
+ So(nt.OrgId, ShouldEqual, 3)
|
|
|
+ So(nt.IsDefault, ShouldBeFalse)
|
|
|
+
|
|
|
+ nt = nts[3]
|
|
|
+ So(nt.Name, ShouldEqual, "Added notification with whitespaces in name")
|
|
|
+ So(nt.Type, ShouldEqual, "email")
|
|
|
+ So(nt.OrgId, ShouldEqual, 3)
|
|
|
+
|
|
|
+ deleteNts := ntCfg.DeleteNotifications
|
|
|
+ So(len(deleteNts), ShouldEqual, 4)
|
|
|
+
|
|
|
+ deleteNt := deleteNts[0]
|
|
|
+ So(deleteNt.Name, ShouldEqual, "default-slack-notification")
|
|
|
+ So(deleteNt.OrgId, ShouldEqual, 2)
|
|
|
+
|
|
|
+ deleteNt = deleteNts[1]
|
|
|
+ So(deleteNt.Name, ShouldEqual, "deleted-notification-without-orgId")
|
|
|
+ So(deleteNt.OrgId, ShouldEqual, 1)
|
|
|
+
|
|
|
+ deleteNt = deleteNts[2]
|
|
|
+ So(deleteNt.Name, ShouldEqual, "deleted-notification-with-0-orgId")
|
|
|
+ So(deleteNt.OrgId, ShouldEqual, 1)
|
|
|
+
|
|
|
+ deleteNt = deleteNts[3]
|
|
|
+ So(deleteNt.Name, ShouldEqual, "Deleted notification with whitespaces in name")
|
|
|
+ So(deleteNt.OrgId, ShouldEqual, 1)
|
|
|
+ })
|
|
|
+
|
|
|
+ Convey("One configured notification", func() {
|
|
|
+ Convey("no notification in database", func() {
|
|
|
+ dc := newNotificationProvisioner(logger)
|
|
|
+ err := dc.applyChanges(twoNotificationsConfig)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("applyChanges return an error %v", err)
|
|
|
+ }
|
|
|
+ So(len(fakeRepo.deleted), ShouldEqual, 0)
|
|
|
+ So(len(fakeRepo.inserted), ShouldEqual, 2)
|
|
|
+ So(len(fakeRepo.updated), ShouldEqual, 0)
|
|
|
+ })
|
|
|
+ Convey("One notification in database with same name", func() {
|
|
|
+ fakeRepo.loadAll = []*models.AlertNotification{
|
|
|
+ {Name: "channel1", OrgId: 1, Id: 1},
|
|
|
+ }
|
|
|
+ Convey("should update one notification", func() {
|
|
|
+ dc := newNotificationProvisioner(logger)
|
|
|
+ err := dc.applyChanges(twoNotificationsConfig)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("applyChanges return an error %v", err)
|
|
|
+ }
|
|
|
+ So(len(fakeRepo.deleted), ShouldEqual, 0)
|
|
|
+ So(len(fakeRepo.inserted), ShouldEqual, 1)
|
|
|
+ So(len(fakeRepo.updated), ShouldEqual, 1)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ Convey("Two notifications with is_default", func() {
|
|
|
+ dc := newNotificationProvisioner(logger)
|
|
|
+ err := dc.applyChanges(doubleNotificationsConfig)
|
|
|
+ Convey("should both be inserted", func() {
|
|
|
+ So(err, ShouldBeNil)
|
|
|
+ So(len(fakeRepo.deleted), ShouldEqual, 0)
|
|
|
+ So(len(fakeRepo.inserted), ShouldEqual, 2)
|
|
|
+ So(len(fakeRepo.updated), ShouldEqual, 0)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ Convey("Two configured notification", func() {
|
|
|
+ Convey("two other notifications in database", func() {
|
|
|
+ fakeRepo.loadAll = []*models.AlertNotification{
|
|
|
+ {Name: "channel1", OrgId: 1, Id: 1},
|
|
|
+ {Name: "channel3", OrgId: 1, Id: 2},
|
|
|
+ }
|
|
|
+ Convey("should have two new notifications", func() {
|
|
|
+ dc := newNotificationProvisioner(logger)
|
|
|
+ err := dc.applyChanges(twoNotificationsConfig)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("applyChanges return an error %v", err)
|
|
|
+ }
|
|
|
+ So(len(fakeRepo.deleted), ShouldEqual, 0)
|
|
|
+ So(len(fakeRepo.inserted), ShouldEqual, 1)
|
|
|
+ So(len(fakeRepo.updated), ShouldEqual, 1)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+ Convey("Empty yaml file", func() {
|
|
|
+ Convey("should have not changed repo", func() {
|
|
|
+ dc := newNotificationProvisioner(logger)
|
|
|
+ err := dc.applyChanges(emptyFile)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("applyChanges return an error %v", err)
|
|
|
+ }
|
|
|
+ So(len(fakeRepo.deleted), ShouldEqual, 0)
|
|
|
+ So(len(fakeRepo.inserted), ShouldEqual, 0)
|
|
|
+ So(len(fakeRepo.updated), ShouldEqual, 0)
|
|
|
+ })
|
|
|
+ })
|
|
|
+ Convey("Broken yaml should return error", func() {
|
|
|
+ reader := &configReader{log: log.New("test logger")}
|
|
|
+ _, err := reader.readConfig(brokenYaml)
|
|
|
+ So(err, ShouldNotBeNil)
|
|
|
+ })
|
|
|
+ Convey("Skip invalid directory", func() {
|
|
|
+ cfgProvifer := &configReader{log: log.New("test logger")}
|
|
|
+ cfg, err := cfgProvifer.readConfig(emptyFolder)
|
|
|
+ if err != nil {
|
|
|
+ t.Fatalf("readConfig return an error %v", err)
|
|
|
+ }
|
|
|
+ So(len(cfg), ShouldEqual, 0)
|
|
|
+ })
|
|
|
+ Convey("Unknown notifier should return error", func() {
|
|
|
+ cfgProvifer := &configReader{log: log.New("test logger")}
|
|
|
+ _, err := cfgProvifer.readConfig(unknownNotifier)
|
|
|
+ So(err, ShouldEqual, ErrInvalidNotifierType)
|
|
|
+ })
|
|
|
+
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+type fakeRepository struct {
|
|
|
+ inserted []*models.CreateAlertNotificationCommand
|
|
|
+ deleted []*models.DeleteAlertNotificationCommand
|
|
|
+ updated []*models.UpdateAlertNotificationCommand
|
|
|
+ loadAll []*models.AlertNotification
|
|
|
+}
|
|
|
+
|
|
|
+func mockDelete(cmd *models.DeleteAlertNotificationCommand) error {
|
|
|
+ fakeRepo.deleted = append(fakeRepo.deleted, cmd)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func mockUpdate(cmd *models.UpdateAlertNotificationCommand) error {
|
|
|
+ fakeRepo.updated = append(fakeRepo.updated, cmd)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func mockInsert(cmd *models.CreateAlertNotificationCommand) error {
|
|
|
+ fakeRepo.inserted = append(fakeRepo.inserted, cmd)
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func mockGet(cmd *models.GetAlertNotificationsQuery) error {
|
|
|
+ for _, v := range fakeRepo.loadAll {
|
|
|
+ if cmd.Name == v.Name && cmd.OrgId == v.OrgId {
|
|
|
+ cmd.Result = v
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|