mirror of
https://github.com/grafana/grafana.git
synced 2024-11-29 20:24:18 -06:00
2d20c8db7b
* make TimeRange interface and add relative range * make Execute methods support the current time * update resample to support relative time range * update DSNode to support relative time range * update query service to create queries with absolute time * make alerting evaluator create relative time ranges
173 lines
4.7 KiB
Go
173 lines
4.7 KiB
Go
package expr
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"math/rand"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
ptr "github.com/xorcare/pointer"
|
|
|
|
"github.com/grafana/grafana/pkg/expr/mathexp"
|
|
"github.com/grafana/grafana/pkg/util"
|
|
)
|
|
|
|
func Test_UnmarshalReduceCommand_Settings(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
querySettings string
|
|
isError bool
|
|
expectedMapper mathexp.ReduceMapper
|
|
}{
|
|
{
|
|
name: "no mapper function when settings is not specified",
|
|
querySettings: ``,
|
|
expectedMapper: nil,
|
|
},
|
|
{
|
|
name: "no mapper function when mode is not specified",
|
|
querySettings: `, "settings" : { }`,
|
|
expectedMapper: nil,
|
|
},
|
|
{
|
|
name: "error when settings is not object",
|
|
querySettings: `, "settings" : "drop-nan"`,
|
|
isError: true,
|
|
},
|
|
{
|
|
name: "no mapper function when mode is empty",
|
|
querySettings: `, "settings" : { "mode": "" }`,
|
|
expectedMapper: nil,
|
|
},
|
|
{
|
|
name: "error when mode is not known",
|
|
querySettings: `, "settings" : { "mode": "test" }`,
|
|
isError: true,
|
|
},
|
|
{
|
|
name: "filterNonNumber function when mode is 'dropNN'",
|
|
querySettings: `, "settings" : { "mode": "dropNN" }`,
|
|
expectedMapper: mathexp.DropNonNumber{},
|
|
},
|
|
{
|
|
name: "replaceNanWithValue function when mode is 'dropNN'",
|
|
querySettings: `, "settings" : { "mode": "replaceNN" , "replaceWithValue": -12 }`,
|
|
expectedMapper: mathexp.ReplaceNonNumberWithValue{Value: -12},
|
|
},
|
|
{
|
|
name: "error if mode is 'replaceNN' but field replaceWithValue is not specified",
|
|
querySettings: `, "settings" : { "mode": "replaceNN" }`,
|
|
isError: true,
|
|
},
|
|
{
|
|
name: "error if mode is 'replaceNN' but field replaceWithValue is not a number",
|
|
querySettings: `, "settings" : { "mode": "replaceNN", "replaceWithValue" : "-12" }`,
|
|
isError: true,
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
q := fmt.Sprintf(`{ "expression" : "$A", "reducer": "sum"%s }`, test.querySettings)
|
|
var qmap = make(map[string]interface{})
|
|
require.NoError(t, json.Unmarshal([]byte(q), &qmap))
|
|
|
|
cmd, err := UnmarshalReduceCommand(&rawNode{
|
|
RefID: "A",
|
|
Query: qmap,
|
|
QueryType: "",
|
|
TimeRange: RelativeTimeRange{},
|
|
DataSource: nil,
|
|
})
|
|
|
|
if test.isError {
|
|
require.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NotNil(t, cmd)
|
|
|
|
require.Equal(t, test.expectedMapper, cmd.seriesMapper)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestReduceExecute(t *testing.T) {
|
|
varToReduce := util.GenerateShortUID()
|
|
cmd, err := NewReduceCommand(util.GenerateShortUID(), randomReduceFunc(), varToReduce, nil)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("should noop if Number", func(t *testing.T) {
|
|
var numbers mathexp.Values = []mathexp.Value{
|
|
mathexp.GenerateNumber(ptr.Float64(rand.Float64())),
|
|
mathexp.GenerateNumber(ptr.Float64(rand.Float64())),
|
|
mathexp.GenerateNumber(ptr.Float64(rand.Float64())),
|
|
}
|
|
|
|
vars := map[string]mathexp.Results{
|
|
varToReduce: {
|
|
Values: numbers,
|
|
},
|
|
}
|
|
|
|
execute, err := cmd.Execute(context.Background(), time.Now(), vars)
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, execute.Values, len(numbers))
|
|
for i, value := range execute.Values {
|
|
expected := numbers[i]
|
|
require.Equal(t, expected.Type(), value.Type())
|
|
require.Equal(t, expected.GetLabels(), value.GetLabels())
|
|
|
|
expectedValue := expected.Value().(*mathexp.Number).GetFloat64Value()
|
|
actualValue := value.Value().(*mathexp.Number).GetFloat64Value()
|
|
require.Equal(t, expectedValue, actualValue)
|
|
}
|
|
|
|
t.Run("should add warn notices to every frame", func(t *testing.T) {
|
|
frames := execute.Values.AsDataFrames("test")
|
|
for _, frame := range frames {
|
|
require.Len(t, frame.Meta.Notices, 1)
|
|
notice := frame.Meta.Notices[0]
|
|
require.Equal(t, data.NoticeSeverityWarning, notice.Severity)
|
|
}
|
|
})
|
|
})
|
|
|
|
t.Run("should return new NoData", func(t *testing.T) {
|
|
var noData mathexp.Values = []mathexp.Value{
|
|
mathexp.NoData{Frame: data.NewFrame("no data")},
|
|
}
|
|
|
|
vars := map[string]mathexp.Results{
|
|
varToReduce: {
|
|
Values: noData,
|
|
},
|
|
}
|
|
|
|
results, err := cmd.Execute(context.Background(), time.Now(), vars)
|
|
require.NoError(t, err)
|
|
|
|
require.Len(t, results.Values, 1)
|
|
|
|
v := results.Values[0]
|
|
assert.Equal(t, v, mathexp.NoData{}.New())
|
|
|
|
// should not be able to change the original frame
|
|
v.AsDataFrame().Name = "there is still no data"
|
|
assert.NotEqual(t, v, mathexp.NoData{}.New())
|
|
assert.NotEqual(t, v, noData[0])
|
|
assert.Equal(t, "no data", noData[0].AsDataFrame().Name)
|
|
})
|
|
}
|
|
|
|
func randomReduceFunc() string {
|
|
res := mathexp.GetSupportedReduceFuncs()
|
|
return res[rand.Intn(len(res)-1)]
|
|
}
|