influxdb/http/variable_test.go

634 lines
17 KiB
Go
Raw Normal View History

2018-09-11 23:13:15 +00:00
package http
import (
"bytes"
"context"
"fmt"
2018-09-11 23:13:15 +00:00
"io/ioutil"
"net/http"
2018-09-11 23:13:15 +00:00
"net/http/httptest"
"testing"
"go.uber.org/zap"
platform "github.com/influxdata/influxdb"
"github.com/influxdata/influxdb/inmem"
"github.com/influxdata/influxdb/mock"
platformtesting "github.com/influxdata/influxdb/testing"
2018-09-11 23:13:15 +00:00
"github.com/julienschmidt/httprouter"
)
// NewMockVariableBackend returns a VariableBackend with mock services.
func NewMockVariableBackend() *VariableBackend {
return &VariableBackend{
Logger: zap.NewNop().With(zap.String("handler", "variable")),
VariableService: mock.NewVariableService(),
}
}
func TestVariableService_handleGetVariables(t *testing.T) {
2018-09-11 23:13:15 +00:00
type fields struct {
VariableService platform.VariableService
2018-09-11 23:13:15 +00:00
}
type args struct {
queryParams map[string][]string
}
2018-09-11 23:13:15 +00:00
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
fields fields
args args
2018-09-11 23:13:15 +00:00
wants wants
}{
{
name: "get all variables",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
FindVariablesF: func(ctx context.Context, filter platform.VariableFilter, opts ...platform.FindOptions) ([]*platform.Variable, error) {
return []*platform.Variable{
2018-09-11 23:13:15 +00:00
{
ID: platformtesting.MustIDBase16("6162207574726f71"),
OrganizationID: platform.ID(1),
Name: "variable-a",
Selected: []string{"b"},
Arguments: &platform.VariableArguments{
2018-09-11 23:13:15 +00:00
Type: "constant",
Values: platform.VariableConstantValues{"a", "b"},
2018-09-11 23:13:15 +00:00
},
},
{
ID: platformtesting.MustIDBase16("61726920617a696f"),
OrganizationID: platform.ID(1),
Name: "variable-b",
Selected: []string{"c"},
Arguments: &platform.VariableArguments{
2018-09-11 23:13:15 +00:00
Type: "map",
Values: platform.VariableMapValues{"a": "b", "c": "d"},
2018-09-11 23:13:15 +00:00
},
},
}, nil
},
},
},
wants: wants{
statusCode: http.StatusOK,
2018-09-11 23:13:15 +00:00
contentType: "application/json; charset=utf-8",
body: `{"variables":[{"id":"6162207574726f71","orgID":"0000000000000001","name":"variable-a","selected":["b"],"arguments":{"type":"constant","values":["a","b"]},"links":{"self":"/api/v2/variables/6162207574726f71","org": "/api/v2/orgs/0000000000000001"}},{"id":"61726920617a696f","orgID":"0000000000000001","name":"variable-b","selected":["c"],"arguments":{"type":"map","values":{"a":"b","c":"d"}},"links":{"self":"/api/v2/variables/61726920617a696f","org": "/api/v2/orgs/0000000000000001"}}],"links":{"self":"/api/v2/variables?descending=false&limit=20&offset=0"}}`,
2018-09-11 23:13:15 +00:00
},
},
{
name: "get all variables when there are none",
fields: fields{
&mock.VariableService{
FindVariablesF: func(ctx context.Context, filter platform.VariableFilter, opts ...platform.FindOptions) ([]*platform.Variable, error) {
return []*platform.Variable{}, nil
},
},
},
args: args{
map[string][]string{
2019-02-01 06:02:13 +00:00
"limit": {"1"},
},
},
wants: wants{
statusCode: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: `{"links":{"self":"/api/v2/variables?descending=false&limit=1&offset=0"},"variables":[]}`,
},
},
{
name: "get all variables belonging to an org",
fields: fields{
&mock.VariableService{
FindVariablesF: func(ctx context.Context, filter platform.VariableFilter, opts ...platform.FindOptions) ([]*platform.Variable, error) {
return []*platform.Variable{
{
ID: platformtesting.MustIDBase16("6162207574726f71"),
OrganizationID: platformtesting.MustIDBase16("0000000000000001"),
Name: "variable-a",
Selected: []string{"b"},
Arguments: &platform.VariableArguments{
Type: "constant",
Values: platform.VariableConstantValues{"a", "b"},
},
},
}, nil
},
},
},
args: args{
map[string][]string{
2019-02-01 06:02:13 +00:00
"orgID": {"0000000000000001"},
},
},
wants: wants{
statusCode: http.StatusOK,
contentType: "application/json; charset=utf-8",
body: `{"variables":[{"id":"6162207574726f71","orgID":"0000000000000001","name":"variable-a","selected":["b"],"arguments":{"type":"constant","values":["a","b"]},"links":{"self":"/api/v2/variables/6162207574726f71","org":"/api/v2/orgs/0000000000000001"}}],"links":{"self":"/api/v2/variables?descending=false&limit=20&offset=0&orgID=0000000000000001"}}`,
},
},
2018-09-11 23:13:15 +00:00
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
variableBackend := NewMockVariableBackend()
variableBackend.VariableService = tt.fields.VariableService
h := NewVariableHandler(variableBackend)
2018-09-11 23:13:15 +00:00
r := httptest.NewRequest("GET", "http://howdy.tld", nil)
qp := r.URL.Query()
for k, vs := range tt.args.queryParams {
for _, v := range vs {
qp.Add(k, v)
}
}
r.URL.RawQuery = qp.Encode()
2018-09-11 23:13:15 +00:00
w := httptest.NewRecorder()
h.handleGetVariables(w, r)
2018-09-11 23:13:15 +00:00
res := w.Result()
contentType := res.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(res.Body)
2018-09-11 23:13:15 +00:00
if res.StatusCode != tt.wants.statusCode {
t.Errorf("%q. handleGetVariables() = %v, want %v", tt.name, res.StatusCode, tt.wants.statusCode)
2018-09-11 23:13:15 +00:00
}
if contentType != tt.wants.contentType {
t.Errorf("%q. handleGetVariables() = %v, want %v", tt.name, contentType, tt.wants.contentType)
2018-09-11 23:13:15 +00:00
}
if eq, diff, _ := jsonEqual(string(body), tt.wants.body); tt.wants.body != "" && !eq {
t.Errorf("%q. handleGetVariables() = ***%s***", tt.name, diff)
2018-09-11 23:13:15 +00:00
}
})
}
}
func TestVariableService_handleGetVariable(t *testing.T) {
2018-09-11 23:13:15 +00:00
type fields struct {
VariableService platform.VariableService
2018-09-11 23:13:15 +00:00
}
type args struct {
id string
}
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
args args
fields fields
wants wants
}{
{
name: "get a single variable by id",
2018-09-11 23:13:15 +00:00
args: args{
id: "75650d0a636f6d70",
2018-09-11 23:13:15 +00:00
},
fields: fields{
&mock.VariableService{
FindVariableByIDF: func(ctx context.Context, id platform.ID) (*platform.Variable, error) {
return &platform.Variable{
ID: platformtesting.MustIDBase16("75650d0a636f6d70"),
OrganizationID: platform.ID(1),
Name: "variable-a",
Selected: []string{"b"},
Arguments: &platform.VariableArguments{
2018-09-11 23:13:15 +00:00
Type: "constant",
Values: platform.VariableConstantValues{"a", "b"},
2018-09-11 23:13:15 +00:00
},
}, nil
},
},
},
wants: wants{
statusCode: 200,
contentType: "application/json; charset=utf-8",
body: `{"id":"75650d0a636f6d70","orgID":"0000000000000001","name":"variable-a","selected":["b"],"arguments":{"type":"constant","values":["a","b"]},"links":{"self":"/api/v2/variables/75650d0a636f6d70","org":"/api/v2/orgs/0000000000000001"}}
2018-09-11 23:13:15 +00:00
`,
},
},
{
name: "get a non-existant variable",
2018-09-11 23:13:15 +00:00
args: args{
id: "75650d0a636f6d70",
2018-09-11 23:13:15 +00:00
},
fields: fields{
&mock.VariableService{
FindVariableByIDF: func(ctx context.Context, id platform.ID) (*platform.Variable, error) {
return nil, &platform.Error{
Code: platform.ENotFound,
Msg: fmt.Sprintf("variable with ID %v not found", id),
}
2018-09-11 23:13:15 +00:00
},
},
},
wants: wants{
statusCode: 404,
contentType: "application/json; charset=utf-8",
body: `{"code":"not found","message":"variable with ID 75650d0a636f6d70 not found"}`,
2018-09-11 23:13:15 +00:00
},
},
{
name: "request an invalid variable ID",
args: args{
id: "baz",
},
fields: fields{
&mock.VariableService{
FindVariableByIDF: func(ctx context.Context, id platform.ID) (*platform.Variable, error) {
return nil, nil
},
},
},
wants: wants{
statusCode: 400,
2018-12-15 15:33:54 +00:00
contentType: "application/json; charset=utf-8",
body: `{"code":"invalid","message":"id must have a length of 16 bytes"}`,
},
},
2018-09-11 23:13:15 +00:00
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
variableBackend := NewMockVariableBackend()
variableBackend.VariableService = tt.fields.VariableService
h := NewVariableHandler(variableBackend)
2018-09-11 23:13:15 +00:00
r := httptest.NewRequest("GET", "http://howdy.tld", nil)
r = r.WithContext(context.WithValue(
context.TODO(),
httprouter.ParamsKey,
httprouter.Params{
{
Key: "id",
Value: tt.args.id,
},
}))
w := httptest.NewRecorder()
h.handleGetVariable(w, r)
2018-09-11 23:13:15 +00:00
res := w.Result()
contentType := res.Header.Get("Content-Type")
bodyBytes, _ := ioutil.ReadAll(res.Body)
body := string(bodyBytes[:])
if res.StatusCode != tt.wants.statusCode {
t.Errorf("got = %v, want %v", res.StatusCode, tt.wants.statusCode)
}
if contentType != tt.wants.contentType {
t.Errorf("got = %v, want %v", contentType, tt.wants.contentType)
}
if body != tt.wants.body {
t.Errorf("got = %v, want %v", body, tt.wants.body)
}
})
}
}
func TestVariableService_handlePostVariable(t *testing.T) {
2018-09-11 23:13:15 +00:00
type fields struct {
VariableService platform.VariableService
2018-09-11 23:13:15 +00:00
}
type args struct {
variable string
2018-09-11 23:13:15 +00:00
}
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
fields fields
args args
wants wants
}{
{
name: "create a new variable",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
CreateVariableF: func(ctx context.Context, m *platform.Variable) error {
m.ID = platformtesting.MustIDBase16("75650d0a636f6d70")
m.OrganizationID = platform.ID(1)
2018-09-11 23:13:15 +00:00
return nil
},
},
},
args: args{
variable: `
2018-09-11 23:13:15 +00:00
{
"name": "my-great-variable",
"orgID": "0000000000000001",
2018-09-11 23:13:15 +00:00
"arguments": {
"type": "constant",
"values": [
"bar",
"foo"
]
},
"selected": [
"'foo'"
]
}
`,
},
wants: wants{
statusCode: 201,
contentType: "application/json; charset=utf-8",
body: `{"id":"75650d0a636f6d70","orgID":"0000000000000001","name":"my-great-variable","selected":["'foo'"],"arguments":{"type":"constant","values":["bar","foo"]},"links":{"self":"/api/v2/variables/75650d0a636f6d70","org":"/api/v2/orgs/0000000000000001"}}
2018-09-11 23:13:15 +00:00
`,
},
},
{
name: "create a variable with invalid fields",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
CreateVariableF: func(ctx context.Context, m *platform.Variable) error {
m.ID = platformtesting.MustIDBase16("0")
2018-09-11 23:13:15 +00:00
return nil
},
},
},
args: args{
variable: `{"data": "nonsense"}`,
2018-09-11 23:13:15 +00:00
},
wants: wants{
statusCode: 400,
contentType: "application/json; charset=utf-8",
body: `{"code":"invalid","message":"missing variable name"}`,
2018-09-11 23:13:15 +00:00
},
},
{
name: "create a variable with invalid json",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
CreateVariableF: func(ctx context.Context, m *platform.Variable) error {
m.ID = platformtesting.MustIDBase16("0")
2018-09-11 23:13:15 +00:00
return nil
},
},
},
args: args{
variable: `howdy`,
2018-09-11 23:13:15 +00:00
},
wants: wants{
statusCode: 400,
contentType: "application/json; charset=utf-8",
body: `{"code":"invalid","message":"invalid character 'h' looking for beginning of value"}`,
2018-09-11 23:13:15 +00:00
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
variableBackend := NewMockVariableBackend()
variableBackend.VariableService = tt.fields.VariableService
h := NewVariableHandler(variableBackend)
r := httptest.NewRequest("GET", "http://howdy.tld", bytes.NewReader([]byte(tt.args.variable)))
2018-09-11 23:13:15 +00:00
w := httptest.NewRecorder()
h.handlePostVariable(w, r)
2018-09-11 23:13:15 +00:00
res := w.Result()
contentType := res.Header.Get("Content-Type")
bodyBytes, _ := ioutil.ReadAll(res.Body)
body := string(bodyBytes[:])
if res.StatusCode != tt.wants.statusCode {
t.Errorf("got = %v, want %v", res.StatusCode, tt.wants.statusCode)
}
if contentType != tt.wants.contentType {
t.Errorf("got = %v, want %v", contentType, tt.wants.contentType)
}
if body != tt.wants.body {
t.Errorf("got = %v, want %v", body, tt.wants.body)
}
})
}
}
func TestVariableService_handlePatchVariable(t *testing.T) {
2018-09-11 23:13:15 +00:00
type fields struct {
VariableService platform.VariableService
2018-09-11 23:13:15 +00:00
}
type args struct {
id string
update string
}
type wants struct {
statusCode int
contentType string
body string
}
tests := []struct {
name string
fields fields
args args
wants wants
}{
{
name: "update a variable name",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
UpdateVariableF: func(ctx context.Context, id platform.ID, u *platform.VariableUpdate) (*platform.Variable, error) {
return &platform.Variable{
ID: platformtesting.MustIDBase16("75650d0a636f6d70"),
OrganizationID: platform.ID(2),
Name: "new-name",
Arguments: &platform.VariableArguments{
2018-09-11 23:13:15 +00:00
Type: "constant",
Values: platform.VariableConstantValues{},
2018-09-11 23:13:15 +00:00
},
Selected: []string{},
}, nil
},
},
},
args: args{
id: "75650d0a636f6d70",
2018-09-11 23:13:15 +00:00
update: `{"name": "new-name"}`,
},
wants: wants{
statusCode: 200,
contentType: "application/json; charset=utf-8",
body: `{"id":"75650d0a636f6d70","orgID":"0000000000000002","name":"new-name","selected":[],"arguments":{"type":"constant","values":[]},"links":{"self":"/api/v2/variables/75650d0a636f6d70","org":"/api/v2/orgs/0000000000000002"}}
2018-09-11 23:13:15 +00:00
`,
},
},
{
name: "with an empty json body",
fields: fields{
&mock.VariableService{},
2018-09-11 23:13:15 +00:00
},
args: args{
id: "75650d0a636f6d70",
2018-09-11 23:13:15 +00:00
update: `{}`,
},
wants: wants{
statusCode: 400,
contentType: "application/json; charset=utf-8",
body: `{"code":"invalid","message":"no fields supplied in update"}`,
2018-09-11 23:13:15 +00:00
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
variableBackend := NewMockVariableBackend()
variableBackend.VariableService = tt.fields.VariableService
h := NewVariableHandler(variableBackend)
2018-09-11 23:13:15 +00:00
r := httptest.NewRequest("GET", "http://howdy.tld", bytes.NewReader([]byte(tt.args.update)))
r = r.WithContext(context.WithValue(
context.TODO(),
httprouter.ParamsKey,
httprouter.Params{
{
Key: "id",
Value: tt.args.id,
},
}))
w := httptest.NewRecorder()
h.handlePatchVariable(w, r)
2018-09-11 23:13:15 +00:00
res := w.Result()
contentType := res.Header.Get("Content-Type")
bodyBytes, _ := ioutil.ReadAll(res.Body)
body := string(bodyBytes[:])
if res.StatusCode != tt.wants.statusCode {
t.Errorf("got = %v, want %v", res.StatusCode, tt.wants.statusCode)
}
if contentType != tt.wants.contentType {
t.Errorf("got = %v, want %v", contentType, tt.wants.contentType)
}
if body != tt.wants.body {
t.Errorf("got = %v, want %v", body, tt.wants.body)
}
})
}
}
func TestVariableService_handleDeleteVariable(t *testing.T) {
2018-09-11 23:13:15 +00:00
type fields struct {
VariableService platform.VariableService
2018-09-11 23:13:15 +00:00
}
type args struct {
id string
}
type wants struct {
statusCode int
2018-09-11 23:13:15 +00:00
}
tests := []struct {
name string
fields fields
args args
wants wants
}{
{
name: "delete a variable",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
DeleteVariableF: func(ctx context.Context, id platform.ID) error {
2018-09-11 23:13:15 +00:00
return nil
},
},
},
args: args{
id: "75650d0a636f6d70",
2018-09-11 23:13:15 +00:00
},
wants: wants{
statusCode: 204,
},
},
{
name: "delete a non-existant variable",
2018-09-11 23:13:15 +00:00
fields: fields{
&mock.VariableService{
DeleteVariableF: func(ctx context.Context, id platform.ID) error {
return &platform.Error{
Code: platform.ENotFound,
Msg: fmt.Sprintf("variable with ID %v not found", id),
}
2018-09-11 23:13:15 +00:00
},
},
},
args: args{
id: "75650d0a636f6d70",
2018-09-11 23:13:15 +00:00
},
wants: wants{
statusCode: 404,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
variableBackend := NewMockVariableBackend()
variableBackend.VariableService = tt.fields.VariableService
h := NewVariableHandler(variableBackend)
2018-09-11 23:13:15 +00:00
r := httptest.NewRequest("GET", "http://howdy.tld", nil)
r = r.WithContext(context.WithValue(
context.TODO(),
httprouter.ParamsKey,
httprouter.Params{
{
Key: "id",
Value: tt.args.id,
},
}))
w := httptest.NewRecorder()
h.handleDeleteVariable(w, r)
2018-09-11 23:13:15 +00:00
statusCode := w.Result().StatusCode
if statusCode != tt.wants.statusCode {
t.Errorf("got = %v, want %v", statusCode, tt.wants.statusCode)
}
})
}
}
func initVariableService(f platformtesting.VariableFields, t *testing.T) (platform.VariableService, string, func()) {
t.Helper()
svc := inmem.NewService()
svc.IDGenerator = f.IDGenerator
ctx := context.Background()
for _, variable := range f.Variables {
if err := svc.ReplaceVariable(ctx, variable); err != nil {
t.Fatalf("failed to populate variables")
}
}
variableBackend := NewMockVariableBackend()
variableBackend.VariableService = svc
handler := NewVariableHandler(variableBackend)
server := httptest.NewServer(handler)
client := VariableService{
Addr: server.URL,
}
done := server.Close
2018-12-13 14:57:00 +00:00
return &client, inmem.OpPrefix, done
}
func TestVariableService(t *testing.T) {
platformtesting.VariableService(initVariableService, t)
}