package server import ( "bytes" "context" "fmt" "io/ioutil" "net/http" "net/http/httptest" "strings" "testing" "github.com/bouk/httprouter" "github.com/influxdata/chronograf/log" "github.com/influxdata/chronograf/mocks" "github.com/influxdata/chronograf" ) func TestService_Influx(t *testing.T) { type fields struct { SourcesStore chronograf.SourcesStore TimeSeries TimeSeriesClient } type args struct { w *httptest.ResponseRecorder r *http.Request } type want struct { StatusCode int ContentType string Body string } tests := []struct { name string fields fields args args ID string want want }{ { name: "Proxies request to Influxdb", fields: fields{ SourcesStore: &mocks.SourcesStore{ GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { return chronograf.Source{ ID: 1337, URL: "http://any.url", }, nil }, }, TimeSeries: &mocks.TimeSeries{ ConnectF: func(ctx context.Context, src *chronograf.Source) error { return nil }, QueryF: func(ctx context.Context, query chronograf.Query) (chronograf.Response, error) { return mocks.NewResponse( `{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["key","value"],"values":[["cpu","cpu-total"],["cpu","cpu0"],["cpu","cpu1"],["cpu","cpu2"],["cpu","cpu3"],["host","pineapples-MBP"],["host","pineapples-MacBook-Pro.local"]]}]}]}`, nil, ), nil }, }, }, args: args{ w: httptest.NewRecorder(), r: httptest.NewRequest( "POST", "http://any.url", ioutil.NopCloser( bytes.NewReader([]byte( `{"db":"bob", "rp":"joe", "uuid": "bob", "query":"SELECT mean(\"usage_user\") FROM cpu WHERE \"cpu\" = 'cpu-total' AND time > now() - 10m GROUP BY host;"}`, )), ), ), }, ID: "1", want: want{ StatusCode: http.StatusOK, ContentType: "application/json", Body: `{"results":{"results":[{"statement_id":0,"series":[{"name":"cpu","columns":["key","value"],"values":[["cpu","cpu-total"],["cpu","cpu0"],["cpu","cpu1"],["cpu","cpu2"],["cpu","cpu3"],["host","pineapples-MBP"],["host","pineapples-MacBook-Pro.local"]]}]}]},"uuid":"bob"} `, }, }, } for _, tt := range tests { tt.args.r = tt.args.r.WithContext(httprouter.WithParams( context.Background(), httprouter.Params{ { Key: "id", Value: tt.ID, }, }, )) h := &Service{ Store: &mocks.Store{ SourcesStore: tt.fields.SourcesStore, }, TimeSeriesClient: tt.fields.TimeSeries, } h.Influx(tt.args.w, tt.args.r) resp := tt.args.w.Result() contentType := resp.Header.Get("Content-Type") body, _ := ioutil.ReadAll(resp.Body) if resp.StatusCode != tt.want.StatusCode { t.Errorf("%q. Influx() = got %v, want %v", tt.name, resp.StatusCode, tt.want.StatusCode) } if contentType != tt.want.ContentType { t.Errorf("%q. Influx() = got %v, want %v", tt.name, contentType, tt.want.ContentType) } if string(body) != tt.want.Body { t.Errorf("%q. Influx() =\ngot ***%v***\nwant ***%v***\n", tt.name, string(body), tt.want.Body) } } } // TestService_Influx_UseCommand test preprocessing of use command func TestService_Influx_UseCommand(t *testing.T) { tests := []struct { name string db string rp string }{ { name: "/* no command */", }, { name: "USE mydb", db: "mydb", }, { name: "USE mydb.myrp", db: "mydb", rp: "myrp", }, { name: `use "mydb"`, db: "mydb", }, { name: `use "mydb.myrp"`, db: "mydb.myrp", }, { name: `USE "mydb"."myrp"`, db: "mydb", rp: "myrp", }, } h := &Service{ Store: &mocks.Store{ SourcesStore: &mocks.SourcesStore{ GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { return chronograf.Source{ ID: 1337, URL: "http://any.url", }, nil }, }, }, TimeSeriesClient: &mocks.TimeSeries{ ConnectF: func(ctx context.Context, src *chronograf.Source) error { return nil }, QueryF: func(ctx context.Context, query chronograf.Query) (chronograf.Response, error) { return mocks.NewResponse( fmt.Sprintf(`{"db":"%s","rp":"%s"}`, query.DB, query.RP), nil, ), nil }, }, Logger: log.New(log.ErrorLevel), } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { prefixCommand := strings.ReplaceAll(tt.name, "\"", "\\\"") w := httptest.NewRecorder() r := httptest.NewRequest( "POST", "http://any.url", ioutil.NopCloser( bytes.NewReader([]byte( `{"uuid": "tst", "query":"`+prefixCommand+` ; DROP MEASUREMENT test"}`, )), ), ) r = r.WithContext(httprouter.WithParams( context.Background(), httprouter.Params{ { Key: "id", Value: "1", }, }, )) h.Influx(w, r) resp := w.Result() body, _ := ioutil.ReadAll(resp.Body) want := fmt.Sprintf(`{"results":{"db":"%s","rp":"%s"},"uuid":"tst"}`, tt.db, tt.rp) got := strings.TrimSpace(string(body)) if got != want { t.Errorf("%q. Influx() =\ngot ***%v***\nwant ***%v***\n", tt.name, got, want) } }) } }