package server import ( "bytes" "context" "net/http" "net/http/httptest" "testing" "github.com/bouk/httprouter" "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/mocks" ) func TestService_Queries(t *testing.T) { tests := []struct { name string SourcesStore chronograf.SourcesStore ID string w *httptest.ResponseRecorder r *http.Request want string }{ { name: "bad json", SourcesStore: &mocks.SourcesStore{ GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { return chronograf.Source{ ID: ID, }, nil }, }, ID: "1", w: httptest.NewRecorder(), r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`howdy`))), want: `{"code":400,"message":"Unparsable JSON"}`, }, { name: "bad id", ID: "howdy", w: httptest.NewRecorder(), r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte{})), want: `{"code":422,"message":"Error converting ID howdy"}`, }, { name: "query with no template vars", SourcesStore: &mocks.SourcesStore{ GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { return chronograf.Source{ ID: ID, }, nil }, }, ID: "1", w: httptest.NewRecorder(), r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`{ "queries": [ { "query": "SELECT \"pingReq\" FROM db.\"monitor\".\"httpd\" WHERE time > now() - 1m", "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ]}`))), want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM db.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"db","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":null,"range":{"upper":"","lower":"now() - 1m"}},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"db","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]}}]} `, }, { name: "query with unparsable query", SourcesStore: &mocks.SourcesStore{ GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { return chronograf.Source{ ID: ID, }, nil }, }, ID: "1", w: httptest.NewRecorder(), r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`{ "queries": [ { "query": "SHOW DATABASES", "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ]}`))), want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SHOW DATABASES","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"","measurement":"","retentionPolicy":"","fields":[],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SHOW DATABASES","range":null}}]} `, }, { name: "query with template vars", SourcesStore: &mocks.SourcesStore{ GetF: func(ctx context.Context, ID int) (chronograf.Source, error) { return chronograf.Source{ ID: ID, }, nil }, }, ID: "1", w: httptest.NewRecorder(), r: httptest.NewRequest("POST", "/queries", bytes.NewReader([]byte(`{ "queries": [ { "query": "SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time > now() - 1m", "id": "82b60d37-251e-4afe-ac93-ca20a3642b11" } ], "tempVars": [ { "tempVar": ":dbs:", "values": [ { "value": "_internal", "type": "database", "selected": true } ], "id": "792eda0d-2bb2-4de6-a86f-1f652889b044", "type": "databases", "label": "", "query": { "influxql": "SHOW DATABASES", "measurement": "", "tagKey": "", "fieldKey": "" }, "links": { "self": "/chronograf/v1/dashboards/1/templates/792eda0d-2bb2-4de6-a86f-1f652889b044" } }, { "id": "dashtime", "tempVar": ":dashboardTime:", "type": "constant", "values": [ { "value": "now() - 15m", "type": "constant", "selected": true } ] }, { "id": "upperdashtime", "tempVar": ":upperDashboardTime:", "type": "constant", "values": [ { "value": "now()", "type": "constant", "selected": true } ] }, { "id": "interval", "type": "constant", "tempVar": ":interval:", "resolution": 1000, "reportingInterval": 10000000000, "values": [] } ] }`))), want: `{"queries":[{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","query":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","queryConfig":{"id":"82b60d37-251e-4afe-ac93-ca20a3642b11","database":"_internal","measurement":"httpd","retentionPolicy":"monitor","fields":[{"value":"pingReq","type":"field","alias":""}],"tags":{},"groupBy":{"time":"","tags":[]},"areTagsAccepted":false,"rawText":"SELECT \"pingReq\" FROM :dbs:.\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","range":{"upper":"","lower":"now() - 1m"}},"queryAST":{"condition":{"expr":"binary","op":"\u003e","lhs":{"expr":"reference","val":"time"},"rhs":{"expr":"binary","op":"-","lhs":{"expr":"call","name":"now"},"rhs":{"expr":"literal","val":"1m","type":"duration"}}},"fields":[{"column":{"expr":"reference","val":"pingReq"}}],"sources":[{"database":"_internal","retentionPolicy":"monitor","name":"httpd","type":"measurement"}]},"queryTemplated":"SELECT \"pingReq\" FROM \"_internal\".\"monitor\".\"httpd\" WHERE time \u003e now() - 1m","tempVars":[{"tempVar":":dbs:","values":[{"value":"_internal","type":"database","selected":true}]},{"tempVar":":dashboardTime:","values":[{"value":"now() - 15m","type":"constant","selected":true}]},{"tempVar":":upperDashboardTime:","values":[{"value":"now()","type":"constant","selected":true}]},{"tempVar":":interval:","duration":60000000000,"resolution":1000,"reportingInterval":10000000000}]}]} `, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { tt.r = tt.r.WithContext(httprouter.WithParams( context.Background(), httprouter.Params{ { Key: "id", Value: tt.ID, }, })) s := &Service{ SourcesStore: tt.SourcesStore, Logger: &mocks.TestLogger{}, } s.Queries(tt.w, tt.r) got := tt.w.Body.String() if got != tt.want { t.Errorf("got:\n%s\nwant:\n%s\n", got, tt.want) } }) } }