The open and composable observability and data visualization platform. Visualize metrics, logs, and traces from multiple sources like Prometheus, Loki, Elasticsearch, InfluxDB, Postgres and many more.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
grafana/pkg/services/sqlstore/alert_test.go

355 lines
11 KiB

//go:build integration
// +build integration
package sqlstore
import (
"context"
"testing"
"time"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/grafana/grafana/pkg/models"
"github.com/stretchr/testify/require"
)
func mockTimeNow() {
var timeSeed int64
timeNow = func() time.Time {
loc := time.FixedZone("MockZoneUTC-5", -5*60*60)
fakeNow := time.Unix(timeSeed, 0).In(loc)
timeSeed++
return fakeNow
}
}
func resetTimeNow() {
timeNow = time.Now
}
func TestAlertingDataAccess(t *testing.T) {
mockTimeNow()
defer resetTimeNow()
var sqlStore *SQLStore
var testDash *models.Dashboard
var items []*models.Alert
setup := func(t *testing.T) {
sqlStore = InitTestDB(t)
testDash = insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
evalData, err := simplejson.NewJson([]byte(`{"test": "test"}`))
require.Nil(t, err)
items = []*models.Alert{
{
PanelId: 1,
DashboardId: testDash.Id,
OrgId: testDash.OrgId,
Name: "Alerting title",
Message: "Alerting message",
Settings: simplejson.New(),
Frequency: 1,
EvalData: evalData,
},
}
err = sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
require.Nil(t, err)
}
t.Run("Can set new states", func(t *testing.T) {
setup(t)
// Get alert so we can use its ID in tests
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
require.Nil(t, err2)
insertedAlert := alertQuery.Result[0]
t.Run("new state ok", func(t *testing.T) {
cmd := &models.SetAlertStateCommand{
AlertId: insertedAlert.Id,
State: models.AlertStateOK,
}
err := sqlStore.SetAlertState(context.Background(), cmd)
require.Nil(t, err)
})
alert, _ := getAlertById(t, insertedAlert.Id, sqlStore)
stateDateBeforePause := alert.NewStateDate
t.Run("can pause all alerts", func(t *testing.T) {
err := sqlStore.pauseAllAlerts(t, true)
require.Nil(t, err)
t.Run("cannot updated paused alert", func(t *testing.T) {
cmd := &models.SetAlertStateCommand{
AlertId: insertedAlert.Id,
State: models.AlertStateOK,
}
err = sqlStore.SetAlertState(context.Background(), cmd)
require.Error(t, err)
})
t.Run("alert is paused", func(t *testing.T) {
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
currentState := alert.State
require.Equal(t, models.AlertStatePaused, currentState)
})
t.Run("pausing alerts should update their NewStateDate", func(t *testing.T) {
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
stateDateAfterPause := alert.NewStateDate
require.True(t, stateDateBeforePause.Before(stateDateAfterPause))
})
t.Run("unpausing alerts should update their NewStateDate again", func(t *testing.T) {
err := sqlStore.pauseAllAlerts(t, false)
require.Nil(t, err)
alert, _ = getAlertById(t, insertedAlert.Id, sqlStore)
stateDateAfterUnpause := alert.NewStateDate
require.True(t, stateDateBeforePause.Before(stateDateAfterUnpause))
})
})
})
t.Run("Can read properties", func(t *testing.T) {
setup(t)
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
alert := alertQuery.Result[0]
require.Nil(t, err2)
require.Greater(t, alert.Id, int64(0))
require.Equal(t, testDash.Id, alert.DashboardId)
require.Equal(t, int64(1), alert.PanelId)
require.Equal(t, "Alerting title", alert.Name)
require.Equal(t, models.AlertStateUnknown, alert.State)
require.NotNil(t, alert.NewStateDate)
require.NotNil(t, alert.EvalData)
require.Equal(t, "test", alert.EvalData.Get("test").MustString())
require.NotNil(t, alert.EvalDate)
require.Equal(t, "", alert.ExecutionError)
require.NotNil(t, alert.DashboardUid)
require.Equal(t, "dashboard-with-alerts", alert.DashboardSlug)
})
t.Run("Viewer can read alerts", func(t *testing.T) {
setup(t)
viewerUser := &models.SignedInUser{OrgRole: models.ROLE_VIEWER, OrgId: 1}
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: viewerUser}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
require.Nil(t, err2)
require.Equal(t, 1, len(alertQuery.Result))
})
t.Run("Alerts with same dashboard id and panel id should update", func(t *testing.T) {
setup(t)
modifiedItems := items
modifiedItems[0].Name = "Name"
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
t.Run("Can save alerts with same dashboard and panel id", func(t *testing.T) {
require.Nil(t, err)
})
t.Run("Alerts should be updated", func(t *testing.T) {
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
require.Nil(t, err2)
require.Equal(t, 1, len(query.Result))
require.Equal(t, "Name", query.Result[0].Name)
t.Run("Alert state should not be updated", func(t *testing.T) {
require.Equal(t, models.AlertStateUnknown, query.Result[0].State)
})
})
t.Run("Updates without changes should be ignored", func(t *testing.T) {
err3 := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
require.Nil(t, err3)
})
})
t.Run("Multiple alerts per dashboard", func(t *testing.T) {
setup(t)
multipleItems := []*models.Alert{
{
DashboardId: testDash.Id,
PanelId: 1,
Name: "1",
OrgId: 1,
Settings: simplejson.New(),
},
{
DashboardId: testDash.Id,
PanelId: 2,
Name: "2",
OrgId: 1,
Settings: simplejson.New(),
},
{
DashboardId: testDash.Id,
PanelId: 3,
Name: "3",
OrgId: 1,
Settings: simplejson.New(),
},
}
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, multipleItems)
t.Run("Should save 3 dashboards", func(t *testing.T) {
require.Nil(t, err)
queryForDashboard := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &queryForDashboard)
require.Nil(t, err2)
require.Equal(t, 3, len(queryForDashboard.Result))
})
t.Run("should updated two dashboards and delete one", func(t *testing.T) {
missingOneAlert := multipleItems[:2]
err = sqlStore.SaveAlerts(context.Background(), testDash.Id, missingOneAlert)
t.Run("should delete the missing alert", func(t *testing.T) {
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
require.Nil(t, err2)
require.Equal(t, 2, len(query.Result))
})
})
})
t.Run("When dashboard is removed", func(t *testing.T) {
setup(t)
items := []*models.Alert{
{
PanelId: 1,
DashboardId: testDash.Id,
Name: "Alerting title",
Message: "Alerting message",
},
}
err := sqlStore.SaveAlerts(context.Background(), testDash.Id, items)
require.Nil(t, err)
err = sqlStore.DeleteDashboard(context.Background(), &models.DeleteDashboardCommand{
OrgId: 1,
Id: testDash.Id,
})
require.Nil(t, err)
t.Run("Alerts should be removed", func(t *testing.T) {
query := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &query)
require.Nil(t, err2)
require.Equal(t, 0, len(query.Result))
})
})
}
func TestPausingAlerts(t *testing.T) {
mockTimeNow()
defer resetTimeNow()
t.Run("Given an alert", func(t *testing.T) {
sqlStore := InitTestDB(t)
testDash := insertTestDashboard(t, sqlStore, "dashboard with alerts", 1, 0, false, "alert")
alert, err := insertTestAlert("Alerting title", "Alerting message", testDash.OrgId, testDash.Id, simplejson.New(), sqlStore)
require.Nil(t, err)
stateDateBeforePause := alert.NewStateDate
stateDateAfterPause := stateDateBeforePause
// Get alert so we can use its ID in tests
alertQuery := models.GetAlertsQuery{DashboardIDs: []int64{testDash.Id}, PanelId: 1, OrgId: 1, User: &models.SignedInUser{OrgRole: models.ROLE_ADMIN}}
err2 := sqlStore.HandleAlertsQuery(context.Background(), &alertQuery)
require.Nil(t, err2)
insertedAlert := alertQuery.Result[0]
t.Run("when paused", func(t *testing.T) {
_, err := sqlStore.pauseAlert(t, testDash.OrgId, insertedAlert.Id, true)
require.Nil(t, err)
t.Run("the NewStateDate should be updated", func(t *testing.T) {
alert, err := getAlertById(t, insertedAlert.Id, sqlStore)
require.Nil(t, err)
stateDateAfterPause = alert.NewStateDate
require.True(t, stateDateBeforePause.Before(stateDateAfterPause))
})
})
t.Run("when unpaused", func(t *testing.T) {
_, err := sqlStore.pauseAlert(t, testDash.OrgId, insertedAlert.Id, false)
require.Nil(t, err)
t.Run("the NewStateDate should be updated again", func(t *testing.T) {
alert, err := getAlertById(t, insertedAlert.Id, sqlStore)
require.Nil(t, err)
stateDateAfterUnpause := alert.NewStateDate
require.True(t, stateDateAfterPause.Before(stateDateAfterUnpause))
})
})
})
}
func (ss *SQLStore) pauseAlert(t *testing.T, orgId int64, alertId int64, pauseState bool) (int64, error) {
cmd := &models.PauseAlertCommand{
OrgId: orgId,
AlertIds: []int64{alertId},
Paused: pauseState,
}
err := ss.PauseAlert(context.Background(), cmd)
require.Nil(t, err)
return cmd.ResultCount, err
}
func insertTestAlert(title string, message string, orgId int64, dashId int64, settings *simplejson.Json, ss *SQLStore) (*models.Alert, error) {
items := []*models.Alert{
{
PanelId: 1,
DashboardId: dashId,
OrgId: orgId,
Name: title,
Message: message,
Settings: settings,
Frequency: 1,
},
}
err := ss.SaveAlerts(context.Background(), dashId, items)
return items[0], err
}
func getAlertById(t *testing.T, id int64, ss *SQLStore) (*models.Alert, error) {
q := &models.GetAlertByIdQuery{
Id: id,
}
err := ss.GetAlertById(context.Background(), q)
require.Nil(t, err)
return q.Result, err
}
func (ss *SQLStore) pauseAllAlerts(t *testing.T, pauseState bool) error {
cmd := &models.PauseAllAlertCommand{
Paused: pauseState,
}
err := ss.PauseAllAlerts(context.Background(), cmd)
require.Nil(t, err)
return err
}