grafana/pkg/expr/translate/translate_test.go

147 lines
5.1 KiB
Go

package translate
import (
"encoding/json"
"fmt"
"io/ioutil"
"path/filepath"
"testing"
"time"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/expr/classic"
"github.com/grafana/grafana/pkg/models"
ngmodels "github.com/grafana/grafana/pkg/services/ngalert/models"
"github.com/stretchr/testify/require"
)
func TestDashboardAlertConditions(t *testing.T) {
registerGetDsInfoHandler()
var tests = []struct {
name string
// inputJSONFName, at least for now, is as "conditions" will appear within the alert table
// settings column JSON. Which means it has already run through the dashboard
// alerting Extractor. It is the input.
inputJSONFName string
// Condition is quite large (and unexported things), so check misc attributes.
spotCheckFn func(t *testing.T, cond *ngmodels.Condition)
}{
{
name: "two conditions one query but different time ranges",
inputJSONFName: `sameQueryDifferentTimeRange.json`,
spotCheckFn: func(t *testing.T, cond *ngmodels.Condition) {
require.Equal(t, "C", cond.Condition, "unexpected refId for condition")
require.Equal(t, 3, len(cond.Data), "unexpected query/expression array length")
firstQuery := cond.Data[0]
require.Equal(t, "A", firstQuery.RefID, "unexpected refId for first query")
require.Equal(t, ngmodels.RelativeTimeRange{
From: ngmodels.Duration(time.Second * 600),
To: ngmodels.Duration(time.Second * 300),
}, firstQuery.RelativeTimeRange, "unexpected timerange for first query")
secondQuery := cond.Data[1]
require.Equal(t, "B", secondQuery.RefID, "unexpected refId for second query")
require.Equal(t, ngmodels.RelativeTimeRange{
From: ngmodels.Duration(time.Second * 300),
To: ngmodels.Duration(0),
}, secondQuery.RelativeTimeRange, "unexpected timerange for second query")
condQuery := cond.Data[2]
require.Equal(t, "C", condQuery.RefID, "unexpected refId for second query")
isExpr, err := condQuery.IsExpression()
require.NoError(t, err)
require.Equal(t, true, isExpr, "third query should be an expression")
c := struct {
Conditions []classic.ClassicConditionJSON `json:"conditions"`
}{}
err = json.Unmarshal(condQuery.Model, &c)
require.NoError(t, err)
require.Equal(t, 2, len(c.Conditions), "expected 2 conditions in classic condition")
// This is "correct" in that the condition gets the correct time range,
// but a bit odd that it creates B then A, can look into changing that
// later.
firstCond := c.Conditions[0]
require.Equal(t, "lt", firstCond.Evaluator.Type, "expected first cond to use lt")
require.Equal(t, "B", firstCond.Query.Params[0], "expected first cond to reference B")
secondCond := c.Conditions[1]
require.Equal(t, "gt", secondCond.Evaluator.Type, "expected second cond to use gt")
require.Equal(t, "A", secondCond.Query.Params[0], "expected second cond to reference A")
},
},
{
name: "mixed shared and unshared time ranges",
inputJSONFName: `mixedSharedUnsharedTimeRange.json`,
spotCheckFn: func(t *testing.T, cond *ngmodels.Condition) {
require.Equal(t, "G", cond.Condition, "unexpected refId for condition")
require.Equal(t, 7, len(cond.Data), "unexpected query/expression array length")
condQuery := cond.Data[6]
isExpr, err := condQuery.IsExpression()
require.NoError(t, err)
require.Equal(t, true, isExpr, "expected last query to be an expression")
c := struct {
Conditions []classic.ClassicConditionJSON `json:"conditions"`
}{}
err = json.Unmarshal(condQuery.Model, &c)
require.NoError(t, err)
require.Equal(t, 8, len(c.Conditions), "expected 8 conditions in classic condition")
firstCond := c.Conditions[0]
require.Equal(t, "gt", firstCond.Evaluator.Type, "expected first cond to use gt")
require.Equal(t, "avg", firstCond.Reducer.Type, "expected first cond to use reducer avg")
firstCondRefID := firstCond.Query.Params[0]
aq, err := alertRuleByRefId(cond, firstCondRefID)
require.NoError(t, err)
require.Equal(t, ngmodels.Duration(300*time.Second), aq.RelativeTimeRange.From,
"expected first condition to reference a query with a from of 300 seconds")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
jsonFile := filepath.Join("testdata", tt.inputJSONFName)
//nolint:GOSEC
b, err := ioutil.ReadFile(jsonFile)
require.NoError(t, err)
cond, err := DashboardAlertConditions(b, 1)
require.NoError(t, err)
tt.spotCheckFn(t, cond)
})
}
}
func alertRuleByRefId(cond *ngmodels.Condition, refID string) (ngmodels.AlertQuery, error) {
for _, aq := range cond.Data {
if aq.RefID == refID {
return aq, nil
}
}
return ngmodels.AlertQuery{}, fmt.Errorf("query with refId %v not found", refID)
}
func registerGetDsInfoHandler() {
bus.AddHandler("test", func(query *models.GetDataSourceQuery) error {
switch {
case query.Id == 2:
query.Result = &models.DataSource{Id: 2, OrgId: 1, Uid: "000000002"}
case query.Id == 4:
query.Result = &models.DataSource{Id: 4, OrgId: 1, Uid: "000000004"}
default:
return fmt.Errorf("datasource not found")
}
return nil
})
}