mirror of
https://github.com/grafana/grafana.git
synced 2025-02-15 01:53:33 -06:00
* chore: refactor CountDashboardsInFolder to use the more efficient Count() sql function * feat(nested folders): Add CountAlertRulesInFolder to ngalert store This commit adds CountAlertRulesInFolder and a new model for the CountAlertRulesQuery. It returns a count of alert rules associated with a given orgID and parent folder UID. (the namespace referenced inside alert rules is the parent folder). I'm not sure where this belongs in the ngalert service, so that will come in a future commit.
199 lines
4.9 KiB
Go
199 lines
4.9 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"golang.org/x/exp/rand"
|
|
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
)
|
|
|
|
func TestIntegrationUpdateAlertRules(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
sqlStore := db.InitTestDB(t)
|
|
store := DBstore{
|
|
SQLStore: sqlStore,
|
|
Cfg: setting.UnifiedAlertingSettings{
|
|
BaseInterval: time.Duration(rand.Int63n(100)) * time.Second,
|
|
},
|
|
}
|
|
|
|
t.Run("should increase version", func(t *testing.T) {
|
|
rule := createRule(t, store)
|
|
newRule := models.CopyRule(rule)
|
|
newRule.Title = util.GenerateShortUID()
|
|
err := store.UpdateAlertRules(context.Background(), []models.UpdateRule{{
|
|
Existing: rule,
|
|
New: *newRule,
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
dbrule := &models.AlertRule{}
|
|
err = sqlStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
|
exist, err := sess.Table(models.AlertRule{}).ID(rule.ID).Get(dbrule)
|
|
require.Truef(t, exist, fmt.Sprintf("rule with ID %d does not exist", rule.ID))
|
|
return err
|
|
})
|
|
|
|
require.NoError(t, err)
|
|
require.Equal(t, rule.Version+1, dbrule.Version)
|
|
})
|
|
|
|
t.Run("should fail due to optimistic locking if version does not match", func(t *testing.T) {
|
|
rule := createRule(t, store)
|
|
rule.Version-- // simulate version discrepancy
|
|
|
|
newRule := models.CopyRule(rule)
|
|
newRule.Title = util.GenerateShortUID()
|
|
|
|
err := store.UpdateAlertRules(context.Background(), []models.UpdateRule{{
|
|
Existing: rule,
|
|
New: *newRule,
|
|
},
|
|
})
|
|
|
|
require.ErrorIs(t, err, ErrOptimisticLock)
|
|
})
|
|
}
|
|
|
|
func withIntervalMatching(baseInterval time.Duration) func(*models.AlertRule) {
|
|
return func(rule *models.AlertRule) {
|
|
rule.IntervalSeconds = int64(baseInterval.Seconds()) * rand.Int63n(10)
|
|
rule.For = time.Duration(rule.IntervalSeconds*rand.Int63n(9)+1) * time.Second
|
|
}
|
|
}
|
|
|
|
func TestIntegration_getFilterByOrgsString(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
testCases := []struct {
|
|
testName string
|
|
orgs map[int64]struct{}
|
|
expectedFilter string
|
|
expectedArgs []interface{}
|
|
}{
|
|
{
|
|
testName: "should return empty string if map is empty",
|
|
orgs: map[int64]struct{}{},
|
|
expectedFilter: "",
|
|
expectedArgs: nil,
|
|
},
|
|
{
|
|
testName: "should return empty string if map is nil",
|
|
orgs: nil,
|
|
expectedFilter: "",
|
|
expectedArgs: nil,
|
|
},
|
|
{
|
|
testName: "should return correct filter if single element",
|
|
orgs: map[int64]struct{}{
|
|
1: {},
|
|
},
|
|
expectedFilter: "org_id NOT IN(?)",
|
|
expectedArgs: []interface{}{int64(1)},
|
|
},
|
|
{
|
|
testName: "should return correct filter if many elements",
|
|
orgs: map[int64]struct{}{
|
|
1: {},
|
|
2: {},
|
|
3: {},
|
|
},
|
|
expectedFilter: "org_id NOT IN(?,?,?)",
|
|
expectedArgs: []interface{}{int64(1), int64(2), int64(3)},
|
|
},
|
|
}
|
|
for _, testCase := range testCases {
|
|
t.Run(testCase.testName, func(t *testing.T) {
|
|
store := &DBstore{
|
|
Cfg: setting.UnifiedAlertingSettings{
|
|
DisabledOrgs: testCase.orgs,
|
|
},
|
|
}
|
|
filter, args := store.getFilterByOrgsString()
|
|
assert.Equal(t, testCase.expectedFilter, filter)
|
|
assert.ElementsMatch(t, testCase.expectedArgs, args)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestIntegration_CountAlertRules(t *testing.T) {
|
|
if testing.Short() {
|
|
t.Skip("skipping integration test")
|
|
}
|
|
|
|
sqlStore := db.InitTestDB(t)
|
|
store := DBstore{SQLStore: sqlStore}
|
|
rule := createRule(t, store)
|
|
|
|
tests := map[string]struct {
|
|
query *models.CountAlertRulesQuery
|
|
expected int64
|
|
expectErr bool
|
|
}{
|
|
"basic success": {
|
|
&models.CountAlertRulesQuery{
|
|
NamespaceUID: rule.NamespaceUID,
|
|
OrgID: rule.OrgID,
|
|
},
|
|
1,
|
|
false,
|
|
},
|
|
"successfully returning no results": {
|
|
&models.CountAlertRulesQuery{
|
|
NamespaceUID: "probably not a uid we'd generate",
|
|
OrgID: rule.OrgID,
|
|
},
|
|
0,
|
|
false,
|
|
},
|
|
}
|
|
|
|
for name, test := range tests {
|
|
t.Run(name, func(t *testing.T) {
|
|
count, err := store.CountAlertRulesInFolder(context.Background(), test.query)
|
|
if test.expectErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.expected, count)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func createRule(t *testing.T, store DBstore) *models.AlertRule {
|
|
rule := models.AlertRuleGen(withIntervalMatching(store.Cfg.BaseInterval))()
|
|
err := store.SQLStore.WithDbSession(context.Background(), func(sess *db.Session) error {
|
|
_, err := sess.Table(models.AlertRule{}).InsertOne(rule)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
dbRule := &models.AlertRule{}
|
|
exist, err := sess.Table(models.AlertRule{}).ID(rule.ID).Get(dbRule)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if !exist {
|
|
return errors.New("cannot read inserted record")
|
|
}
|
|
rule = dbRule
|
|
return nil
|
|
})
|
|
require.NoError(t, err)
|
|
return rule
|
|
}
|