grafana/pkg/services/ngalert/schedule/loaded_metrics_reader_test.go
Yuri Tseretyan f6a46744a6
Alerting: Support hysteresis command expression (#75189)
Backend: 

* Update the Grafana Alerting engine to provide feedback to HysteresisCommand. The feedback information is stored in state.Manager as a fingerprint of each state. The fingerprint is persisted to the database. Only fingerprints that belong to Pending and Alerting states are considered as "loaded" and provided back to the command.
   - add ResultFingerprint to state.State. It's different from other fingerprints we store in the state because it is calculated from the result labels.
  -  add rule_fingerprint column to alert_instance
   - update alerting evaluator to accept AlertingResultsReader via context, and update scheduler to provide it.
   - add AlertingResultsFromRuleState that implements the new interface in eval package
   - update getExprRequest to patch the hysteresis command.

* Only one "Recovery Threshold" query is allowed to be used in the alert rule and it must be the Condition.


Frontend:

* Add hysteresis option to Threshold in UI. It's called "Recovery Threshold"
* Add test for getUnloadEvaluatorTypeFromCondition
* Hide hysteresis in panel expressions

* Refactor isInvalid and add test for it
* Remove unnecesary React.memo
* Add tests for updateEvaluatorConditions

---------

Co-authored-by: Sonia Aguilar <soniaaguilarpeiron@gmail.com>
2024-01-04 11:47:13 -05:00

66 lines
1.7 KiB
Go

package schedule
import (
"testing"
"github.com/google/uuid"
"github.com/grafana/grafana-plugin-sdk-go/data"
"github.com/stretchr/testify/require"
"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"
)
func TestLoadedResultsFromRuleState(t *testing.T) {
rule := ngmodels.AlertRuleGen()()
p := &FakeRuleStateProvider{
map[ngmodels.AlertRuleKey][]*state.State{
rule.GetKey(): {
{State: eval.Alerting, ResultFingerprint: data.Fingerprint(1)},
{State: eval.Pending, ResultFingerprint: data.Fingerprint(2)},
{State: eval.Normal, ResultFingerprint: data.Fingerprint(3)},
{State: eval.NoData, ResultFingerprint: data.Fingerprint(4)},
{State: eval.Error, ResultFingerprint: data.Fingerprint(5)},
},
},
}
reader := AlertingResultsFromRuleState{
Manager: p,
Rule: rule,
}
t.Run("should return pending and alerting states", func(t *testing.T) {
loaded := reader.Read()
require.Len(t, loaded, 2)
require.Contains(t, loaded, data.Fingerprint(1))
require.Contains(t, loaded, data.Fingerprint(2))
})
t.Run("should not return any states with reason", func(t *testing.T) {
for _, s := range p.states[rule.GetKey()] {
s.StateReason = uuid.NewString()
}
loaded := reader.Read()
require.Empty(t, loaded)
})
t.Run("empty if no states", func(t *testing.T) {
p.states[rule.GetKey()] = nil
loaded := reader.Read()
require.Empty(t, loaded)
})
}
type FakeRuleStateProvider struct {
states map[ngmodels.AlertRuleKey][]*state.State
}
func (f FakeRuleStateProvider) GetStatesForRuleUID(orgID int64, UID string) []*state.State {
return f.states[ngmodels.AlertRuleKey{
OrgID: orgID,
UID: UID,
}]
}