mirror of https://github.com/grafana/grafana.git
				
				
				
			
		
			
				
	
	
		
			105 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			Go
		
	
	
	
| package expr
 | |
| 
 | |
| import (
 | |
| 	"context"
 | |
| 	"encoding/json"
 | |
| 	"fmt"
 | |
| 	"testing"
 | |
| 	"time"
 | |
| 
 | |
| 	"github.com/google/go-cmp/cmp"
 | |
| 	"github.com/grafana/grafana-plugin-sdk-go/backend"
 | |
| 	"github.com/grafana/grafana-plugin-sdk-go/data"
 | |
| 	"github.com/grafana/grafana/pkg/services/datasources"
 | |
| 	"github.com/grafana/grafana/pkg/services/featuremgmt"
 | |
| 	"github.com/stretchr/testify/require"
 | |
| )
 | |
| 
 | |
| func TestSQLService(t *testing.T) {
 | |
| 	inputFrame := data.NewFrame("",
 | |
| 		data.NewField("time", nil, []time.Time{time.Unix(1, 0)}),
 | |
| 		data.NewField("value", nil, []*float64{fp(2)}),
 | |
| 	)
 | |
| 
 | |
| 	resp := map[string]backend.DataResponse{
 | |
| 		"A": {Frames: data.Frames{inputFrame}},
 | |
| 	}
 | |
| 
 | |
| 	newABSQLQueries := func(q string) []Query {
 | |
| 		q, err := jsonEscape(q)
 | |
| 		require.NoError(t, err)
 | |
| 		return []Query{
 | |
| 			{
 | |
| 				RefID: "A",
 | |
| 				DataSource: &datasources.DataSource{
 | |
| 					OrgID: 1,
 | |
| 					UID:   "test",
 | |
| 					Type:  "test",
 | |
| 				},
 | |
| 				JSON: json.RawMessage(`{ "datasource": { "uid": "1" }, "intervalMs": 1000, "maxDataPoints": 1000 }`),
 | |
| 				TimeRange: AbsoluteTimeRange{
 | |
| 					From: time.Time{},
 | |
| 					To:   time.Time{},
 | |
| 				},
 | |
| 			},
 | |
| 			{
 | |
| 				RefID:      "B",
 | |
| 				DataSource: dataSourceModel(),
 | |
| 				JSON:       json.RawMessage(fmt.Sprintf(`{ "datasource": { "uid": "__expr__", "type": "__expr__"}, "type": "sql", "expression": "%s" }`, q)),
 | |
| 				TimeRange: AbsoluteTimeRange{
 | |
| 					From: time.Time{},
 | |
| 					To:   time.Time{},
 | |
| 				},
 | |
| 			},
 | |
| 		}
 | |
| 	}
 | |
| 	t.Run("no feature flag no queries for you", func(t *testing.T) {
 | |
| 		s, req := newMockQueryService(resp, newABSQLQueries(""))
 | |
| 
 | |
| 		_, err := s.BuildPipeline(req)
 | |
| 		require.Error(t, err, "should not be able to build pipeline without feature flag")
 | |
| 	})
 | |
| 
 | |
| 	t.Run("with feature flag basic select works", func(t *testing.T) {
 | |
| 		s, req := newMockQueryService(resp, newABSQLQueries("SELECT * FROM A"))
 | |
| 		s.features = featuremgmt.WithFeatures(featuremgmt.FlagSqlExpressions)
 | |
| 		pl, err := s.BuildPipeline(req)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		res, err := s.ExecutePipeline(context.Background(), time.Now(), pl)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		inputFrame.RefID = "B"
 | |
| 		inputFrame.Name = "B"
 | |
| 		if diff := cmp.Diff(res.Responses["B"].Frames[0], inputFrame, data.FrameTestCompareOptions()...); diff != "" {
 | |
| 			require.FailNowf(t, "Result mismatch (-want +got):%s\n", diff)
 | |
| 		}
 | |
| 	})
 | |
| 
 | |
| 	t.Run("load_file is blocked", func(t *testing.T) {
 | |
| 		s, req := newMockQueryService(resp,
 | |
| 			newABSQLQueries(`SELECT CAST(load_file('/etc/topSecretz') AS CHAR(10000) CHARACTER SET utf8)`),
 | |
| 		)
 | |
| 
 | |
| 		s.features = featuremgmt.WithFeatures(featuremgmt.FlagSqlExpressions)
 | |
| 
 | |
| 		pl, err := s.BuildPipeline(req)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		rsp, err := s.ExecutePipeline(context.Background(), time.Now(), pl)
 | |
| 		require.NoError(t, err)
 | |
| 
 | |
| 		require.Error(t, rsp.Responses["B"].Error, "should return invalid sql error")
 | |
| 		require.ErrorContains(t, rsp.Responses["B"].Error, "blocked function load_file")
 | |
| 	})
 | |
| }
 | |
| 
 | |
| func jsonEscape(input string) (string, error) {
 | |
| 	escaped, err := json.Marshal(input)
 | |
| 	if err != nil {
 | |
| 		return "", err
 | |
| 	}
 | |
| 	// json.Marshal returns the escaped string with quotes, so we need to trim them
 | |
| 	return string(escaped[1 : len(escaped)-1]), nil
 | |
| }
 |