From 24a74cd06e3a829102669e3342473fabf180a92e Mon Sep 17 00:00:00 2001 From: Serge Zaitsev Date: Tue, 26 Oct 2021 23:24:58 +0200 Subject: [PATCH] Chore: Refactor GoConvey tests in alerting/conditions (#40843) * refactor goconvery tests * use more meaningful assertion * use more meaningful assertions --- .../alerting/conditions/evaluator_test.go | 55 +- .../conditions/query_interval_test.go | 59 +- .../alerting/conditions/query_test.go | 276 ++++---- .../alerting/conditions/reducer_test.go | 631 +++++++++--------- 4 files changed, 508 insertions(+), 513 deletions(-) diff --git a/pkg/services/alerting/conditions/evaluator_test.go b/pkg/services/alerting/conditions/evaluator_test.go index ce3100b59e4..f7188f8a49c 100644 --- a/pkg/services/alerting/conditions/evaluator_test.go +++ b/pkg/services/alerting/conditions/evaluator_test.go @@ -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))) }) }) } diff --git a/pkg/services/alerting/conditions/query_interval_test.go b/pkg/services/alerting/conditions/query_interval_test.go index c3e6876d79e..33b06c8a497 100644 --- a/pkg/services/alerting/conditions/query_interval_test.go +++ b/pkg/services/alerting/conditions/query_interval_test.go @@ -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) }) } diff --git a/pkg/services/alerting/conditions/query_test.go b/pkg/services/alerting/conditions/query_test.go index 1021ba61fb0..7b88c6d0749 100644 --- a/pkg/services/alerting/conditions/query_test.go +++ b/pkg/services/alerting/conditions/query_test.go @@ -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 diff --git a/pkg/services/alerting/conditions/reducer_test.go b/pkg/services/alerting/conditions/reducer_test.go index baa46925785..f60c78d2db1 100644 --- a/pkg/services/alerting/conditions/reducer_test.go +++ b/pkg/services/alerting/conditions/reducer_test.go @@ -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) }) }