mirror of
https://github.com/grafana/grafana.git
synced 2024-11-25 18:30:41 -06:00
fa51724bc6
Move ruleinfo and tests to new files
280 lines
9.0 KiB
Go
280 lines
9.0 KiB
Go
package schedule
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math/rand"
|
|
"reflect"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/services/ngalert/models"
|
|
)
|
|
|
|
func TestSchedulableAlertRulesRegistry(t *testing.T) {
|
|
r := alertRulesRegistry{rules: make(map[models.AlertRuleKey]*models.AlertRule)}
|
|
rules, folders := r.all()
|
|
assert.Len(t, rules, 0)
|
|
assert.Len(t, folders, 0)
|
|
|
|
expectedFolders := map[models.FolderKey]string{{OrgID: 1, UID: "test-uid"}: "test-title"}
|
|
// replace all rules in the registry with foo
|
|
r.set([]*models.AlertRule{{OrgID: 1, UID: "foo", Version: 1}}, expectedFolders)
|
|
rules, folders = r.all()
|
|
assert.Len(t, rules, 1)
|
|
assert.Equal(t, expectedFolders, folders)
|
|
|
|
foo := r.get(models.AlertRuleKey{OrgID: 1, UID: "foo"})
|
|
require.NotNil(t, foo)
|
|
assert.Equal(t, models.AlertRule{OrgID: 1, UID: "foo", Version: 1}, *foo)
|
|
|
|
// update foo to a newer version
|
|
r.update(&models.AlertRule{OrgID: 1, UID: "foo", Version: 2})
|
|
rules, _ = r.all()
|
|
assert.Len(t, rules, 1)
|
|
foo = r.get(models.AlertRuleKey{OrgID: 1, UID: "foo"})
|
|
require.NotNil(t, foo)
|
|
assert.Equal(t, models.AlertRule{OrgID: 1, UID: "foo", Version: 2}, *foo)
|
|
|
|
// update bar which does not exist in the registry
|
|
r.update(&models.AlertRule{OrgID: 1, UID: "bar", Version: 1})
|
|
rules, _ = r.all()
|
|
assert.Len(t, rules, 2)
|
|
foo = r.get(models.AlertRuleKey{OrgID: 1, UID: "foo"})
|
|
require.NotNil(t, foo)
|
|
assert.Equal(t, models.AlertRule{OrgID: 1, UID: "foo", Version: 2}, *foo)
|
|
bar := r.get(models.AlertRuleKey{OrgID: 1, UID: "bar"})
|
|
require.NotNil(t, foo)
|
|
assert.Equal(t, models.AlertRule{OrgID: 1, UID: "bar", Version: 1}, *bar)
|
|
|
|
// replace all rules in the registry with baz
|
|
r.set([]*models.AlertRule{{OrgID: 1, UID: "baz", Version: 1}}, nil)
|
|
rules, folders = r.all()
|
|
assert.Len(t, rules, 1)
|
|
assert.Nil(t, folders)
|
|
baz := r.get(models.AlertRuleKey{OrgID: 1, UID: "baz"})
|
|
require.NotNil(t, baz)
|
|
assert.Equal(t, models.AlertRule{OrgID: 1, UID: "baz", Version: 1}, *baz)
|
|
assert.Nil(t, r.get(models.AlertRuleKey{OrgID: 1, UID: "foo"}))
|
|
assert.Nil(t, r.get(models.AlertRuleKey{OrgID: 1, UID: "bar"}))
|
|
|
|
// delete baz
|
|
deleted, ok := r.del(models.AlertRuleKey{OrgID: 1, UID: "baz"})
|
|
assert.True(t, ok)
|
|
require.NotNil(t, deleted)
|
|
assert.Equal(t, *deleted, *baz)
|
|
rules, folders = r.all()
|
|
assert.Len(t, rules, 0)
|
|
assert.Len(t, folders, 0)
|
|
assert.Nil(t, r.get(models.AlertRuleKey{OrgID: 1, UID: "baz"}))
|
|
|
|
// baz cannot be deleted twice
|
|
deleted, ok = r.del(models.AlertRuleKey{OrgID: 1, UID: "baz"})
|
|
assert.False(t, ok)
|
|
assert.Nil(t, deleted)
|
|
}
|
|
|
|
func TestSchedulableAlertRulesRegistry_set(t *testing.T) {
|
|
_, initialRules := models.GenerateUniqueAlertRules(100, models.AlertRuleGen())
|
|
init := make(map[models.AlertRuleKey]*models.AlertRule, len(initialRules))
|
|
for _, rule := range initialRules {
|
|
init[rule.GetKey()] = rule
|
|
}
|
|
r := alertRulesRegistry{rules: init}
|
|
t.Run("should return empty diff if exactly the same rules", func(t *testing.T) {
|
|
newRules := make([]*models.AlertRule, 0, len(initialRules))
|
|
for _, rule := range initialRules {
|
|
newRules = append(newRules, models.CopyRule(rule))
|
|
}
|
|
diff := r.set(newRules, map[models.FolderKey]string{})
|
|
require.Truef(t, diff.IsEmpty(), "Diff is not empty. Probably we check something else than key + version")
|
|
})
|
|
t.Run("should return empty diff if version does not change", func(t *testing.T) {
|
|
newRules := make([]*models.AlertRule, 0, len(initialRules))
|
|
// generate random and then override rule key + version
|
|
_, randomNew := models.GenerateUniqueAlertRules(len(initialRules), models.AlertRuleGen())
|
|
for i := 0; i < len(initialRules); i++ {
|
|
rule := randomNew[i]
|
|
oldRule := initialRules[i]
|
|
rule.UID = oldRule.UID
|
|
rule.OrgID = oldRule.OrgID
|
|
rule.Version = oldRule.Version
|
|
newRules = append(newRules, rule)
|
|
}
|
|
|
|
diff := r.set(newRules, map[models.FolderKey]string{})
|
|
require.Truef(t, diff.IsEmpty(), "Diff is not empty. Probably we check something else than key + version")
|
|
})
|
|
t.Run("should return key in diff if version changes", func(t *testing.T) {
|
|
newRules := make([]*models.AlertRule, 0, len(initialRules))
|
|
expectedUpdated := map[models.AlertRuleKey]struct{}{}
|
|
for i, rule := range initialRules {
|
|
cp := models.CopyRule(rule)
|
|
if i%2 == 0 {
|
|
cp.Version++
|
|
expectedUpdated[cp.GetKey()] = struct{}{}
|
|
}
|
|
newRules = append(newRules, cp)
|
|
}
|
|
require.NotEmptyf(t, expectedUpdated, "Input parameters have changed. Nothing to assert")
|
|
|
|
diff := r.set(newRules, map[models.FolderKey]string{})
|
|
require.Falsef(t, diff.IsEmpty(), "Diff is empty but should not be")
|
|
require.Equal(t, expectedUpdated, diff.updated)
|
|
})
|
|
}
|
|
|
|
func TestRuleWithFolderFingerprint(t *testing.T) {
|
|
rule := models.AlertRuleGen()()
|
|
title := uuid.NewString()
|
|
f := ruleWithFolder{rule: rule, folderTitle: title}.Fingerprint()
|
|
t.Run("should calculate a fingerprint", func(t *testing.T) {
|
|
require.NotEqual(t, 0, uint64(f))
|
|
})
|
|
t.Run("mirror copy should have the same fingerprint", func(t *testing.T) {
|
|
f2 := ruleWithFolder{rule: models.CopyRule(rule), folderTitle: title}.Fingerprint()
|
|
require.Equal(t, f, f2)
|
|
})
|
|
t.Run("order of queries should not affect the fingerprint", func(t *testing.T) {
|
|
cp := models.CopyRule(rule)
|
|
rand.Shuffle(len(cp.Data), func(i, j int) {
|
|
cp.Data[i], cp.Data[j] = cp.Data[j], cp.Data[i]
|
|
})
|
|
f2 := ruleWithFolder{rule: cp, folderTitle: title}.Fingerprint()
|
|
require.Equal(t, f, f2)
|
|
})
|
|
t.Run("folder name should be used in fingerprint", func(t *testing.T) {
|
|
f2 := ruleWithFolder{rule: rule, folderTitle: uuid.NewString()}.Fingerprint()
|
|
require.NotEqual(t, f, f2)
|
|
})
|
|
t.Run("Version and Updated should be excluded from fingerprint", func(t *testing.T) {
|
|
cp := models.CopyRule(rule)
|
|
cp.Version++
|
|
cp.Updated = cp.Updated.Add(1 * time.Second)
|
|
|
|
f2 := ruleWithFolder{rule: cp, folderTitle: title}.Fingerprint()
|
|
require.Equal(t, f, f2)
|
|
})
|
|
|
|
t.Run("all other fields should be considered", func(t *testing.T) {
|
|
r1 := &models.AlertRule{
|
|
ID: 1,
|
|
OrgID: 2,
|
|
Title: "test",
|
|
Condition: "A",
|
|
Data: []models.AlertQuery{
|
|
{
|
|
RefID: "1",
|
|
QueryType: "323",
|
|
RelativeTimeRange: models.RelativeTimeRange{
|
|
From: 1,
|
|
To: 2,
|
|
},
|
|
DatasourceUID: "123",
|
|
Model: json.RawMessage(`{"test": "test-model"}`),
|
|
},
|
|
},
|
|
Updated: time.Now(),
|
|
IntervalSeconds: 2,
|
|
Version: 1,
|
|
UID: "test-uid",
|
|
NamespaceUID: "test-ns",
|
|
DashboardUID: func(s string) *string { return &s }("dashboard"),
|
|
PanelID: func(i int64) *int64 { return &i }(123),
|
|
RuleGroup: "test-group",
|
|
RuleGroupIndex: 1,
|
|
NoDataState: "test-nodata",
|
|
ExecErrState: "test-err",
|
|
For: 12,
|
|
Annotations: map[string]string{
|
|
"key-annotation": "value-annotation",
|
|
},
|
|
Labels: map[string]string{
|
|
"key-label": "value-label",
|
|
},
|
|
IsPaused: false,
|
|
NotificationSettings: []models.NotificationSettings{
|
|
models.NotificationSettingsGen()(),
|
|
},
|
|
}
|
|
r2 := &models.AlertRule{
|
|
ID: 2,
|
|
OrgID: 3,
|
|
Title: "test-2",
|
|
Condition: "B",
|
|
Data: []models.AlertQuery{
|
|
{
|
|
RefID: "2",
|
|
QueryType: "12313123",
|
|
RelativeTimeRange: models.RelativeTimeRange{
|
|
From: 2,
|
|
To: 3,
|
|
},
|
|
DatasourceUID: "asdasdasd21",
|
|
Model: json.RawMessage(`{"test": "test-model-2"}`),
|
|
},
|
|
},
|
|
IntervalSeconds: 23,
|
|
UID: "test-uid2",
|
|
NamespaceUID: "test-ns2",
|
|
DashboardUID: func(s string) *string { return &s }("dashboard-2"),
|
|
PanelID: func(i int64) *int64 { return &i }(1222),
|
|
RuleGroup: "test-group-2",
|
|
RuleGroupIndex: 22,
|
|
NoDataState: "test-nodata2",
|
|
ExecErrState: "test-err2",
|
|
For: 1141,
|
|
Annotations: map[string]string{
|
|
"key-annotation2": "value-annotation",
|
|
},
|
|
Labels: map[string]string{
|
|
"key-label": "value-label23",
|
|
},
|
|
IsPaused: true,
|
|
NotificationSettings: []models.NotificationSettings{
|
|
models.NotificationSettingsGen()(),
|
|
},
|
|
}
|
|
|
|
excludedFields := map[string]struct{}{
|
|
"Version": {},
|
|
"Updated": {},
|
|
}
|
|
|
|
tp := reflect.TypeOf(rule).Elem()
|
|
var nonDiffFields []string
|
|
// making sure that we get completely different struct
|
|
|
|
dif := r1.Diff(r2)
|
|
nonDiffFields = make([]string, 0)
|
|
for j := 0; j < tp.NumField(); j++ {
|
|
name := tp.Field(j).Name
|
|
if _, ok := excludedFields[name]; ok {
|
|
continue
|
|
}
|
|
if len(dif.GetDiffsForField(tp.Field(j).Name)) == 0 {
|
|
nonDiffFields = append(nonDiffFields, tp.Field(j).Name)
|
|
}
|
|
}
|
|
require.Emptyf(t, nonDiffFields, "cannot generate completely unique alert rule. Some fields are not randomized")
|
|
|
|
r2v := reflect.ValueOf(r2).Elem()
|
|
for i := 0; i < tp.NumField(); i++ {
|
|
if _, ok := excludedFields[tp.Field(i).Name]; ok {
|
|
continue
|
|
}
|
|
cp := models.CopyRule(r1)
|
|
v := reflect.ValueOf(cp).Elem()
|
|
vf := v.Field(i)
|
|
vf.Set(r2v.Field(i))
|
|
f2 := ruleWithFolder{rule: cp, folderTitle: title}.Fingerprint()
|
|
if f2 == f {
|
|
t.Fatalf("Field %s does not seem to be used in fingerprint. Diff: %s", tp.Field(i).Name, r1.Diff(cp))
|
|
}
|
|
}
|
|
})
|
|
}
|