Chore: Refactor GoConvey tests in alerting/conditions (#40843)

* refactor goconvery tests

* use more meaningful assertion

* use more meaningful assertions
This commit is contained in:
Serge Zaitsev 2021-10-26 23:24:58 +02:00 committed by GitHub
parent bce1011361
commit 24a74cd06e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 508 additions and 513 deletions

View File

@ -3,60 +3,59 @@ package conditions
import (
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/components/simplejson"
"github.com/stretchr/testify/require"
)
func evaluatorScenario(json string, reducedValue float64, datapoints ...float64) bool {
func evaluatorScenario(t *testing.T, json string, reducedValue float64, datapoints ...float64) bool {
jsonModel, err := simplejson.NewJson([]byte(json))
So(err, ShouldBeNil)
require.NoError(t, err)
evaluator, err := NewAlertEvaluator(jsonModel)
So(err, ShouldBeNil)
require.NoError(t, err)
return evaluator.Eval(null.FloatFrom(reducedValue))
}
func TestEvaluators(t *testing.T) {
Convey("greater then", t, func() {
So(evaluatorScenario(`{"type": "gt", "params": [1] }`, 3), ShouldBeTrue)
So(evaluatorScenario(`{"type": "gt", "params": [3] }`, 1), ShouldBeFalse)
t.Run("greater then", func(t *testing.T) {
require.True(t, evaluatorScenario(t, `{"type": "gt", "params": [1] }`, 3))
require.False(t, evaluatorScenario(t, `{"type": "gt", "params": [3] }`, 1))
})
Convey("less then", t, func() {
So(evaluatorScenario(`{"type": "lt", "params": [1] }`, 3), ShouldBeFalse)
So(evaluatorScenario(`{"type": "lt", "params": [3] }`, 1), ShouldBeTrue)
t.Run("less then", func(t *testing.T) {
require.False(t, evaluatorScenario(t, `{"type": "lt", "params": [1] }`, 3))
require.True(t, evaluatorScenario(t, `{"type": "lt", "params": [3] }`, 1))
})
Convey("within_range", t, func() {
So(evaluatorScenario(`{"type": "within_range", "params": [1, 100] }`, 3), ShouldBeTrue)
So(evaluatorScenario(`{"type": "within_range", "params": [1, 100] }`, 300), ShouldBeFalse)
So(evaluatorScenario(`{"type": "within_range", "params": [100, 1] }`, 3), ShouldBeTrue)
So(evaluatorScenario(`{"type": "within_range", "params": [100, 1] }`, 300), ShouldBeFalse)
t.Run("within_range", func(t *testing.T) {
require.True(t, evaluatorScenario(t, `{"type": "within_range", "params": [1, 100] }`, 3))
require.False(t, evaluatorScenario(t, `{"type": "within_range", "params": [1, 100] }`, 300))
require.True(t, evaluatorScenario(t, `{"type": "within_range", "params": [100, 1] }`, 3))
require.False(t, evaluatorScenario(t, `{"type": "within_range", "params": [100, 1] }`, 300))
})
Convey("outside_range", t, func() {
So(evaluatorScenario(`{"type": "outside_range", "params": [1, 100] }`, 1000), ShouldBeTrue)
So(evaluatorScenario(`{"type": "outside_range", "params": [1, 100] }`, 50), ShouldBeFalse)
So(evaluatorScenario(`{"type": "outside_range", "params": [100, 1] }`, 1000), ShouldBeTrue)
So(evaluatorScenario(`{"type": "outside_range", "params": [100, 1] }`, 50), ShouldBeFalse)
t.Run("outside_range", func(t *testing.T) {
require.True(t, evaluatorScenario(t, `{"type": "outside_range", "params": [1, 100] }`, 1000))
require.False(t, evaluatorScenario(t, `{"type": "outside_range", "params": [1, 100] }`, 50))
require.True(t, evaluatorScenario(t, `{"type": "outside_range", "params": [100, 1] }`, 1000))
require.False(t, evaluatorScenario(t, `{"type": "outside_range", "params": [100, 1] }`, 50))
})
Convey("no_value", t, func() {
Convey("should be false if series have values", func() {
So(evaluatorScenario(`{"type": "no_value", "params": [] }`, 50), ShouldBeFalse)
t.Run("no_value", func(t *testing.T) {
t.Run("should be false if series have values", func(t *testing.T) {
require.False(t, evaluatorScenario(t, `{"type": "no_value", "params": [] }`, 50))
})
Convey("should be true when the series have no value", func() {
t.Run("should be true when the series have no value", func(t *testing.T) {
jsonModel, err := simplejson.NewJson([]byte(`{"type": "no_value", "params": [] }`))
So(err, ShouldBeNil)
require.NoError(t, err)
evaluator, err := NewAlertEvaluator(jsonModel)
So(err, ShouldBeNil)
require.NoError(t, err)
So(evaluator.Eval(null.FloatFromPtr(nil)), ShouldBeTrue)
require.True(t, evaluator.Eval(null.FloatFromPtr(nil)))
})
})
}

View File

@ -12,12 +12,13 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/require"
)
func TestQueryInterval(t *testing.T) {
Convey("When evaluating query condition, regarding the interval value", t, func() {
Convey("Can handle interval-calculation with no panel-min-interval and no datasource-min-interval", func() {
t.Run("When evaluating query condition, regarding the interval value", func(t *testing.T) {
t.Run("Can handle interval-calculation with no panel-min-interval and no datasource-min-interval", func(t *testing.T) {
// no panel-min-interval in the queryModel
queryModel := `{"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}`
@ -29,13 +30,13 @@ func TestQueryInterval(t *testing.T) {
verifier := func(query plugins.DataSubQuery) {
// 5minutes timerange = 300000milliseconds; default-resolution is 1500pixels,
// so we should have 300000/1500 = 200milliseconds here
So(query.IntervalMS, ShouldEqual, 200)
So(query.MaxDataPoints, ShouldEqual, interval.DefaultRes)
require.Equal(t, int64(200), query.IntervalMS)
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
}
applyScenario(timeRange, dataSourceJson, queryModel, verifier)
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
})
Convey("Can handle interval-calculation with panel-min-interval and no datasource-min-interval", func() {
t.Run("Can handle interval-calculation with panel-min-interval and no datasource-min-interval", func(t *testing.T) {
// panel-min-interval in the queryModel
queryModel := `{"interval":"123s", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}`
@ -45,13 +46,13 @@ func TestQueryInterval(t *testing.T) {
timeRange := "5m"
verifier := func(query plugins.DataSubQuery) {
So(query.IntervalMS, ShouldEqual, 123000)
So(query.MaxDataPoints, ShouldEqual, interval.DefaultRes)
require.Equal(t, int64(123000), query.IntervalMS)
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
}
applyScenario(timeRange, dataSourceJson, queryModel, verifier)
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
})
Convey("Can handle interval-calculation with no panel-min-interval and datasource-min-interval", func() {
t.Run("Can handle interval-calculation with no panel-min-interval and datasource-min-interval", func(t *testing.T) {
// no panel-min-interval in the queryModel
queryModel := `{"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}`
@ -59,18 +60,18 @@ func TestQueryInterval(t *testing.T) {
dataSourceJson, err := simplejson.NewJson([]byte(`{
"timeInterval": "71s"
}`))
So(err, ShouldBeNil)
require.Nil(t, err)
timeRange := "5m"
verifier := func(query plugins.DataSubQuery) {
So(query.IntervalMS, ShouldEqual, 71000)
So(query.MaxDataPoints, ShouldEqual, interval.DefaultRes)
require.Equal(t, int64(71000), query.IntervalMS)
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
}
applyScenario(timeRange, dataSourceJson, queryModel, verifier)
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
})
Convey("Can handle interval-calculation with both panel-min-interval and datasource-min-interval", func() {
t.Run("Can handle interval-calculation with both panel-min-interval and datasource-min-interval", func(t *testing.T) {
// panel-min-interval in the queryModel
queryModel := `{"interval":"19s", "target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}`
@ -78,21 +79,21 @@ func TestQueryInterval(t *testing.T) {
dataSourceJson, err := simplejson.NewJson([]byte(`{
"timeInterval": "71s"
}`))
So(err, ShouldBeNil)
require.Nil(t, err)
timeRange := "5m"
verifier := func(query plugins.DataSubQuery) {
// when both panel-min-interval and datasource-min-interval exists,
// panel-min-interval is used
So(query.IntervalMS, ShouldEqual, 19000)
So(query.MaxDataPoints, ShouldEqual, interval.DefaultRes)
require.Equal(t, int64(19000), query.IntervalMS)
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
}
applyScenario(timeRange, dataSourceJson, queryModel, verifier)
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
})
Convey("Can handle no min-interval, and very small time-ranges, where the default-min-interval=1ms applies", func() {
t.Run("Can handle no min-interval, and very small time-ranges, where the default-min-interval=1ms applies", func(t *testing.T) {
// no panel-min-interval in the queryModel
queryModel := `{"target": "aliasByNode(statsd.fakesite.counters.session_start.mobile.count, 4)"}`
@ -104,11 +105,11 @@ func TestQueryInterval(t *testing.T) {
verifier := func(query plugins.DataSubQuery) {
// no min-interval exists, the default-min-interval will be used,
// and for such a short time-range this will cause the value to be 1millisecond.
So(query.IntervalMS, ShouldEqual, 1)
So(query.MaxDataPoints, ShouldEqual, interval.DefaultRes)
require.Equal(t, int64(1), query.IntervalMS)
require.Equal(t, interval.DefaultRes, query.MaxDataPoints)
}
applyScenario(timeRange, dataSourceJson, queryModel, verifier)
applyScenario(t, timeRange, dataSourceJson, queryModel, verifier)
})
})
}
@ -135,8 +136,8 @@ func (rh fakeIntervalTestReqHandler) HandleRequest(ctx context.Context, dsInfo *
}
//nolint: staticcheck // plugins.DataResponse deprecated
func applyScenario(timeRange string, dataSourceJsonData *simplejson.Json, queryModel string, verifier func(query plugins.DataSubQuery)) {
Convey("desc", func() {
func applyScenario(t *testing.T, timeRange string, dataSourceJsonData *simplejson.Json, queryModel string, verifier func(query plugins.DataSubQuery)) {
t.Run("desc", func(t *testing.T) {
bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetDataSourceQuery) error {
query.Result = &models.DataSource{Id: 1, Type: "graphite", JsonData: dataSourceJsonData}
return nil
@ -159,10 +160,10 @@ func applyScenario(timeRange string, dataSourceJsonData *simplejson.Json, queryM
"reducer":{"type": "avg"},
"evaluator":{"type": "gt", "params": [100]}
}`))
So(err, ShouldBeNil)
require.Nil(t, err)
condition, err := newQueryCondition(jsonModel, 0)
So(err, ShouldBeNil)
require.Nil(t, err)
ctx.condition = condition
@ -179,6 +180,6 @@ func applyScenario(timeRange string, dataSourceJsonData *simplejson.Json, queryM
_, err = condition.Eval(ctx.result, reqHandler)
So(err, ShouldBeNil)
require.Nil(t, err)
})
}

View File

@ -17,7 +17,7 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/services/alerting"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/require"
"github.com/xorcare/pointer"
)
@ -33,147 +33,165 @@ func newTimeSeriesPointsFromArgs(values ...float64) plugins.DataTimeSeriesPoints
}
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]}`
setup := func() *queryConditionTestContext {
ctx := &queryConditionTestContext{}
bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetDataSourceQuery) error {
query.Result = &models.DataSource{Id: 1, Type: "graphite"}
return nil
})
Convey("Can read query condition from json model", func() {
_, err := ctx.exec()
So(err, ShouldBeNil)
ctx.reducer = `{"type":"avg"}`
ctx.evaluator = `{"type":"gt","params":[100]}`
ctx.result = &alerting.EvalContext{
Ctx: context.Background(),
Rule: &alerting.Rule{},
RequestValidator: &validations.OSSPluginRequestValidator{},
}
return ctx
}
So(ctx.condition.Query.From, ShouldEqual, "5m")
So(ctx.condition.Query.To, ShouldEqual, "now")
So(ctx.condition.Query.DatasourceID, ShouldEqual, 1)
t.Run("Can read query condition from json model", func(t *testing.T) {
ctx := setup()
_, err := ctx.exec(t)
require.Nil(t, err)
Convey("Can read query reducer", func() {
reducer := ctx.condition.Reducer
So(reducer.Type, ShouldEqual, "avg")
})
require.Equal(t, "5m", ctx.condition.Query.From)
require.Equal(t, "now", ctx.condition.Query.To)
require.Equal(t, int64(1), ctx.condition.Query.DatasourceID)
Convey("Can read evaluator", func() {
evaluator, ok := ctx.condition.Evaluator.(*thresholdEvaluator)
So(ok, ShouldBeTrue)
So(evaluator.Type, ShouldEqual, "gt")
})
})
t.Run("Can read query reducer", func(t *testing.T) {
reducer := ctx.condition.Reducer
require.Equal(t, "avg", reducer.Type)
})
Convey("should fire when avg is above 100", func() {
points := newTimeSeriesPointsFromArgs(120, 0)
ctx.series = plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "test1", Points: points}}
cr, err := ctx.exec()
t.Run("Can read evaluator", func(t *testing.T) {
evaluator, ok := ctx.condition.Evaluator.(*thresholdEvaluator)
require.True(t, ok)
require.Equal(t, "gt", evaluator.Type)
})
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeTrue)
})
t.Run("should fire when avg is above 100", func(t *testing.T) {
ctx := setup()
points := newTimeSeriesPointsFromArgs(120, 0)
ctx.series = plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "test1", Points: points}}
cr, err := ctx.exec(t)
Convey("should fire when avg is above 100 on dataframe", func() {
ctx.frame = data.NewFrame("",
data.NewField("time", nil, []time.Time{time.Now(), time.Now()}),
data.NewField("val", nil, []int64{120, 150}),
)
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.Firing)
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeTrue)
})
t.Run("should fire when avg is above 100 on dataframe", func(t *testing.T) {
ctx := setup()
ctx.frame = data.NewFrame("",
data.NewField("time", nil, []time.Time{time.Now(), time.Now()}),
data.NewField("val", nil, []int64{120, 150}),
)
cr, err := ctx.exec(t)
Convey("Should not fire when avg is below 100", func() {
points := newTimeSeriesPointsFromArgs(90, 0)
ctx.series = plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "test1", Points: points}}
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.Firing)
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeFalse)
})
t.Run("Should not fire when avg is below 100", func(t *testing.T) {
ctx := setup()
points := newTimeSeriesPointsFromArgs(90, 0)
ctx.series = plugins.DataTimeSeriesSlice{plugins.DataTimeSeries{Name: "test1", Points: points}}
cr, err := ctx.exec(t)
Convey("Should not fire when avg is below 100 on dataframe", func() {
ctx.frame = data.NewFrame("",
data.NewField("time", nil, []time.Time{time.Now(), time.Now()}),
data.NewField("val", nil, []int64{12, 47}),
)
cr, err := ctx.exec()
require.Nil(t, err)
require.False(t, cr.Firing)
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeFalse)
})
t.Run("Should not fire when avg is below 100 on dataframe", func(t *testing.T) {
ctx := setup()
ctx.frame = data.NewFrame("",
data.NewField("time", nil, []time.Time{time.Now(), time.Now()}),
data.NewField("val", nil, []int64{12, 47}),
)
cr, err := ctx.exec(t)
Convey("Should fire if only first series matches", func() {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs(120, 0)},
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(0, 0)},
}
cr, err := ctx.exec()
require.Nil(t, err)
require.False(t, cr.Firing)
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeTrue)
})
t.Run("Should fire if only first series matches", func(t *testing.T) {
ctx := setup()
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs(120, 0)},
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(0, 0)},
}
cr, err := ctx.exec(t)
Convey("No series", func() {
Convey("Should set NoDataFound when condition is gt", func() {
ctx.series = plugins.DataTimeSeriesSlice{}
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.Firing)
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeFalse)
So(cr.NoDataFound, ShouldBeTrue)
})
t.Run("No series", func(t *testing.T) {
ctx := setup()
t.Run("Should set NoDataFound when condition is gt", func(t *testing.T) {
ctx.series = plugins.DataTimeSeriesSlice{}
cr, err := ctx.exec(t)
Convey("Should be firing when condition is no_value", func() {
ctx.evaluator = `{"type": "no_value", "params": []}`
ctx.series = plugins.DataTimeSeriesSlice{}
cr, err := ctx.exec()
require.Nil(t, err)
require.False(t, cr.Firing)
require.True(t, cr.NoDataFound)
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeTrue)
})
})
t.Run("Should be firing when condition is no_value", func(t *testing.T) {
ctx.evaluator = `{"type": "no_value", "params": []}`
ctx.series = plugins.DataTimeSeriesSlice{}
cr, err := ctx.exec(t)
Convey("Empty series", func() {
Convey("Should set Firing if eval match", func() {
ctx.evaluator = `{"type": "no_value", "params": []}`
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
}
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.Firing)
})
})
So(err, ShouldBeNil)
So(cr.Firing, ShouldBeTrue)
})
t.Run("Empty series", func(t *testing.T) {
ctx := setup()
t.Run("Should set Firing if eval match", func(t *testing.T) {
ctx.evaluator = `{"type": "no_value", "params": []}`
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
}
cr, err := ctx.exec(t)
Convey("Should set NoDataFound both series are empty", func() {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs()},
}
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.Firing)
})
So(err, ShouldBeNil)
So(cr.NoDataFound, ShouldBeTrue)
})
t.Run("Should set NoDataFound both series are empty", func(t *testing.T) {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs()},
}
cr, err := ctx.exec(t)
Convey("Should set NoDataFound both series contains null", func() {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: plugins.DataTimeSeriesPoints{plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
plugins.DataTimeSeries{Name: "test2", Points: plugins.DataTimeSeriesPoints{plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
}
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.NoDataFound)
})
So(err, ShouldBeNil)
So(cr.NoDataFound, ShouldBeTrue)
})
t.Run("Should set NoDataFound both series contains null", func(t *testing.T) {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: plugins.DataTimeSeriesPoints{plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
plugins.DataTimeSeries{Name: "test2", Points: plugins.DataTimeSeriesPoints{plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(0)}}},
}
cr, err := ctx.exec(t)
Convey("Should not set NoDataFound if one series is empty", func() {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(120, 0)},
}
cr, err := ctx.exec()
require.Nil(t, err)
require.True(t, cr.NoDataFound)
})
So(err, ShouldBeNil)
So(cr.NoDataFound, ShouldBeFalse)
})
})
t.Run("Should not set NoDataFound if one series is empty", func(t *testing.T) {
ctx.series = plugins.DataTimeSeriesSlice{
plugins.DataTimeSeries{Name: "test1", Points: newTimeSeriesPointsFromArgs()},
plugins.DataTimeSeries{Name: "test2", Points: newTimeSeriesPointsFromArgs(120, 0)},
}
cr, err := ctx.exec(t)
require.Nil(t, err)
require.False(t, cr.NoDataFound)
})
})
}
@ -187,10 +205,8 @@ type queryConditionTestContext struct {
condition *QueryCondition
}
type queryConditionScenarioFunc func(c *queryConditionTestContext)
//nolint: staticcheck // plugins.DataPlugin deprecated
func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error) {
func (ctx *queryConditionTestContext) exec(t *testing.T) (*alerting.ConditionResult, error) {
jsonModel, err := simplejson.NewJson([]byte(`{
"type": "query",
"query": {
@ -201,10 +217,10 @@ func (ctx *queryConditionTestContext) exec() (*alerting.ConditionResult, error)
"reducer":` + ctx.reducer + `,
"evaluator":` + ctx.evaluator + `
}`))
So(err, ShouldBeNil)
require.Nil(t, err)
condition, err := newQueryCondition(jsonModel, 0)
So(err, ShouldBeNil)
require.Nil(t, err)
ctx.condition = condition
@ -239,24 +255,6 @@ func (rh fakeReqHandler) HandleRequest(context.Context, *models.DataSource, plug
return rh.response, nil
}
func queryConditionScenario(desc string, fn queryConditionScenarioFunc) {
Convey(desc, func() {
bus.AddHandlerCtx("test", func(ctx context.Context, query *models.GetDataSourceQuery) error {
query.Result = &models.DataSource{Id: 1, Type: "graphite"}
return nil
})
ctx := &queryConditionTestContext{}
ctx.result = &alerting.EvalContext{
Ctx: context.Background(),
Rule: &alerting.Rule{},
RequestValidator: &validations.OSSPluginRequestValidator{},
}
fn(ctx)
})
}
func TestFrameToSeriesSlice(t *testing.T) {
tests := []struct {
name string

View File

@ -4,183 +4,103 @@ import (
"math"
"testing"
. "github.com/smartystreets/goconvey/convey"
"github.com/grafana/grafana/pkg/components/null"
"github.com/grafana/grafana/pkg/plugins"
"github.com/stretchr/testify/require"
)
func TestSimpleReducer(t *testing.T) {
Convey("Test simple reducer by calculating", t, func() {
Convey("sum", func() {
result := testReducer("sum", 1, 2, 3)
So(result, ShouldEqual, float64(6))
})
t.Run("sum", func(t *testing.T) {
result := testReducer("sum", 1, 2, 3)
require.Equal(t, float64(6), result)
})
Convey("min", func() {
result := testReducer("min", 3, 2, 1)
So(result, ShouldEqual, float64(1))
})
t.Run("min", func(t *testing.T) {
result := testReducer("min", 3, 2, 1)
require.Equal(t, float64(1), result)
})
Convey("max", func() {
result := testReducer("max", 1, 2, 3)
So(result, ShouldEqual, float64(3))
})
t.Run("max", func(t *testing.T) {
result := testReducer("max", 1, 2, 3)
require.Equal(t, float64(3), result)
})
Convey("count", func() {
result := testReducer("count", 1, 2, 3000)
So(result, ShouldEqual, float64(3))
})
t.Run("count", func(t *testing.T) {
result := testReducer("count", 1, 2, 3000)
require.Equal(t, float64(3), result)
})
Convey("last", func() {
result := testReducer("last", 1, 2, 3000)
So(result, ShouldEqual, float64(3000))
})
t.Run("last", func(t *testing.T) {
result := testReducer("last", 1, 2, 3000)
require.Equal(t, float64(3000), result)
})
Convey("median odd amount of numbers", func() {
result := testReducer("median", 1, 2, 3000)
So(result, ShouldEqual, float64(2))
})
t.Run("median odd amount of numbers", func(t *testing.T) {
result := testReducer("median", 1, 2, 3000)
require.Equal(t, float64(2), result)
})
Convey("median even amount of numbers", func() {
result := testReducer("median", 1, 2, 4, 3000)
So(result, ShouldEqual, float64(3))
})
t.Run("median even amount of numbers", func(t *testing.T) {
result := testReducer("median", 1, 2, 4, 3000)
require.Equal(t, float64(3), result)
})
Convey("median with one values", func() {
result := testReducer("median", 1)
So(result, ShouldEqual, float64(1))
})
t.Run("median with one values", func(t *testing.T) {
result := testReducer("median", 1)
require.Equal(t, float64(1), result)
})
Convey("median should ignore null values", func() {
reducer := newSimpleReducer("median")
t.Run("median should ignore null values", func(t *testing.T) {
reducer := newSimpleReducer("median")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(1)), null.FloatFrom(4)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(2)), null.FloatFrom(5)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(3)), null.FloatFrom(6)})
result := reducer.Reduce(series)
require.Equal(t, true, result.Valid)
require.Equal(t, float64(2), result.Float64)
})
t.Run("avg", func(t *testing.T) {
result := testReducer("avg", 1, 2, 3)
require.Equal(t, float64(2), result)
})
t.Run("avg with only nulls", func(t *testing.T) {
reducer := newSimpleReducer("avg")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
require.Equal(t, false, reducer.Reduce(series).Valid)
})
t.Run("count_non_null", func(t *testing.T) {
t.Run("with null values and real values", func(t *testing.T) {
reducer := newSimpleReducer("count_non_null")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(1)), null.FloatFrom(4)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(2)), null.FloatFrom(5)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(float64(3)), null.FloatFrom(6)})
result := reducer.Reduce(series)
So(result.Valid, ShouldEqual, true)
So(result.Float64, ShouldEqual, float64(2))
})
Convey("avg", func() {
result := testReducer("avg", 1, 2, 3)
So(result, ShouldEqual, float64(2))
})
Convey("avg with only nulls", func() {
reducer := newSimpleReducer("avg")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
So(reducer.Reduce(series).Valid, ShouldEqual, false)
})
Convey("count_non_null", func() {
Convey("with null values and real values", func() {
reducer := newSimpleReducer("count_non_null")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(3)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
So(reducer.Reduce(series).Valid, ShouldEqual, true)
So(reducer.Reduce(series).Float64, ShouldEqual, 2)
})
Convey("with null values", func() {
reducer := newSimpleReducer("count_non_null")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
So(reducer.Reduce(series).Valid, ShouldEqual, false)
})
})
Convey("avg of number values and null values should ignore nulls", func() {
reducer := newSimpleReducer("avg")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(3)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
So(reducer.Reduce(series).Float64, ShouldEqual, float64(3))
require.Equal(t, true, reducer.Reduce(series).Valid)
require.Equal(t, 2.0, reducer.Reduce(series).Float64)
})
// diff function Test Suite
Convey("diff of one positive point", func() {
result := testReducer("diff", 30)
So(result, ShouldEqual, float64(0))
})
Convey("diff of one negative point", func() {
result := testReducer("diff", -30)
So(result, ShouldEqual, float64(0))
})
Convey("diff of two positive points[1]", func() {
result := testReducer("diff", 30, 40)
So(result, ShouldEqual, float64(10))
})
Convey("diff of two positive points[2]", func() {
result := testReducer("diff", 30, 20)
So(result, ShouldEqual, float64(-10))
})
Convey("diff of two negative points[1]", func() {
result := testReducer("diff", -30, -40)
So(result, ShouldEqual, float64(-10))
})
Convey("diff of two negative points[2]", func() {
result := testReducer("diff", -30, -10)
So(result, ShouldEqual, float64(20))
})
Convey("diff of one positive and one negative point", func() {
result := testReducer("diff", 30, -40)
So(result, ShouldEqual, float64(-70))
})
Convey("diff of one negative and one positive point", func() {
result := testReducer("diff", -30, 40)
So(result, ShouldEqual, float64(70))
})
Convey("diff of three positive points", func() {
result := testReducer("diff", 30, 40, 50)
So(result, ShouldEqual, float64(20))
})
Convey("diff of three negative points", func() {
result := testReducer("diff", -30, -40, -50)
So(result, ShouldEqual, float64(-20))
})
Convey("diff with only nulls", func() {
reducer := newSimpleReducer("diff")
t.Run("with null values", func(t *testing.T) {
reducer := newSimpleReducer("count_non_null")
series := plugins.DataTimeSeries{
Name: "test time series",
}
@ -188,212 +108,289 @@ func TestSimpleReducer(t *testing.T) {
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
So(reducer.Reduce(series).Valid, ShouldEqual, false)
require.Equal(t, false, reducer.Reduce(series).Valid)
})
})
// diff_abs function Test Suite
Convey("diff_abs of one positive point", func() {
result := testReducer("diff_abs", 30)
So(result, ShouldEqual, float64(0))
})
t.Run("avg of number values and null values should ignore nulls", func(t *testing.T) {
reducer := newSimpleReducer("avg")
series := plugins.DataTimeSeries{
Name: "test time series",
}
Convey("diff_abs of one negative point", func() {
result := testReducer("diff_abs", -30)
So(result, ShouldEqual, float64(0))
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(3)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFrom(3), null.FloatFrom(4)})
Convey("diff_abs of two positive points[1]", func() {
result := testReducer("diff_abs", 30, 40)
So(result, ShouldEqual, float64(10))
})
require.Equal(t, float64(3), reducer.Reduce(series).Float64)
})
Convey("diff_abs of two positive points[2]", func() {
result := testReducer("diff_abs", 30, 20)
So(result, ShouldEqual, float64(10))
})
// diff function Test Suite
t.Run("diff of one positive point", func(t *testing.T) {
result := testReducer("diff", 30)
require.Equal(t, float64(0), result)
})
Convey("diff_abs of two negative points[1]", func() {
result := testReducer("diff_abs", -30, -40)
So(result, ShouldEqual, float64(10))
})
t.Run("diff of one negative point", func(t *testing.T) {
result := testReducer("diff", -30)
require.Equal(t, float64(0), result)
})
Convey("diff_abs of two negative points[2]", func() {
result := testReducer("diff_abs", -30, -10)
So(result, ShouldEqual, float64(20))
})
t.Run("diff of two positive points[1]", func(t *testing.T) {
result := testReducer("diff", 30, 40)
require.Equal(t, float64(10), result)
})
Convey("diff_abs of one positive and one negative point", func() {
result := testReducer("diff_abs", 30, -40)
So(result, ShouldEqual, float64(70))
})
t.Run("diff of two positive points[2]", func(t *testing.T) {
result := testReducer("diff", 30, 20)
require.Equal(t, float64(-10), result)
})
Convey("diff_abs of one negative and one positive point", func() {
result := testReducer("diff_abs", -30, 40)
So(result, ShouldEqual, float64(70))
})
t.Run("diff of two negative points[1]", func(t *testing.T) {
result := testReducer("diff", -30, -40)
require.Equal(t, float64(-10), result)
})
Convey("diff_abs of three positive points", func() {
result := testReducer("diff_abs", 30, 40, 50)
So(result, ShouldEqual, float64(20))
})
t.Run("diff of two negative points[2]", func(t *testing.T) {
result := testReducer("diff", -30, -10)
require.Equal(t, float64(20), result)
})
Convey("diff_abs of three negative points", func() {
result := testReducer("diff_abs", -30, -40, -50)
So(result, ShouldEqual, float64(20))
})
t.Run("diff of one positive and one negative point", func(t *testing.T) {
result := testReducer("diff", 30, -40)
require.Equal(t, float64(-70), result)
})
Convey("diff_abs with only nulls", func() {
reducer := newSimpleReducer("diff_abs")
series := plugins.DataTimeSeries{
Name: "test time series",
}
t.Run("diff of one negative and one positive point", func(t *testing.T) {
result := testReducer("diff", -30, 40)
require.Equal(t, float64(70), result)
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
t.Run("diff of three positive points", func(t *testing.T) {
result := testReducer("diff", 30, 40, 50)
require.Equal(t, float64(20), result)
})
So(reducer.Reduce(series).Valid, ShouldEqual, false)
})
t.Run("diff of three negative points", func(t *testing.T) {
result := testReducer("diff", -30, -40, -50)
require.Equal(t, float64(-20), result)
})
// percent_diff function Test Suite
Convey("percent_diff of one positive point", func() {
result := testReducer("percent_diff", 30)
So(result, ShouldEqual, float64(0))
})
t.Run("diff with only nulls", func(t *testing.T) {
reducer := newSimpleReducer("diff")
series := plugins.DataTimeSeries{
Name: "test time series",
}
Convey("percent_diff of one negative point", func() {
result := testReducer("percent_diff", -30)
So(result, ShouldEqual, float64(0))
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
Convey("percent_diff of two positive points[1]", func() {
result := testReducer("percent_diff", 30, 40)
So(result, ShouldEqual, float64(33.33333333333333))
})
require.Equal(t, false, reducer.Reduce(series).Valid)
})
Convey("percent_diff of two positive points[2]", func() {
result := testReducer("percent_diff", 30, 20)
So(result, ShouldEqual, float64(-33.33333333333333))
})
// diff_abs function Test Suite
t.Run("diff_abs of one positive point", func(t *testing.T) {
result := testReducer("diff_abs", 30)
require.Equal(t, float64(0), result)
})
Convey("percent_diff of two negative points[1]", func() {
result := testReducer("percent_diff", -30, -40)
So(result, ShouldEqual, float64(-33.33333333333333))
})
t.Run("diff_abs of one negative point", func(t *testing.T) {
result := testReducer("diff_abs", -30)
require.Equal(t, float64(0), result)
})
Convey("percent_diff of two negative points[2]", func() {
result := testReducer("percent_diff", -30, -10)
So(result, ShouldEqual, float64(66.66666666666666))
})
t.Run("diff_abs of two positive points[1]", func(t *testing.T) {
result := testReducer("diff_abs", 30, 40)
require.Equal(t, float64(10), result)
})
Convey("percent_diff of one positive and one negative point", func() {
result := testReducer("percent_diff", 30, -40)
So(result, ShouldEqual, float64(-233.33333333333334))
})
t.Run("diff_abs of two positive points[2]", func(t *testing.T) {
result := testReducer("diff_abs", 30, 20)
require.Equal(t, float64(10), result)
})
Convey("percent_diff of one negative and one positive point", func() {
result := testReducer("percent_diff", -30, 40)
So(result, ShouldEqual, float64(233.33333333333334))
})
t.Run("diff_abs of two negative points[1]", func(t *testing.T) {
result := testReducer("diff_abs", -30, -40)
require.Equal(t, float64(10), result)
})
Convey("percent_diff of three positive points", func() {
result := testReducer("percent_diff", 30, 40, 50)
So(result, ShouldEqual, float64(66.66666666666666))
})
t.Run("diff_abs of two negative points[2]", func(t *testing.T) {
result := testReducer("diff_abs", -30, -10)
require.Equal(t, float64(20), result)
})
Convey("percent_diff of three negative points", func() {
result := testReducer("percent_diff", -30, -40, -50)
So(result, ShouldEqual, float64(-66.66666666666666))
})
t.Run("diff_abs of one positive and one negative point", func(t *testing.T) {
result := testReducer("diff_abs", 30, -40)
require.Equal(t, float64(70), result)
})
Convey("percent_diff with only nulls", func() {
reducer := newSimpleReducer("percent_diff")
series := plugins.DataTimeSeries{
Name: "test time series",
}
t.Run("diff_abs of one negative and one positive point", func(t *testing.T) {
result := testReducer("diff_abs", -30, 40)
require.Equal(t, float64(70), result)
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
t.Run("diff_abs of three positive points", func(t *testing.T) {
result := testReducer("diff_abs", 30, 40, 50)
require.Equal(t, float64(20), result)
})
So(reducer.Reduce(series).Valid, ShouldEqual, false)
})
t.Run("diff_abs of three negative points", func(t *testing.T) {
result := testReducer("diff_abs", -30, -40, -50)
require.Equal(t, float64(20), result)
})
// percent_diff_abs function Test Suite
Convey("percent_diff_abs_abs of one positive point", func() {
result := testReducer("percent_diff_abs", 30)
So(result, ShouldEqual, float64(0))
})
t.Run("diff_abs with only nulls", func(t *testing.T) {
reducer := newSimpleReducer("diff_abs")
series := plugins.DataTimeSeries{
Name: "test time series",
}
Convey("percent_diff_abs of one negative point", func() {
result := testReducer("percent_diff_abs", -30)
So(result, ShouldEqual, float64(0))
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
Convey("percent_diff_abs of two positive points[1]", func() {
result := testReducer("percent_diff_abs", 30, 40)
So(result, ShouldEqual, float64(33.33333333333333))
})
require.Equal(t, false, reducer.Reduce(series).Valid)
})
Convey("percent_diff_abs of two positive points[2]", func() {
result := testReducer("percent_diff_abs", 30, 20)
So(result, ShouldEqual, float64(33.33333333333333))
})
// percent_diff function Test Suite
t.Run("percent_diff of one positive point", func(t *testing.T) {
result := testReducer("percent_diff", 30)
require.Equal(t, float64(0), result)
})
Convey("percent_diff_abs of two negative points[1]", func() {
result := testReducer("percent_diff_abs", -30, -40)
So(result, ShouldEqual, float64(33.33333333333333))
})
t.Run("percent_diff of one negative point", func(t *testing.T) {
result := testReducer("percent_diff", -30)
require.Equal(t, float64(0), result)
})
Convey("percent_diff_abs of two negative points[2]", func() {
result := testReducer("percent_diff_abs", -30, -10)
So(result, ShouldEqual, float64(66.66666666666666))
})
t.Run("percent_diff of two positive points[1]", func(t *testing.T) {
result := testReducer("percent_diff", 30, 40)
require.Equal(t, float64(33.33333333333333), result)
})
Convey("percent_diff_abs of one positive and one negative point", func() {
result := testReducer("percent_diff_abs", 30, -40)
So(result, ShouldEqual, float64(233.33333333333334))
})
t.Run("percent_diff of two positive points[2]", func(t *testing.T) {
result := testReducer("percent_diff", 30, 20)
require.Equal(t, float64(-33.33333333333333), result)
})
Convey("percent_diff_abs of one negative and one positive point", func() {
result := testReducer("percent_diff_abs", -30, 40)
So(result, ShouldEqual, float64(233.33333333333334))
})
t.Run("percent_diff of two negative points[1]", func(t *testing.T) {
result := testReducer("percent_diff", -30, -40)
require.Equal(t, float64(-33.33333333333333), result)
})
Convey("percent_diff_abs of three positive points", func() {
result := testReducer("percent_diff_abs", 30, 40, 50)
So(result, ShouldEqual, float64(66.66666666666666))
})
t.Run("percent_diff of two negative points[2]", func(t *testing.T) {
result := testReducer("percent_diff", -30, -10)
require.Equal(t, float64(66.66666666666666), result)
})
Convey("percent_diff_abs of three negative points", func() {
result := testReducer("percent_diff_abs", -30, -40, -50)
So(result, ShouldEqual, float64(66.66666666666666))
})
t.Run("percent_diff of one positive and one negative point", func(t *testing.T) {
result := testReducer("percent_diff", 30, -40)
require.Equal(t, float64(-233.33333333333334), result)
})
Convey("percent_diff_abs with only nulls", func() {
reducer := newSimpleReducer("percent_diff_abs")
series := plugins.DataTimeSeries{
Name: "test time series",
}
t.Run("percent_diff of one negative and one positive point", func(t *testing.T) {
result := testReducer("percent_diff", -30, 40)
require.Equal(t, float64(233.33333333333334), result)
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
t.Run("percent_diff of three positive points", func(t *testing.T) {
result := testReducer("percent_diff", 30, 40, 50)
require.Equal(t, float64(66.66666666666666), result)
})
So(reducer.Reduce(series).Valid, ShouldEqual, false)
})
t.Run("percent_diff of three negative points", func(t *testing.T) {
result := testReducer("percent_diff", -30, -40, -50)
require.Equal(t, float64(-66.66666666666666), result)
})
Convey("min should work with NaNs", func() {
result := testReducer("min", math.NaN(), math.NaN(), math.NaN())
So(result, ShouldEqual, float64(0))
})
t.Run("percent_diff with only nulls", func(t *testing.T) {
reducer := newSimpleReducer("percent_diff")
series := plugins.DataTimeSeries{
Name: "test time series",
}
Convey("isValid should treat NaN as invalid", func() {
result := isValid(null.FloatFrom(math.NaN()))
So(result, ShouldBeFalse)
})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
Convey("isValid should treat invalid null.Float as invalid", func() {
result := isValid(null.FloatFromPtr(nil))
So(result, ShouldBeFalse)
})
require.Equal(t, false, reducer.Reduce(series).Valid)
})
// percent_diff_abs function Test Suite
t.Run("percent_diff_abs_abs of one positive point", func(t *testing.T) {
result := testReducer("percent_diff_abs", 30)
require.Equal(t, float64(0), result)
})
t.Run("percent_diff_abs of one negative point", func(t *testing.T) {
result := testReducer("percent_diff_abs", -30)
require.Equal(t, float64(0), result)
})
t.Run("percent_diff_abs of two positive points[1]", func(t *testing.T) {
result := testReducer("percent_diff_abs", 30, 40)
require.Equal(t, float64(33.33333333333333), result)
})
t.Run("percent_diff_abs of two positive points[2]", func(t *testing.T) {
result := testReducer("percent_diff_abs", 30, 20)
require.Equal(t, float64(33.33333333333333), result)
})
t.Run("percent_diff_abs of two negative points[1]", func(t *testing.T) {
result := testReducer("percent_diff_abs", -30, -40)
require.Equal(t, float64(33.33333333333333), result)
})
t.Run("percent_diff_abs of two negative points[2]", func(t *testing.T) {
result := testReducer("percent_diff_abs", -30, -10)
require.Equal(t, float64(66.66666666666666), result)
})
t.Run("percent_diff_abs of one positive and one negative point", func(t *testing.T) {
result := testReducer("percent_diff_abs", 30, -40)
require.Equal(t, float64(233.33333333333334), result)
})
t.Run("percent_diff_abs of one negative and one positive point", func(t *testing.T) {
result := testReducer("percent_diff_abs", -30, 40)
require.Equal(t, float64(233.33333333333334), result)
})
t.Run("percent_diff_abs of three positive points", func(t *testing.T) {
result := testReducer("percent_diff_abs", 30, 40, 50)
require.Equal(t, float64(66.66666666666666), result)
})
t.Run("percent_diff_abs of three negative points", func(t *testing.T) {
result := testReducer("percent_diff_abs", -30, -40, -50)
require.Equal(t, float64(66.66666666666666), result)
})
t.Run("percent_diff_abs with only nulls", func(t *testing.T) {
reducer := newSimpleReducer("percent_diff_abs")
series := plugins.DataTimeSeries{
Name: "test time series",
}
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(1)})
series.Points = append(series.Points, plugins.DataTimePoint{null.FloatFromPtr(nil), null.FloatFrom(2)})
require.Equal(t, false, reducer.Reduce(series).Valid)
})
t.Run("min should work with NaNs", func(t *testing.T) {
result := testReducer("min", math.NaN(), math.NaN(), math.NaN())
require.Equal(t, float64(0), result)
})
t.Run("isValid should treat NaN as invalid", func(t *testing.T) {
result := isValid(null.FloatFrom(math.NaN()))
require.False(t, result)
})
t.Run("isValid should treat invalid null.Float as invalid", func(t *testing.T) {
result := isValid(null.FloatFromPtr(nil))
require.False(t, result)
})
}