mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Alerting: Move fake rule store to the test utilities package (#56062)
* Move fakeRuleStore to tests/fakes package * Break stub dependencies on store * Update existing tests to point to new location * Remove unused stub of TimeNow * Rename fake to take advantage of package name
This commit is contained in:
parent
82d7f80a15
commit
c16317e5b8
@ -20,7 +20,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/eval"
|
||||
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/state"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -423,7 +423,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
|
||||
t.Run("with many rules in a group", func(t *testing.T) {
|
||||
t.Run("should return sorted", func(t *testing.T) {
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
fakeAIM := NewFakeAlertInstanceManager(t)
|
||||
groupKey := ngmodels.GenerateGroupKey(orgID)
|
||||
_, rules := ngmodels.GenerateUniqueAlertRules(rand.Intn(5)+5, ngmodels.AlertRuleGen(withGroupKey(groupKey), ngmodels.WithUniqueGroupIndex()))
|
||||
@ -466,7 +466,7 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
|
||||
t.Run("when fine-grained access is enabled", func(t *testing.T) {
|
||||
t.Run("should return only rules if the user can query all data sources", func(t *testing.T) {
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
fakeAIM := NewFakeAlertInstanceManager(t)
|
||||
|
||||
rules := ngmodels.GenerateAlertRules(rand.Intn(4)+2, ngmodels.AlertRuleGen(withOrgID(orgID)))
|
||||
@ -503,8 +503,8 @@ func TestRouteGetRuleStatuses(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func setupAPI(t *testing.T) (*store.FakeRuleStore, *fakeAlertInstanceManager, *acmock.Mock, PrometheusSrv) {
|
||||
fakeStore := store.NewFakeRuleStore(t)
|
||||
func setupAPI(t *testing.T) (*fakes.RuleStore, *fakeAlertInstanceManager, *acmock.Mock, PrometheusSrv) {
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
fakeAIM := NewFakeAlertInstanceManager(t)
|
||||
acMock := acmock.New().WithDisabled()
|
||||
|
||||
@ -518,7 +518,7 @@ func setupAPI(t *testing.T) (*store.FakeRuleStore, *fakeAlertInstanceManager, *a
|
||||
return fakeStore, fakeAIM, acMock, api
|
||||
}
|
||||
|
||||
func generateRuleAndInstanceWithQuery(t *testing.T, orgID int64, fakeAIM *fakeAlertInstanceManager, fakeStore *store.FakeRuleStore, query func(r *ngmodels.AlertRule)) {
|
||||
func generateRuleAndInstanceWithQuery(t *testing.T, orgID int64, fakeAIM *fakeAlertInstanceManager, fakeStore *fakes.RuleStore, query func(r *ngmodels.AlertRule)) {
|
||||
t.Helper()
|
||||
|
||||
rules := ngmodels.GenerateAlertRules(1, ngmodels.AlertRuleGen(withOrgID(orgID), asFixture(), query))
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/provisioning"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
"github.com/grafana/grafana/pkg/services/org"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
@ -30,22 +31,22 @@ import (
|
||||
)
|
||||
|
||||
func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
getRecordedCommand := func(ruleStore *store.FakeRuleStore) []store.GenericRecordedQuery {
|
||||
getRecordedCommand := func(ruleStore *fakes.RuleStore) []fakes.GenericRecordedQuery {
|
||||
results := ruleStore.GetRecordedCommands(func(cmd interface{}) (interface{}, bool) {
|
||||
c, ok := cmd.(store.GenericRecordedQuery)
|
||||
c, ok := cmd.(fakes.GenericRecordedQuery)
|
||||
if !ok || c.Name != "DeleteAlertRulesByUID" {
|
||||
return nil, false
|
||||
}
|
||||
return c, ok
|
||||
})
|
||||
var result []store.GenericRecordedQuery
|
||||
var result []fakes.GenericRecordedQuery
|
||||
for _, cmd := range results {
|
||||
result = append(result, cmd.(store.GenericRecordedQuery))
|
||||
result = append(result, cmd.(fakes.GenericRecordedQuery))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
assertRulesDeleted := func(t *testing.T, expectedRules []*models.AlertRule, ruleStore *store.FakeRuleStore, scheduler *schedule.FakeScheduleService) {
|
||||
assertRulesDeleted := func(t *testing.T, expectedRules []*models.AlertRule, ruleStore *fakes.RuleStore, scheduler *schedule.FakeScheduleService) {
|
||||
deleteCommands := getRecordedCommand(ruleStore)
|
||||
require.Len(t, deleteCommands, 1)
|
||||
cmd := deleteCommands[0]
|
||||
@ -73,8 +74,8 @@ func TestRouteDeleteAlertRules(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
|
||||
initFakeRuleStore := func(t *testing.T) *store.FakeRuleStore {
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
initFakeRuleStore := func(t *testing.T) *fakes.RuleStore {
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
// add random data
|
||||
ruleStore.PutRule(context.Background(), models.GenerateAlertRulesSmallNonEmpty(models.AlertRuleGen(withOrgID(orgID)))...)
|
||||
@ -267,7 +268,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
t.Run("should return rules for which user has access to data source", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withOrgID(orgID), withNamespace(folder)))
|
||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||
@ -303,7 +304,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
t.Run("should return all rules from folder", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withOrgID(orgID), withNamespace(folder)))
|
||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||
@ -337,7 +338,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
t.Run("should return the provenance of the alert rules", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
expectedRules := models.GenerateAlertRules(rand.Intn(4)+2, models.AlertRuleGen(withOrgID(orgID), withNamespace(folder)))
|
||||
ruleStore.PutRule(context.Background(), expectedRules...)
|
||||
@ -378,7 +379,7 @@ func TestRouteGetNamespaceRulesConfig(t *testing.T) {
|
||||
t.Run("should enforce order of rules in the group", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
groupKey := models.GenerateGroupKey(orgID)
|
||||
groupKey.NamespaceUID = folder.Uid
|
||||
@ -422,7 +423,7 @@ func TestRouteGetRulesConfig(t *testing.T) {
|
||||
t.Run("fine-grained access is enabled", func(t *testing.T) {
|
||||
t.Run("should check access to data source", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
folder1 := randFolder()
|
||||
folder2 := randFolder()
|
||||
ruleStore.Folders[orgID] = []*models2.Folder{folder1, folder2}
|
||||
@ -460,7 +461,7 @@ func TestRouteGetRulesConfig(t *testing.T) {
|
||||
t.Run("should return rules in group sorted by group index", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
groupKey := models.GenerateGroupKey(orgID)
|
||||
groupKey.NamespaceUID = folder.Uid
|
||||
@ -505,7 +506,7 @@ func TestRouteGetRulesGroupConfig(t *testing.T) {
|
||||
t.Run("should check access to data source", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
groupKey := models.GenerateGroupKey(orgID)
|
||||
groupKey.NamespaceUID = folder.Uid
|
||||
@ -540,7 +541,7 @@ func TestRouteGetRulesGroupConfig(t *testing.T) {
|
||||
t.Run("should return rules in group sorted by group index", func(t *testing.T) {
|
||||
orgID := rand.Int63()
|
||||
folder := randFolder()
|
||||
ruleStore := store.NewFakeRuleStore(t)
|
||||
ruleStore := fakes.NewRuleStore(t)
|
||||
ruleStore.Folders[orgID] = append(ruleStore.Folders[orgID], folder)
|
||||
groupKey := models.GenerateGroupKey(orgID)
|
||||
groupKey.NamespaceUID = folder.Uid
|
||||
@ -636,13 +637,13 @@ func TestVerifyProvisionedRulesNotAffected(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func createServiceWithProvenanceStore(ac *acMock.Mock, store *store.FakeRuleStore, scheduler schedule.ScheduleService, provenanceStore provisioning.ProvisioningStore) *RulerSrv {
|
||||
func createServiceWithProvenanceStore(ac *acMock.Mock, store *fakes.RuleStore, scheduler schedule.ScheduleService, provenanceStore provisioning.ProvisioningStore) *RulerSrv {
|
||||
svc := createService(ac, store, scheduler)
|
||||
svc.provenanceStore = provenanceStore
|
||||
return svc
|
||||
}
|
||||
|
||||
func createService(ac *acMock.Mock, store *store.FakeRuleStore, scheduler schedule.ScheduleService) *RulerSrv {
|
||||
func createService(ac *acMock.Mock, store *fakes.RuleStore, scheduler schedule.ScheduleService) *RulerSrv {
|
||||
return &RulerSrv{
|
||||
xactManager: store,
|
||||
store: store,
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
models2 "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/schedule"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/store"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
@ -29,7 +29,7 @@ func Test_subscribeToFolderChanges(t *testing.T) {
|
||||
rules := models.GenerateAlertRules(5, models.AlertRuleGen(models.WithOrgID(orgID), models.WithNamespace(folder)))
|
||||
|
||||
bus := busmock.New()
|
||||
db := store.NewFakeRuleStore(t)
|
||||
db := fakes.NewRuleStore(t)
|
||||
db.Folders[orgID] = append(db.Folders[orgID], folder)
|
||||
db.PutRule(context.Background(), rules...)
|
||||
|
||||
@ -49,7 +49,7 @@ func Test_subscribeToFolderChanges(t *testing.T) {
|
||||
|
||||
require.Eventuallyf(t, func() bool {
|
||||
return len(db.GetRecordedCommands(func(cmd interface{}) (interface{}, bool) {
|
||||
c, ok := cmd.(store.GenericRecordedQuery)
|
||||
c, ok := cmd.(fakes.GenericRecordedQuery)
|
||||
if !ok || c.Name != "IncreaseVersionForAllRulesInNamespace" {
|
||||
return nil, false
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
|
||||
grafana_models "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/tests/fakes"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -19,7 +20,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
orgId := rand.Int63()
|
||||
|
||||
t.Run("detects alerts that need to be added", func(t *testing.T) {
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
|
||||
groupKey := models.GenerateGroupKey(orgId)
|
||||
submitted := models.GenerateAlertRules(rand.Intn(5)+1, models.AlertRuleGen(withOrgID(orgId), simulateSubmitted, withoutUID))
|
||||
@ -46,7 +47,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
groupKey := models.GenerateGroupKey(orgId)
|
||||
inDatabaseMap, inDatabase := models.GenerateUniqueAlertRules(rand.Intn(5)+1, models.AlertRuleGen(withGroupKey(groupKey)))
|
||||
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
fakeStore.PutRule(context.Background(), inDatabase...)
|
||||
|
||||
changes, err := CalculateChanges(context.Background(), fakeStore, groupKey, make([]*models.AlertRule, 0))
|
||||
@ -70,7 +71,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
inDatabaseMap, inDatabase := models.GenerateUniqueAlertRules(rand.Intn(5)+1, models.AlertRuleGen(withGroupKey(groupKey)))
|
||||
submittedMap, submitted := models.GenerateUniqueAlertRules(len(inDatabase), models.AlertRuleGen(simulateSubmitted, withGroupKey(groupKey), withUIDs(inDatabaseMap)))
|
||||
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
fakeStore.PutRule(context.Background(), inDatabase...)
|
||||
|
||||
changes, err := CalculateChanges(context.Background(), fakeStore, groupKey, submitted)
|
||||
@ -108,7 +109,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
submitted = append(submitted, r)
|
||||
}
|
||||
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
fakeStore.PutRule(context.Background(), inDatabase...)
|
||||
|
||||
changes, err := CalculateChanges(context.Background(), fakeStore, groupKey, submitted)
|
||||
@ -159,7 +160,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
|
||||
dbRule := models.AlertRuleGen(withOrgID(orgId))()
|
||||
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
fakeStore.PutRule(context.Background(), dbRule)
|
||||
|
||||
groupKey := models.GenerateGroupKey(orgId)
|
||||
@ -185,7 +186,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
sourceGroupKey := models.GenerateGroupKey(orgId)
|
||||
inDatabaseMap, inDatabase := models.GenerateUniqueAlertRules(rand.Intn(10)+10, models.AlertRuleGen(withGroupKey(sourceGroupKey)))
|
||||
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
fakeStore.PutRule(context.Background(), inDatabase...)
|
||||
|
||||
namespace := randFolder()
|
||||
@ -221,7 +222,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should fail when submitted rule has UID that does not exist in db", func(t *testing.T) {
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
groupKey := models.GenerateGroupKey(orgId)
|
||||
submitted := models.AlertRuleGen(withOrgID(orgId), simulateSubmitted)()
|
||||
require.NotEqual(t, "", submitted.UID)
|
||||
@ -231,7 +232,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should fail if cannot fetch current rules in the group", func(t *testing.T) {
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
expectedErr := errors.New("TEST ERROR")
|
||||
fakeStore.Hook = func(cmd interface{}) error {
|
||||
switch cmd.(type) {
|
||||
@ -249,7 +250,7 @@ func TestCalculateChanges(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("should fail if cannot fetch rule by UID", func(t *testing.T) {
|
||||
fakeStore := NewFakeRuleStore(t)
|
||||
fakeStore := fakes.NewRuleStore(t)
|
||||
expectedErr := errors.New("TEST ERROR")
|
||||
fakeStore.Hook = func(cmd interface{}) error {
|
||||
switch cmd.(type) {
|
||||
|
@ -2,16 +2,10 @@ package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
|
||||
models2 "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
)
|
||||
|
||||
@ -66,331 +60,6 @@ func (s *FakeImageStore) SaveImage(_ context.Context, image *models.Image) error
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewFakeRuleStore(t *testing.T) *FakeRuleStore {
|
||||
return &FakeRuleStore{
|
||||
t: t,
|
||||
Rules: map[int64][]*models.AlertRule{},
|
||||
Hook: func(interface{}) error {
|
||||
return nil
|
||||
},
|
||||
Folders: map[int64][]*models2.Folder{},
|
||||
}
|
||||
}
|
||||
|
||||
// FakeRuleStore mocks the RuleStore of the scheduler.
|
||||
type FakeRuleStore struct {
|
||||
t *testing.T
|
||||
mtx sync.Mutex
|
||||
// OrgID -> RuleGroup -> Namespace -> Rules
|
||||
Rules map[int64][]*models.AlertRule
|
||||
Hook func(cmd interface{}) error // use Hook if you need to intercept some query and return an error
|
||||
RecordedOps []interface{}
|
||||
Folders map[int64][]*models2.Folder
|
||||
}
|
||||
|
||||
type GenericRecordedQuery struct {
|
||||
Name string
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
// PutRule puts the rule in the Rules map. If there are existing rule in the same namespace, they will be overwritten
|
||||
func (f *FakeRuleStore) PutRule(_ context.Context, rules ...*models.AlertRule) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
mainloop:
|
||||
for _, r := range rules {
|
||||
rgs := f.Rules[r.OrgID]
|
||||
for idx, rulePtr := range rgs {
|
||||
if rulePtr.UID == r.UID {
|
||||
rgs[idx] = r
|
||||
continue mainloop
|
||||
}
|
||||
}
|
||||
rgs = append(rgs, r)
|
||||
f.Rules[r.OrgID] = rgs
|
||||
|
||||
var existing *models2.Folder
|
||||
folders := f.Folders[r.OrgID]
|
||||
for _, folder := range folders {
|
||||
if folder.Uid == r.NamespaceUID {
|
||||
existing = folder
|
||||
break
|
||||
}
|
||||
}
|
||||
if existing == nil {
|
||||
folders = append(folders, &models2.Folder{
|
||||
Id: rand.Int63(),
|
||||
Uid: r.NamespaceUID,
|
||||
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
|
||||
})
|
||||
f.Folders[r.OrgID] = folders
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetRecordedCommands filters recorded commands using predicate function. Returns the subset of the recorded commands that meet the predicate
|
||||
func (f *FakeRuleStore) GetRecordedCommands(predicate func(cmd interface{}) (interface{}, bool)) []interface{} {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
result := make([]interface{}, 0, len(f.RecordedOps))
|
||||
for _, op := range f.RecordedOps {
|
||||
cmd, ok := predicate(op)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
result = append(result, cmd)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) DeleteAlertRulesByUID(_ context.Context, orgID int64, UIDs ...string) error {
|
||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||
Name: "DeleteAlertRulesByUID",
|
||||
Params: []interface{}{orgID, UIDs},
|
||||
})
|
||||
|
||||
rules := f.Rules[orgID]
|
||||
|
||||
var result = make([]*models.AlertRule, 0, len(rules))
|
||||
|
||||
for _, rule := range rules {
|
||||
add := true
|
||||
for _, UID := range UIDs {
|
||||
if rule.UID == UID {
|
||||
add = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if add {
|
||||
result = append(result, rule)
|
||||
}
|
||||
}
|
||||
|
||||
f.Rules[orgID] = result
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) GetAlertRuleByUID(_ context.Context, q *models.GetAlertRuleByUIDQuery) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, *q)
|
||||
if err := f.Hook(*q); err != nil {
|
||||
return err
|
||||
}
|
||||
rules, ok := f.Rules[q.OrgID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
if rule.UID == q.UID {
|
||||
q.Result = rule
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) GetAlertRulesGroupByRuleUID(_ context.Context, q *models.GetAlertRulesGroupByRuleUIDQuery) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, *q)
|
||||
if err := f.Hook(*q); err != nil {
|
||||
return err
|
||||
}
|
||||
rules, ok := f.Rules[q.OrgID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var selected *models.AlertRule
|
||||
for _, rule := range rules {
|
||||
if rule.UID == q.UID {
|
||||
selected = rule
|
||||
break
|
||||
}
|
||||
}
|
||||
if selected == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
if rule.GetGroupKey() == selected.GetGroupKey() {
|
||||
q.Result = append(q.Result, rule)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQuery) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, *q)
|
||||
|
||||
if err := f.Hook(*q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasDashboard := func(r *models.AlertRule, dashboardUID string, panelID int64) bool {
|
||||
if dashboardUID != "" {
|
||||
if r.DashboardUID == nil || *r.DashboardUID != dashboardUID {
|
||||
return false
|
||||
}
|
||||
if panelID > 0 {
|
||||
if r.PanelID == nil || *r.PanelID != panelID {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
hasNamespace := func(r *models.AlertRule, namespaceUIDs []string) bool {
|
||||
if len(namespaceUIDs) > 0 {
|
||||
var ok bool
|
||||
for _, uid := range q.NamespaceUIDs {
|
||||
if uid == r.NamespaceUID {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, r := range f.Rules[q.OrgID] {
|
||||
if !hasDashboard(r, q.DashboardUID, q.PanelID) {
|
||||
continue
|
||||
}
|
||||
if !hasNamespace(r, q.NamespaceUIDs) {
|
||||
continue
|
||||
}
|
||||
if q.RuleGroup != "" && r.RuleGroup != q.RuleGroup {
|
||||
continue
|
||||
}
|
||||
q.Result = append(q.Result, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*models2.Folder, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
namespacesMap := map[string]*models2.Folder{}
|
||||
|
||||
_, ok := f.Rules[orgID]
|
||||
if !ok {
|
||||
return namespacesMap, nil
|
||||
}
|
||||
|
||||
for _, folder := range f.Folders[orgID] {
|
||||
namespacesMap[folder.Uid] = folder
|
||||
}
|
||||
return namespacesMap, nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser, _ bool) (*models2.Folder, error) {
|
||||
folders := f.Folders[orgID]
|
||||
for _, folder := range folders {
|
||||
if folder.Title == title {
|
||||
return folder, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*models2.Folder, error) {
|
||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||
Name: "GetNamespaceByUID",
|
||||
Params: []interface{}{orgID, uid},
|
||||
})
|
||||
|
||||
folders := f.Folders[orgID]
|
||||
for _, folder := range folders {
|
||||
if folder.Uid == uid {
|
||||
return folder, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) UpdateAlertRules(_ context.Context, q []models.UpdateRule) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, q)
|
||||
if err := f.Hook(q); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) InsertAlertRules(_ context.Context, q []models.AlertRule) (map[string]int64, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, q)
|
||||
ids := make(map[string]int64, len(q))
|
||||
if err := f.Hook(q); err != nil {
|
||||
return ids, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) InTransaction(ctx context.Context, fn func(c context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) (int64, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
for _, rule := range f.Rules[orgID] {
|
||||
if rule.RuleGroup == ruleGroup && rule.NamespaceUID == namespaceUID {
|
||||
return rule.IntervalSeconds, nil
|
||||
}
|
||||
}
|
||||
return 0, ErrAlertRuleGroupNotFound
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) UpdateRuleGroup(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string, interval int64) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
for _, rule := range f.Rules[orgID] {
|
||||
if rule.RuleGroup == ruleGroup && rule.NamespaceUID == namespaceUID {
|
||||
rule.IntervalSeconds = interval
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *FakeRuleStore) IncreaseVersionForAllRulesInNamespace(_ context.Context, orgID int64, namespaceUID string) ([]models.AlertRuleKeyWithVersion, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||
Name: "IncreaseVersionForAllRulesInNamespace",
|
||||
Params: []interface{}{orgID, namespaceUID},
|
||||
})
|
||||
|
||||
var result []models.AlertRuleKeyWithVersion
|
||||
|
||||
for _, rule := range f.Rules[orgID] {
|
||||
if rule.NamespaceUID == namespaceUID && rule.OrgID == orgID {
|
||||
rule.Version++
|
||||
rule.Updated = TimeNow()
|
||||
result = append(result, models.AlertRuleKeyWithVersion{
|
||||
Version: rule.Version,
|
||||
AlertRuleKey: rule.GetKey(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func NewFakeAdminConfigStore(t *testing.T) *FakeAdminConfigStore {
|
||||
t.Helper()
|
||||
return &FakeAdminConfigStore{Configs: map[int64]*models.AdminConfiguration{}}
|
||||
|
341
pkg/services/ngalert/tests/fakes/rules.go
Normal file
341
pkg/services/ngalert/tests/fakes/rules.go
Normal file
@ -0,0 +1,341 @@
|
||||
package fakes
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
models2 "github.com/grafana/grafana/pkg/models"
|
||||
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
||||
"github.com/grafana/grafana/pkg/services/user"
|
||||
"github.com/grafana/grafana/pkg/util"
|
||||
)
|
||||
|
||||
// FakeRuleStore mocks the RuleStore of the scheduler.
|
||||
type RuleStore struct {
|
||||
t *testing.T
|
||||
mtx sync.Mutex
|
||||
// OrgID -> RuleGroup -> Namespace -> Rules
|
||||
Rules map[int64][]*models.AlertRule
|
||||
Hook func(cmd interface{}) error // use Hook if you need to intercept some query and return an error
|
||||
RecordedOps []interface{}
|
||||
Folders map[int64][]*models2.Folder
|
||||
}
|
||||
|
||||
type GenericRecordedQuery struct {
|
||||
Name string
|
||||
Params []interface{}
|
||||
}
|
||||
|
||||
func NewRuleStore(t *testing.T) *RuleStore {
|
||||
return &RuleStore{
|
||||
t: t,
|
||||
Rules: map[int64][]*models.AlertRule{},
|
||||
Hook: func(interface{}) error {
|
||||
return nil
|
||||
},
|
||||
Folders: map[int64][]*models2.Folder{},
|
||||
}
|
||||
}
|
||||
|
||||
// PutRule puts the rule in the Rules map. If there are existing rule in the same namespace, they will be overwritten
|
||||
func (f *RuleStore) PutRule(_ context.Context, rules ...*models.AlertRule) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
mainloop:
|
||||
for _, r := range rules {
|
||||
rgs := f.Rules[r.OrgID]
|
||||
for idx, rulePtr := range rgs {
|
||||
if rulePtr.UID == r.UID {
|
||||
rgs[idx] = r
|
||||
continue mainloop
|
||||
}
|
||||
}
|
||||
rgs = append(rgs, r)
|
||||
f.Rules[r.OrgID] = rgs
|
||||
|
||||
var existing *models2.Folder
|
||||
folders := f.Folders[r.OrgID]
|
||||
for _, folder := range folders {
|
||||
if folder.Uid == r.NamespaceUID {
|
||||
existing = folder
|
||||
break
|
||||
}
|
||||
}
|
||||
if existing == nil {
|
||||
folders = append(folders, &models2.Folder{
|
||||
Id: rand.Int63(),
|
||||
Uid: r.NamespaceUID,
|
||||
Title: "TEST-FOLDER-" + util.GenerateShortUID(),
|
||||
})
|
||||
f.Folders[r.OrgID] = folders
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// GetRecordedCommands filters recorded commands using predicate function. Returns the subset of the recorded commands that meet the predicate
|
||||
func (f *RuleStore) GetRecordedCommands(predicate func(cmd interface{}) (interface{}, bool)) []interface{} {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
result := make([]interface{}, 0, len(f.RecordedOps))
|
||||
for _, op := range f.RecordedOps {
|
||||
cmd, ok := predicate(op)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
result = append(result, cmd)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (f *RuleStore) DeleteAlertRulesByUID(_ context.Context, orgID int64, UIDs ...string) error {
|
||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||
Name: "DeleteAlertRulesByUID",
|
||||
Params: []interface{}{orgID, UIDs},
|
||||
})
|
||||
|
||||
rules := f.Rules[orgID]
|
||||
|
||||
var result = make([]*models.AlertRule, 0, len(rules))
|
||||
|
||||
for _, rule := range rules {
|
||||
add := true
|
||||
for _, UID := range UIDs {
|
||||
if rule.UID == UID {
|
||||
add = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if add {
|
||||
result = append(result, rule)
|
||||
}
|
||||
}
|
||||
|
||||
f.Rules[orgID] = result
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetAlertRuleByUID(_ context.Context, q *models.GetAlertRuleByUIDQuery) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, *q)
|
||||
if err := f.Hook(*q); err != nil {
|
||||
return err
|
||||
}
|
||||
rules, ok := f.Rules[q.OrgID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
if rule.UID == q.UID {
|
||||
q.Result = rule
|
||||
break
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetAlertRulesGroupByRuleUID(_ context.Context, q *models.GetAlertRulesGroupByRuleUIDQuery) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, *q)
|
||||
if err := f.Hook(*q); err != nil {
|
||||
return err
|
||||
}
|
||||
rules, ok := f.Rules[q.OrgID]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
var selected *models.AlertRule
|
||||
for _, rule := range rules {
|
||||
if rule.UID == q.UID {
|
||||
selected = rule
|
||||
break
|
||||
}
|
||||
}
|
||||
if selected == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, rule := range rules {
|
||||
if rule.GetGroupKey() == selected.GetGroupKey() {
|
||||
q.Result = append(q.Result, rule)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) ListAlertRules(_ context.Context, q *models.ListAlertRulesQuery) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, *q)
|
||||
|
||||
if err := f.Hook(*q); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
hasDashboard := func(r *models.AlertRule, dashboardUID string, panelID int64) bool {
|
||||
if dashboardUID != "" {
|
||||
if r.DashboardUID == nil || *r.DashboardUID != dashboardUID {
|
||||
return false
|
||||
}
|
||||
if panelID > 0 {
|
||||
if r.PanelID == nil || *r.PanelID != panelID {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
hasNamespace := func(r *models.AlertRule, namespaceUIDs []string) bool {
|
||||
if len(namespaceUIDs) > 0 {
|
||||
var ok bool
|
||||
for _, uid := range q.NamespaceUIDs {
|
||||
if uid == r.NamespaceUID {
|
||||
ok = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
for _, r := range f.Rules[q.OrgID] {
|
||||
if !hasDashboard(r, q.DashboardUID, q.PanelID) {
|
||||
continue
|
||||
}
|
||||
if !hasNamespace(r, q.NamespaceUIDs) {
|
||||
continue
|
||||
}
|
||||
if q.RuleGroup != "" && r.RuleGroup != q.RuleGroup {
|
||||
continue
|
||||
}
|
||||
q.Result = append(q.Result, r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetUserVisibleNamespaces(_ context.Context, orgID int64, _ *user.SignedInUser) (map[string]*models2.Folder, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
namespacesMap := map[string]*models2.Folder{}
|
||||
|
||||
_, ok := f.Rules[orgID]
|
||||
if !ok {
|
||||
return namespacesMap, nil
|
||||
}
|
||||
|
||||
for _, folder := range f.Folders[orgID] {
|
||||
namespacesMap[folder.Uid] = folder
|
||||
}
|
||||
return namespacesMap, nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetNamespaceByTitle(_ context.Context, title string, orgID int64, _ *user.SignedInUser, _ bool) (*models2.Folder, error) {
|
||||
folders := f.Folders[orgID]
|
||||
for _, folder := range folders {
|
||||
if folder.Title == title {
|
||||
return folder, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetNamespaceByUID(_ context.Context, uid string, orgID int64, _ *user.SignedInUser) (*models2.Folder, error) {
|
||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||
Name: "GetNamespaceByUID",
|
||||
Params: []interface{}{orgID, uid},
|
||||
})
|
||||
|
||||
folders := f.Folders[orgID]
|
||||
for _, folder := range folders {
|
||||
if folder.Uid == uid {
|
||||
return folder, nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("not found")
|
||||
}
|
||||
|
||||
func (f *RuleStore) UpdateAlertRules(_ context.Context, q []models.UpdateRule) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, q)
|
||||
if err := f.Hook(q); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) InsertAlertRules(_ context.Context, q []models.AlertRule) (map[string]int64, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
f.RecordedOps = append(f.RecordedOps, q)
|
||||
ids := make(map[string]int64, len(q))
|
||||
if err := f.Hook(q); err != nil {
|
||||
return ids, err
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) InTransaction(ctx context.Context, fn func(c context.Context) error) error {
|
||||
return fn(ctx)
|
||||
}
|
||||
|
||||
func (f *RuleStore) GetRuleGroupInterval(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string) (int64, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
for _, rule := range f.Rules[orgID] {
|
||||
if rule.RuleGroup == ruleGroup && rule.NamespaceUID == namespaceUID {
|
||||
return rule.IntervalSeconds, nil
|
||||
}
|
||||
}
|
||||
return 0, errors.New("rule group not found")
|
||||
}
|
||||
|
||||
func (f *RuleStore) UpdateRuleGroup(ctx context.Context, orgID int64, namespaceUID string, ruleGroup string, interval int64) error {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
for _, rule := range f.Rules[orgID] {
|
||||
if rule.RuleGroup == ruleGroup && rule.NamespaceUID == namespaceUID {
|
||||
rule.IntervalSeconds = interval
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *RuleStore) IncreaseVersionForAllRulesInNamespace(_ context.Context, orgID int64, namespaceUID string) ([]models.AlertRuleKeyWithVersion, error) {
|
||||
f.mtx.Lock()
|
||||
defer f.mtx.Unlock()
|
||||
|
||||
f.RecordedOps = append(f.RecordedOps, GenericRecordedQuery{
|
||||
Name: "IncreaseVersionForAllRulesInNamespace",
|
||||
Params: []interface{}{orgID, namespaceUID},
|
||||
})
|
||||
|
||||
var result []models.AlertRuleKeyWithVersion
|
||||
|
||||
for _, rule := range f.Rules[orgID] {
|
||||
if rule.NamespaceUID == namespaceUID && rule.OrgID == orgID {
|
||||
rule.Version++
|
||||
rule.Updated = time.Now()
|
||||
result = append(result, models.AlertRuleKeyWithVersion{
|
||||
Version: rule.Version,
|
||||
AlertRuleKey: rule.GetKey(),
|
||||
})
|
||||
}
|
||||
}
|
||||
return result, nil
|
||||
}
|
Loading…
Reference in New Issue
Block a user