influxdb/http/query_test.go

477 lines
10 KiB
Go
Raw Normal View History

package http
import (
"bytes"
"context"
"github.com/influxdata/flux/csv"
"github.com/influxdata/flux/lang"
"github.com/influxdata/platform/mock"
"net/http"
"net/http/httptest"
"reflect"
"testing"
"time"
"github.com/influxdata/flux"
"github.com/influxdata/flux/ast"
"github.com/influxdata/platform"
"github.com/influxdata/platform/query"
)
func TestQueryRequest_WithDefaults(t *testing.T) {
type fields struct {
Spec *flux.Spec
AST *ast.Program
Query string
Type string
Dialect QueryDialect
org *platform.Organization
}
tests := []struct {
name string
fields fields
want QueryRequest
}{
{
name: "empty query has defaults set",
want: QueryRequest{
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "RFC3339",
Header: func(x bool) *bool { return &x }(true),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := QueryRequest{
Spec: tt.fields.Spec,
AST: tt.fields.AST,
Query: tt.fields.Query,
Type: tt.fields.Type,
Dialect: tt.fields.Dialect,
org: tt.fields.org,
}
if got := r.WithDefaults(); !reflect.DeepEqual(got, tt.want) {
t.Errorf("QueryRequest.WithDefaults() = %v, want %v", got, tt.want)
}
})
}
}
func TestQueryRequest_Validate(t *testing.T) {
type fields struct {
Spec *flux.Spec
AST *ast.Program
Query string
Type string
Dialect QueryDialect
org *platform.Organization
}
tests := []struct {
name string
fields fields
wantErr bool
}{
{
name: "requires query, spec, or ast",
fields: fields{
Type: "flux",
},
wantErr: true,
},
{
name: "requires flux type",
fields: fields{
Query: "howdy",
Type: "doody",
},
wantErr: true,
},
{
name: "comment must be a single character",
fields: fields{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
CommentPrefix: "error!",
},
},
wantErr: true,
},
{
name: "delimiter must be a single character",
fields: fields{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
Delimiter: "",
},
},
wantErr: true,
},
{
name: "characters must be unicode runes",
fields: fields{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
Delimiter: string([]byte{0x80}),
},
},
wantErr: true,
},
{
name: "unknown annotations",
fields: fields{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
Annotations: []string{"error"},
},
},
wantErr: true,
},
{
name: "unknown date time format",
fields: fields{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "error",
},
},
wantErr: true,
},
{
name: "valid query",
fields: fields{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "RFC3339",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := QueryRequest{
Spec: tt.fields.Spec,
AST: tt.fields.AST,
Query: tt.fields.Query,
Type: tt.fields.Type,
Dialect: tt.fields.Dialect,
org: tt.fields.org,
}
if err := r.Validate(); (err != nil) != tt.wantErr {
t.Errorf("QueryRequest.Validate() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func Test_toSpec(t *testing.T) {
flux.FinalizeBuiltIns()
type args struct {
p *ast.Program
now func() time.Time
}
tests := []struct {
name string
args args
want *flux.Spec
wantErr bool
}{
{
name: "ast converts to spec",
args: args{
p: &ast.Program{},
now: func() time.Time { return time.Unix(0, 0) },
},
want: &flux.Spec{
Now: time.Unix(0, 0).UTC(),
},
},
{
name: "bad semantics error",
args: args{
p: &ast.Program{
Body: []ast.Statement{
&ast.ReturnStatement{},
},
},
now: func() time.Time { return time.Unix(0, 0) },
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := toSpec(tt.args.p, tt.args.now)
if (err != nil) != tt.wantErr {
t.Errorf("toSpec() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("toSpec() = %v, want %v", got, tt.want)
}
})
}
}
func TestQueryRequest_proxyRequest(t *testing.T) {
type fields struct {
Spec *flux.Spec
AST *ast.Program
Query string
Type string
Dialect QueryDialect
org *platform.Organization
}
tests := []struct {
name string
fields fields
now func() time.Time
want *query.ProxyRequest
wantErr bool
}{
{
name: "requires query, spec, or ast",
fields: fields{
Type: "flux",
},
wantErr: true,
},
{
name: "valid query",
fields: fields{
Query: "howdy",
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "RFC3339",
},
org: &platform.Organization{},
},
want: &query.ProxyRequest{
Request: query.Request{
Compiler: lang.FluxCompiler{
Query: "howdy",
},
},
Dialect: csv.Dialect{
ResultEncoderConfig: csv.ResultEncoderConfig{
NoHeader: false,
Delimiter: ',',
},
},
},
},
{
name: "valid AST",
fields: fields{
AST: &ast.Program{},
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "RFC3339",
},
org: &platform.Organization{},
},
now: func() time.Time { return time.Unix(0, 0).UTC() },
want: &query.ProxyRequest{
Request: query.Request{
Compiler: lang.SpecCompiler{
Spec: &flux.Spec{
Now: time.Unix(0, 0).UTC(),
},
},
},
Dialect: csv.Dialect{
ResultEncoderConfig: csv.ResultEncoderConfig{
NoHeader: false,
Delimiter: ',',
},
},
},
},
{
name: "valid spec",
fields: fields{
Type: "flux",
Spec: &flux.Spec{
Now: time.Unix(0, 0).UTC(),
},
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "RFC3339",
},
org: &platform.Organization{},
},
want: &query.ProxyRequest{
Request: query.Request{
Compiler: lang.SpecCompiler{
Spec: &flux.Spec{
Now: time.Unix(0, 0).UTC(),
},
},
},
Dialect: csv.Dialect{
ResultEncoderConfig: csv.ResultEncoderConfig{
NoHeader: false,
Delimiter: ',',
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := QueryRequest{
Spec: tt.fields.Spec,
AST: tt.fields.AST,
Query: tt.fields.Query,
Type: tt.fields.Type,
Dialect: tt.fields.Dialect,
org: tt.fields.org,
}
got, err := r.proxyRequest(tt.now)
if (err != nil) != tt.wantErr {
t.Errorf("QueryRequest.ProxyRequest() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("QueryRequest.ProxyRequest() = %v, want %v", got, tt.want)
}
})
}
}
func Test_decodeQueryRequest(t *testing.T) {
type args struct {
ctx context.Context
r *http.Request
svc platform.OrganizationService
}
tests := []struct {
name string
args args
want *QueryRequest
wantErr bool
}{
{
name: "valid query request",
args: args{
r: httptest.NewRequest("POST", "/", bytes.NewBufferString(`{"query": "from()"}`)),
svc: &mock.OrganizationService{
FindOrganizationF: func(ctx context.Context, filter platform.OrganizationFilter) (*platform.Organization, error) {
return &platform.Organization{
ID: func() platform.ID { s, _ := platform.IDFromString("deadbeef"); return *s }(),
}, nil
},
},
},
want: &QueryRequest{
Query: "from()",
Type: "flux",
Dialect: QueryDialect{
Delimiter: ",",
DateTimeFormat: "RFC3339",
Header: func(x bool) *bool { return &x }(true),
},
org: &platform.Organization{
ID: func() platform.ID { s, _ := platform.IDFromString("deadbeef"); return *s }(),
},
},
},
{
name: "error decoding json",
args: args{
r: httptest.NewRequest("POST", "/", bytes.NewBufferString(`error`)),
},
wantErr: true,
},
{
name: "error validating query",
args: args{
r: httptest.NewRequest("POST", "/", bytes.NewBufferString(`{}`)),
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := decodeQueryRequest(tt.args.ctx, tt.args.r, tt.args.svc)
if (err != nil) != tt.wantErr {
t.Errorf("decodeQueryRequest() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("decodeQueryRequest() = %v, want %v", got, tt.want)
}
})
}
}
func Test_decodeProxyQueryRequest(t *testing.T) {
type args struct {
ctx context.Context
r *http.Request
auth *platform.Authorization
svc platform.OrganizationService
}
tests := []struct {
name string
args args
want *query.ProxyRequest
wantErr bool
}{
{
name: "valid query request",
args: args{
r: httptest.NewRequest("POST", "/", bytes.NewBufferString(`{"query": "from()"}`)),
svc: &mock.OrganizationService{
FindOrganizationF: func(ctx context.Context, filter platform.OrganizationFilter) (*platform.Organization, error) {
return &platform.Organization{
ID: func() platform.ID { s, _ := platform.IDFromString("deadbeef"); return *s }(),
}, nil
},
},
},
want: &query.ProxyRequest{
Request: query.Request{
OrganizationID: func() platform.ID { s, _ := platform.IDFromString("deadbeef"); return *s }(),
Compiler: lang.FluxCompiler{
Query: "from()",
},
},
Dialect: csv.Dialect{
ResultEncoderConfig: csv.ResultEncoderConfig{
NoHeader: false,
Delimiter: ',',
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := decodeProxyQueryRequest(tt.args.ctx, tt.args.r, tt.args.auth, tt.args.svc)
if (err != nil) != tt.wantErr {
t.Errorf("decodeProxyQueryRequest() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("decodeProxyQueryRequest() = %v, want %v", got, tt.want)
}
})
}
}