| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197 |
- package conditions
- import (
- "context"
- "testing"
- "github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/components/null"
- "github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/services/alerting"
- "github.com/grafana/grafana/pkg/tsdb"
- . "github.com/smartystreets/goconvey/convey"
- )
- func TestQueryCondition(t *testing.T) {
- Convey("when evaluating query condition", t, func() {
- queryConditionScenario("Given avg() and > 100", func(ctx *queryConditionTestContext) {
- ctx.reducer = `{"type": "avg"}`
- ctx.evaluator = `{"type": "gt", "params": [100]}`
- Convey("Can read query condition from json model", func() {
- ctx.exec()
- So(ctx.condition.Query.From, ShouldEqual, "5m")
- So(ctx.condition.Query.To, ShouldEqual, "now")
- So(ctx.condition.Query.DatasourceId, ShouldEqual, 1)
- Convey("Can read query reducer", func() {
- reducer, ok := ctx.condition.Reducer.(*SimpleReducer)
- So(ok, ShouldBeTrue)
- So(reducer.Type, ShouldEqual, "avg")
- })
- Convey("Can read evaluator", func() {
- evaluator, ok := ctx.condition.Evaluator.(*ThresholdEvaluator)
- So(ok, ShouldBeTrue)
- So(evaluator.Type, ShouldEqual, "gt")
- })
- })
- Convey("should fire when avg is above 100", func() {
- points := tsdb.NewTimeSeriesPointsFromArgs(120, 0)
- ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)}
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.Firing, ShouldBeTrue)
- })
- Convey("Should not fire when avg is below 100", func() {
- points := tsdb.NewTimeSeriesPointsFromArgs(90, 0)
- ctx.series = tsdb.TimeSeriesSlice{tsdb.NewTimeSeries("test1", points)}
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.Firing, ShouldBeFalse)
- })
- Convey("Should fire if only first serie matches", func() {
- ctx.series = tsdb.TimeSeriesSlice{
- tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs(120, 0)),
- tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs(0, 0)),
- }
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.Firing, ShouldBeTrue)
- })
- Convey("No series", func() {
- Convey("Should set NoDataFound when condition is gt", func() {
- ctx.series = tsdb.TimeSeriesSlice{}
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.Firing, ShouldBeFalse)
- So(cr.NoDataFound, ShouldBeTrue)
- })
- Convey("Should be firing when condition is no_value", func() {
- ctx.evaluator = `{"type": "no_value", "params": []}`
- ctx.series = tsdb.TimeSeriesSlice{}
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.Firing, ShouldBeTrue)
- })
- })
- Convey("Empty series", func() {
- Convey("Should set Firing if eval match", func() {
- ctx.evaluator = `{"type": "no_value", "params": []}`
- ctx.series = tsdb.TimeSeriesSlice{
- tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()),
- }
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.Firing, ShouldBeTrue)
- })
- Convey("Should set NoDataFound both series are empty", func() {
- ctx.series = tsdb.TimeSeriesSlice{
- tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()),
- tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs()),
- }
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.NoDataFound, ShouldBeTrue)
- })
- Convey("Should set NoDataFound both series contains null", func() {
- ctx.series = tsdb.TimeSeriesSlice{
- tsdb.NewTimeSeries("test1", tsdb.TimeSeriesPoints{tsdb.TimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}),
- tsdb.NewTimeSeries("test2", tsdb.TimeSeriesPoints{tsdb.TimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}),
- }
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.NoDataFound, ShouldBeTrue)
- })
- Convey("Should not set NoDataFound if one serie is empty", func() {
- ctx.series = tsdb.TimeSeriesSlice{
- tsdb.NewTimeSeries("test1", tsdb.NewTimeSeriesPointsFromArgs()),
- tsdb.NewTimeSeries("test2", tsdb.NewTimeSeriesPointsFromArgs(120, 0)),
- }
- cr, err := ctx.exec()
- So(err, ShouldBeNil)
- So(cr.NoDataFound, ShouldBeFalse)
- })
- })
- })
- })
- }
- type queryConditionTestContext struct {
- reducer string
- evaluator string
- series tsdb.TimeSeriesSlice
- result *alerting.EvalContext
- condition *QueryCondition
- }
- type queryConditionScenarioFunc func(c *queryConditionTestContext)
- func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error) {
- jsonModel, err := simplejson.NewJson([]byte(`{
- "type": "query",
- "query": {
- "params": ["A", "5m", "now"],
- "datasourceId": 1,
- "model": {"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}
- },
- "reducer":` + ctx.reducer + `,
- "evaluator":` + ctx.evaluator + `
- }`))
- So(err, ShouldBeNil)
- condition, err := NewQueryCondition(jsonModel, 0)
- So(err, ShouldBeNil)
- ctx.condition = condition
- condition.HandleRequest = func(context context.Context, req *tsdb.Request) (*tsdb.Response, error) {
- return &tsdb.Response{
- Results: map[string]*tsdb.QueryResult{
- "A": {Series: ctx.series},
- },
- }, nil
- }
- return condition.Eval(ctx.result)
- }
- func queryConditionScenario(desc string, fn queryConditionScenarioFunc) {
- Convey(desc, func() {
- bus.AddHandler("test", func(query *m.GetDataSourceByIdQuery) error {
- query.Result = &m.DataSource{Id: 1, Type: "graphite"}
- return nil
- })
- ctx := &queryConditionTestContext{}
- ctx.result = &alerting.EvalContext{
- Rule: &alerting.Rule{},
- }
- fn(ctx)
- })
- }
|