|
|
|
@ -2,11 +2,11 @@ package alerting |
|
|
|
|
|
|
|
|
|
import ( |
|
|
|
|
"context" |
|
|
|
|
"fmt" |
|
|
|
|
"errors" |
|
|
|
|
"testing" |
|
|
|
|
"time" |
|
|
|
|
|
|
|
|
|
"github.com/grafana/grafana/pkg/models" |
|
|
|
|
. "github.com/smartystreets/goconvey/convey" |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
func TestStateIsUpdatedWhenNeeded(t *testing.T) { |
|
|
|
@ -31,71 +31,123 @@ func TestStateIsUpdatedWhenNeeded(t *testing.T) { |
|
|
|
|
}) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
func TestAlertingEvalContext(t *testing.T) { |
|
|
|
|
Convey("Should compute and replace properly new rule state", t, func() { |
|
|
|
|
func TestGetStateFromEvalContext(t *testing.T) { |
|
|
|
|
tcs := []struct { |
|
|
|
|
name string |
|
|
|
|
expected models.AlertStateType |
|
|
|
|
applyFn func(ec *EvalContext) |
|
|
|
|
focus bool |
|
|
|
|
}{ |
|
|
|
|
{ |
|
|
|
|
name: "ok -> alerting", |
|
|
|
|
expected: models.AlertStateAlerting, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.Firing = true |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> error(alerting)", |
|
|
|
|
expected: models.AlertStateAlerting, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Error = errors.New("test error") |
|
|
|
|
ec.Rule.ExecutionErrorState = models.ExecutionErrorSetAlerting |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> pending. since its been firing for less than FOR", |
|
|
|
|
expected: models.AlertStatePending, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Firing = true |
|
|
|
|
ec.Rule.LastStateChange = time.Now().Add(-time.Minute * 2) |
|
|
|
|
ec.Rule.DebounceDuration = time.Minute * 5 |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> alerting. since its been firing for more than FOR", |
|
|
|
|
expected: models.AlertStateAlerting, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Firing = true |
|
|
|
|
ec.Rule.LastStateChange = time.Now().Add(-(time.Hour * 5)) |
|
|
|
|
ec.Rule.DebounceDuration = time.Minute * 2 |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "alerting -> alerting. should not update regardless of FOR", |
|
|
|
|
expected: models.AlertStateAlerting, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateAlerting |
|
|
|
|
ec.Firing = true |
|
|
|
|
ec.Rule.LastStateChange = time.Now().Add(-time.Minute * 5) |
|
|
|
|
ec.Rule.DebounceDuration = time.Minute * 2 |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> ok. should not update regardless of FOR", |
|
|
|
|
expected: models.AlertStateOK, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Rule.LastStateChange = time.Now().Add(-time.Minute * 5) |
|
|
|
|
ec.Rule.DebounceDuration = time.Minute * 2 |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> error(keep_last)", |
|
|
|
|
expected: models.AlertStateOK, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Error = errors.New("test error") |
|
|
|
|
ec.Rule.ExecutionErrorState = models.ExecutionErrorKeepState |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "pending -> error(keep_last)", |
|
|
|
|
expected: models.AlertStatePending, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStatePending |
|
|
|
|
ec.Error = errors.New("test error") |
|
|
|
|
ec.Rule.ExecutionErrorState = models.ExecutionErrorKeepState |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> no_data(alerting)", |
|
|
|
|
expected: models.AlertStateAlerting, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Rule.NoDataState = models.NoDataSetAlerting |
|
|
|
|
ec.NoDataFound = true |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "ok -> no_data(keep_last)", |
|
|
|
|
expected: models.AlertStateOK, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStateOK |
|
|
|
|
ec.Rule.NoDataState = models.NoDataKeepState |
|
|
|
|
ec.NoDataFound = true |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
{ |
|
|
|
|
name: "pending -> no_data(keep_last)", |
|
|
|
|
expected: models.AlertStatePending, |
|
|
|
|
applyFn: func(ec *EvalContext) { |
|
|
|
|
ec.PrevAlertState = models.AlertStatePending |
|
|
|
|
ec.Rule.NoDataState = models.NoDataKeepState |
|
|
|
|
ec.NoDataFound = true |
|
|
|
|
}, |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for _, tc := range tcs { |
|
|
|
|
ctx := NewEvalContext(context.TODO(), &Rule{Conditions: []Condition{&conditionStub{firing: true}}}) |
|
|
|
|
dummieError := fmt.Errorf("dummie error") |
|
|
|
|
|
|
|
|
|
Convey("ok -> alerting", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStateOK |
|
|
|
|
ctx.Firing = true |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
Convey("ok -> error(alerting)", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStateOK |
|
|
|
|
ctx.Error = dummieError |
|
|
|
|
ctx.Rule.ExecutionErrorState = models.ExecutionErrorSetAlerting |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
Convey("ok -> error(keep_last)", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStateOK |
|
|
|
|
ctx.Error = dummieError |
|
|
|
|
ctx.Rule.ExecutionErrorState = models.ExecutionErrorKeepState |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateOK) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
Convey("pending -> error(keep_last)", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStatePending |
|
|
|
|
ctx.Error = dummieError |
|
|
|
|
ctx.Rule.ExecutionErrorState = models.ExecutionErrorKeepState |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStatePending) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
Convey("ok -> no_data(alerting)", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStateOK |
|
|
|
|
ctx.Rule.NoDataState = models.NoDataSetAlerting |
|
|
|
|
ctx.NoDataFound = true |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateAlerting) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
Convey("ok -> no_data(keep_last)", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStateOK |
|
|
|
|
ctx.Rule.NoDataState = models.NoDataKeepState |
|
|
|
|
ctx.NoDataFound = true |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStateOK) |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
Convey("pending -> no_data(keep_last)", func() { |
|
|
|
|
ctx.PrevAlertState = models.AlertStatePending |
|
|
|
|
ctx.Rule.NoDataState = models.NoDataKeepState |
|
|
|
|
ctx.NoDataFound = true |
|
|
|
|
|
|
|
|
|
ctx.Rule.State = ctx.GetNewState() |
|
|
|
|
So(ctx.Rule.State, ShouldEqual, models.AlertStatePending) |
|
|
|
|
}) |
|
|
|
|
}) |
|
|
|
|
tc.applyFn(ctx) |
|
|
|
|
have := ctx.GetNewState() |
|
|
|
|
if have != tc.expected { |
|
|
|
|
t.Errorf("failed: %s \n expected '%s' have '%s'\n", tc.name, tc.expected, string(have)) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|