mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			275 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			275 lines
		
	
	
		
			8.7 KiB
		
	
	
	
		
			Go
		
	
	
	
| package expr
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"errors"
 | |
| 	"fmt"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/grafana/grafana-plugin-sdk-go/data"
 | |
| 	"github.com/stretchr/testify/assert"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| 
 | |
| 	"github.com/grafana/grafana/pkg/expr/mathexp"
 | |
| 	"github.com/grafana/grafana/pkg/infra/log/logtest"
 | |
| 	"github.com/grafana/grafana/pkg/infra/tracing"
 | |
| 	"github.com/grafana/grafana/pkg/services/datasources"
 | |
| 	"github.com/grafana/grafana/pkg/services/featuremgmt"
 | |
| 	"github.com/grafana/grafana/pkg/setting"
 | |
| )
 | |
| 
 | |
| type expectedError struct{}
 | |
| 
 | |
| func (e expectedError) Error() string {
 | |
| 	return "expected"
 | |
| }
 | |
| 
 | |
| func TestQueryError_Error(t *testing.T) {
 | |
| 	e := MakeQueryError("A", "", errors.New("this is an error message"))
 | |
| 	assert.EqualError(t, e, "[sse.dataQueryError] failed to execute query [A]: this is an error message")
 | |
| }
 | |
| 
 | |
| func TestQueryError_Unwrap(t *testing.T) {
 | |
| 	t.Run("errors.Is", func(t *testing.T) {
 | |
| 		expectedIsErr := errors.New("expected")
 | |
| 		e := MakeQueryError("A", "", expectedIsErr)
 | |
| 		assert.True(t, errors.Is(e, expectedIsErr))
 | |
| 	})
 | |
| 
 | |
| 	t.Run("errors.As", func(t *testing.T) {
 | |
| 		e := MakeQueryError("A", "", expectedError{})
 | |
| 		var expectedAsError expectedError
 | |
| 		assert.True(t, errors.As(e, &expectedAsError))
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func TestCheckIfSeriesNeedToBeFixed(t *testing.T) {
 | |
| 	createFrame := func(m ...func(field *data.Field)) []*data.Frame {
 | |
| 		f := data.NewFrame("",
 | |
| 			data.NewField("Time", nil, []time.Time{}))
 | |
| 		for i := 0; i < 100; i++ {
 | |
| 			fld := data.NewField(fmt.Sprintf("fld-%d", i), nil, []*float64{})
 | |
| 			fld.Config = &data.FieldConfig{}
 | |
| 			for _, change := range m {
 | |
| 				change(fld)
 | |
| 			}
 | |
| 			f.Fields = append(f.Fields, fld)
 | |
| 		}
 | |
| 		return []*data.Frame{f}
 | |
| 	}
 | |
| 	withLabels := func(field *data.Field) {
 | |
| 		field.Labels = map[string]string{
 | |
| 			"field": field.Name,
 | |
| 		}
 | |
| 	}
 | |
| 	withDisplayNameFromDS := func(field *data.Field) {
 | |
| 		field.Config.DisplayNameFromDS = fmt.Sprintf("dnds-%s", field.Name)
 | |
| 	}
 | |
| 	withDisplayName := func(field *data.Field) {
 | |
| 		field.Config.DisplayName = fmt.Sprintf("dn-%s", field.Name)
 | |
| 	}
 | |
| 	withoutName := func(field *data.Field) {
 | |
| 		field.Name = ""
 | |
| 	}
 | |
| 
 | |
| 	getLabelName := func(f func(series mathexp.Series, valueField *data.Field)) string {
 | |
| 		s := mathexp.NewSeries("A", nil, 0)
 | |
| 		field := &data.Field{
 | |
| 			Name: "Name",
 | |
| 			Config: &data.FieldConfig{
 | |
| 				DisplayNameFromDS: "DisplayNameFromDS",
 | |
| 				DisplayName:       "DisplayName",
 | |
| 			},
 | |
| 		}
 | |
| 		f(s, field)
 | |
| 		return s.GetLabels()[nameLabelName]
 | |
| 	}
 | |
| 
 | |
| 	testCases := []struct {
 | |
| 		name         string
 | |
| 		frames       []*data.Frame
 | |
| 		expectedName string
 | |
| 	}{
 | |
| 		{
 | |
| 			name:         "should return nil if at least one value field has labels",
 | |
| 			frames:       createFrame(withLabels, withDisplayNameFromDS, withDisplayName),
 | |
| 			expectedName: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "should return nil if names are empty",
 | |
| 			frames:       createFrame(withoutName),
 | |
| 			expectedName: "",
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "should return patcher with DisplayNameFromDS first",
 | |
| 			frames:       createFrame(withDisplayNameFromDS, withDisplayName),
 | |
| 			expectedName: "DisplayNameFromDS",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "should return patcher with DisplayName if DisplayNameFromDS is not unique",
 | |
| 			frames: func() []*data.Frame {
 | |
| 				frames := createFrame(withDisplayNameFromDS, withDisplayName)
 | |
| 				f := frames[0]
 | |
| 				f.Fields[2].Config.DisplayNameFromDS = "test"
 | |
| 				f.Fields[3].Config.DisplayNameFromDS = "test"
 | |
| 				return frames
 | |
| 			}(),
 | |
| 			expectedName: "DisplayName",
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "should return patcher with DisplayName if is empty",
 | |
| 			frames:       createFrame(withDisplayName),
 | |
| 			expectedName: "DisplayName",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "should return patcher with Name if DisplayName and DisplayNameFromDS are not unique",
 | |
| 			frames: func() []*data.Frame {
 | |
| 				frames := createFrame(withDisplayNameFromDS, withDisplayName)
 | |
| 				f := frames[0]
 | |
| 				f.Fields[1].Config.DisplayNameFromDS = f.Fields[2].Config.DisplayNameFromDS
 | |
| 				f.Fields[1].Config.DisplayName = f.Fields[2].Config.DisplayName
 | |
| 				return frames
 | |
| 			}(),
 | |
| 			expectedName: "Name",
 | |
| 		},
 | |
| 		{
 | |
| 			name:         "should return patcher with Name if DisplayName and DisplayNameFromDS are empty",
 | |
| 			frames:       createFrame(),
 | |
| 			expectedName: "Name",
 | |
| 		},
 | |
| 		{
 | |
| 			name: "should return nil if all fields are not unique",
 | |
| 			frames: func() []*data.Frame {
 | |
| 				frames := createFrame(withDisplayNameFromDS, withDisplayName)
 | |
| 				f := frames[0]
 | |
| 				f.Fields[1].Config.DisplayNameFromDS = f.Fields[2].Config.DisplayNameFromDS
 | |
| 				f.Fields[1].Config.DisplayName = f.Fields[2].Config.DisplayName
 | |
| 				f.Fields[1].Name = f.Fields[2].Name
 | |
| 				return frames
 | |
| 			}(),
 | |
| 			expectedName: "",
 | |
| 		},
 | |
| 	}
 | |
| 
 | |
| 	supportedDatasources := []string{
 | |
| 		datasources.DS_GRAPHITE,
 | |
| 		datasources.DS_TESTDATA,
 | |
| 	}
 | |
| 	for _, tc := range testCases {
 | |
| 		t.Run(tc.name, func(t *testing.T) {
 | |
| 			for _, datasource := range supportedDatasources {
 | |
| 				fixer := checkIfSeriesNeedToBeFixed(tc.frames, datasource)
 | |
| 				if tc.expectedName == "" {
 | |
| 					require.Nil(t, fixer)
 | |
| 				} else {
 | |
| 					require.Equal(t, tc.expectedName, getLabelName(fixer))
 | |
| 				}
 | |
| 			}
 | |
| 		})
 | |
| 	}
 | |
| }
 | |
| 
 | |
| func TestConvertDataFramesToResults(t *testing.T) {
 | |
| 	s := &Service{
 | |
| 		cfg:      setting.NewCfg(),
 | |
| 		features: &featuremgmt.FeatureManager{},
 | |
| 		tracer:   tracing.InitializeTracerForTest(),
 | |
| 		metrics:  newMetrics(nil),
 | |
| 	}
 | |
| 
 | |
| 	t.Run("should add name label if no labels and specific data source", func(t *testing.T) {
 | |
| 		supported := []string{datasources.DS_GRAPHITE, datasources.DS_TESTDATA}
 | |
| 		t.Run("when only field name is specified", func(t *testing.T) {
 | |
| 			t.Run("use value field names if one frame - many series", func(t *testing.T) {
 | |
| 				supported := []string{datasources.DS_GRAPHITE, datasources.DS_TESTDATA}
 | |
| 
 | |
| 				frames := []*data.Frame{
 | |
| 					data.NewFrame("test",
 | |
| 						data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
 | |
| 						data.NewField("test-value1", nil, []*float64{fp(2)}),
 | |
| 						data.NewField("test-value2", nil, []*float64{fp(2)})),
 | |
| 				}
 | |
| 
 | |
| 				for _, dtype := range supported {
 | |
| 					t.Run(dtype, func(t *testing.T) {
 | |
| 						resultType, res, err := convertDataFramesToResults(context.Background(), frames, dtype, s, &logtest.Fake{})
 | |
| 						require.NoError(t, err)
 | |
| 						assert.Equal(t, "single frame series", resultType)
 | |
| 						require.Len(t, res.Values, 2)
 | |
| 
 | |
| 						var names []string
 | |
| 						for _, value := range res.Values {
 | |
| 							require.IsType(t, mathexp.Series{}, value)
 | |
| 							lbls := value.GetLabels()
 | |
| 							require.Contains(t, lbls, nameLabelName)
 | |
| 							names = append(names, lbls[nameLabelName])
 | |
| 						}
 | |
| 						require.EqualValues(t, []string{"test-value1", "test-value2"}, names)
 | |
| 					})
 | |
| 				}
 | |
| 			})
 | |
| 			t.Run("should use frame name if one frame - one series", func(t *testing.T) {
 | |
| 				frames := []*data.Frame{
 | |
| 					data.NewFrame("test-frame1",
 | |
| 						data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
 | |
| 						data.NewField("test-value1", nil, []*float64{fp(2)})),
 | |
| 					data.NewFrame("test-frame2",
 | |
| 						data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
 | |
| 						data.NewField("test-value2", nil, []*float64{fp(2)})),
 | |
| 				}
 | |
| 
 | |
| 				for _, dtype := range supported {
 | |
| 					t.Run(dtype, func(t *testing.T) {
 | |
| 						resultType, res, err := convertDataFramesToResults(context.Background(), frames, dtype, s, &logtest.Fake{})
 | |
| 						require.NoError(t, err)
 | |
| 						assert.Equal(t, "multi frame series", resultType)
 | |
| 						require.Len(t, res.Values, 2)
 | |
| 
 | |
| 						var names []string
 | |
| 						for _, value := range res.Values {
 | |
| 							require.IsType(t, mathexp.Series{}, value)
 | |
| 							lbls := value.GetLabels()
 | |
| 							require.Contains(t, lbls, nameLabelName)
 | |
| 							names = append(names, lbls[nameLabelName])
 | |
| 						}
 | |
| 						require.EqualValues(t, []string{"test-frame1", "test-frame2"}, names)
 | |
| 					})
 | |
| 				}
 | |
| 			})
 | |
| 		})
 | |
| 		t.Run("should use fields DisplayNameFromDS when it is unique", func(t *testing.T) {
 | |
| 			f1 := data.NewField("test-value1", nil, []*float64{fp(2)})
 | |
| 			f1.Config = &data.FieldConfig{DisplayNameFromDS: "test-value1"}
 | |
| 			f2 := data.NewField("test-value2", nil, []*float64{fp(2)})
 | |
| 			f2.Config = &data.FieldConfig{DisplayNameFromDS: "test-value2"}
 | |
| 			frames := []*data.Frame{
 | |
| 				data.NewFrame("test-frame1",
 | |
| 					data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
 | |
| 					f1),
 | |
| 				data.NewFrame("test-frame2",
 | |
| 					data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
 | |
| 					f2),
 | |
| 			}
 | |
| 
 | |
| 			for _, dtype := range supported {
 | |
| 				t.Run(dtype, func(t *testing.T) {
 | |
| 					resultType, res, err := convertDataFramesToResults(context.Background(), frames, dtype, s, &logtest.Fake{})
 | |
| 					require.NoError(t, err)
 | |
| 					assert.Equal(t, "multi frame series", resultType)
 | |
| 					require.Len(t, res.Values, 2)
 | |
| 
 | |
| 					var names []string
 | |
| 					for _, value := range res.Values {
 | |
| 						require.IsType(t, mathexp.Series{}, value)
 | |
| 						lbls := value.GetLabels()
 | |
| 						require.Contains(t, lbls, nameLabelName)
 | |
| 						names = append(names, lbls[nameLabelName])
 | |
| 					}
 | |
| 					require.EqualValues(t, []string{"test-value1", "test-value2"}, names)
 | |
| 				})
 | |
| 			}
 | |
| 		})
 | |
| 	})
 | |
| }
 |