mirror of
https://github.com/grafana/grafana.git
synced 2024-11-28 19:54:10 -06:00
294 lines
6.1 KiB
Go
294 lines
6.1 KiB
Go
package expr
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
)
|
|
|
|
func TestServicebuildPipeLine(t *testing.T) {
|
|
var tests = []struct {
|
|
name string
|
|
req *Request
|
|
expectedOrder []string
|
|
expectErrContains string
|
|
}{
|
|
{
|
|
name: "simple: a requires b",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "B",
|
|
"reducer": "mean",
|
|
"type": "reduce"
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "B",
|
|
DataSource: &datasources.DataSource{
|
|
UID: "Fake",
|
|
},
|
|
TimeRange: AbsoluteTimeRange{},
|
|
},
|
|
},
|
|
},
|
|
expectedOrder: []string{"B", "A"},
|
|
},
|
|
{
|
|
name: "cycle will error",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "$B",
|
|
"type": "math"
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "B",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "$A",
|
|
"type": "math"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
expectErrContains: "cyclic components",
|
|
},
|
|
{
|
|
name: "self reference will error",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "$A",
|
|
"type": "math"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
expectErrContains: "expression 'A' cannot reference itself. Must be query or another expression",
|
|
},
|
|
{
|
|
name: "missing dependency will error",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "$B",
|
|
"type": "math"
|
|
}`),
|
|
},
|
|
},
|
|
},
|
|
expectErrContains: "find dependent",
|
|
},
|
|
{
|
|
name: "classic can not take input from another expression",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"type": "classic_conditions",
|
|
"conditions": [
|
|
{
|
|
"evaluator": {
|
|
"params": [
|
|
2,
|
|
3
|
|
],
|
|
"type": "within_range"
|
|
},
|
|
"operator": {
|
|
"type": "or"
|
|
},
|
|
"query": {
|
|
"params": [
|
|
"B"
|
|
]
|
|
},
|
|
"reducer": {
|
|
"params": [],
|
|
"type": "diff"
|
|
},
|
|
"type": "query"
|
|
}
|
|
]
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "B",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "C",
|
|
"reducer": "mean",
|
|
"type": "reduce"
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "C",
|
|
DataSource: &datasources.DataSource{
|
|
UID: "Fake",
|
|
},
|
|
TimeRange: AbsoluteTimeRange{},
|
|
},
|
|
},
|
|
},
|
|
expectErrContains: "only data source queries may be inputs to a classic condition",
|
|
},
|
|
{
|
|
name: "classic can not output to another expression",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"type": "classic_conditions",
|
|
"conditions": [
|
|
{
|
|
"evaluator": {
|
|
"params": [
|
|
2,
|
|
3
|
|
],
|
|
"type": "within_range"
|
|
},
|
|
"operator": {
|
|
"type": "or"
|
|
},
|
|
"query": {
|
|
"params": [
|
|
"C"
|
|
]
|
|
},
|
|
"reducer": {
|
|
"params": [],
|
|
"type": "diff"
|
|
},
|
|
"type": "query"
|
|
}
|
|
]
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "B",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "A",
|
|
"reducer": "mean",
|
|
"type": "reduce"
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "C",
|
|
DataSource: &datasources.DataSource{
|
|
UID: "Fake",
|
|
},
|
|
TimeRange: AbsoluteTimeRange{},
|
|
},
|
|
},
|
|
},
|
|
expectErrContains: "classic conditions may not be the input for other expressions",
|
|
},
|
|
{
|
|
name: "Queries with new datasource ref object",
|
|
req: &Request{
|
|
Queries: []Query{
|
|
{
|
|
RefID: "A",
|
|
DataSource: dataSourceModel(),
|
|
JSON: json.RawMessage(`{
|
|
"expression": "B",
|
|
"reducer": "mean",
|
|
"type": "reduce"
|
|
}`),
|
|
},
|
|
{
|
|
RefID: "B",
|
|
DataSource: &datasources.DataSource{
|
|
UID: "Fake",
|
|
},
|
|
TimeRange: AbsoluteTimeRange{},
|
|
},
|
|
},
|
|
},
|
|
expectedOrder: []string{"B", "A"},
|
|
},
|
|
}
|
|
s := Service{
|
|
features: featuremgmt.WithFeatures(featuremgmt.FlagExpressionParser),
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
nodes, err := s.buildPipeline(tt.req)
|
|
if tt.expectErrContains != "" {
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), tt.expectErrContains)
|
|
} else {
|
|
require.NoError(t, err)
|
|
require.Equal(t, tt.expectedOrder, getRefIDOrder(nodes))
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetCommandsFromPipeline(t *testing.T) {
|
|
pipeline := DataPipeline{
|
|
&MLNode{},
|
|
&DSNode{},
|
|
&CMDNode{
|
|
baseNode: baseNode{},
|
|
CMDType: 0,
|
|
Command: &ReduceCommand{},
|
|
},
|
|
&CMDNode{
|
|
baseNode: baseNode{},
|
|
CMDType: 0,
|
|
Command: &ReduceCommand{},
|
|
},
|
|
&CMDNode{
|
|
baseNode: baseNode{},
|
|
CMDType: 0,
|
|
Command: &HysteresisCommand{},
|
|
},
|
|
}
|
|
t.Run("should find command that exists", func(t *testing.T) {
|
|
cmds := GetCommandsFromPipeline[*HysteresisCommand](pipeline)
|
|
require.Len(t, cmds, 1)
|
|
require.Equal(t, pipeline[4].(*CMDNode).Command, cmds[0])
|
|
})
|
|
t.Run("should find all commands that exist", func(t *testing.T) {
|
|
cmds := GetCommandsFromPipeline[*ReduceCommand](pipeline)
|
|
require.Len(t, cmds, 2)
|
|
})
|
|
t.Run("should not find all command that does not exist", func(t *testing.T) {
|
|
cmds := GetCommandsFromPipeline[*MathCommand](pipeline)
|
|
require.Len(t, cmds, 0)
|
|
})
|
|
}
|
|
|
|
func getRefIDOrder(nodes []Node) []string {
|
|
ids := make([]string, 0, len(nodes))
|
|
for _, n := range nodes {
|
|
ids = append(ids, n.RefID())
|
|
}
|
|
return ids
|
|
}
|