Add tests to sources/users endpoints

pull/101/head
Chris Goller 2017-02-21 23:54:24 -06:00
parent c784a6260e
commit 72dbae043f
5 changed files with 896 additions and 8 deletions

43
mocks/sources.go Normal file
View File

@ -0,0 +1,43 @@
package mocks
import (
"context"
"github.com/influxdata/chronograf"
)
var _ chronograf.SourcesStore = &SourcesStore{}
// SourcesStore mock allows all functions to be set for testing
type SourcesStore struct {
AllF func(context.Context) ([]chronograf.Source, error)
AddF func(context.Context, chronograf.Source) (chronograf.Source, error)
DeleteF func(context.Context, chronograf.Source) error
GetF func(ctx context.Context, ID int) (chronograf.Source, error)
UpdateF func(context.Context, chronograf.Source) error
}
// All returns all sources in the store
func (s *SourcesStore) All(ctx context.Context) ([]chronograf.Source, error) {
return s.AllF(ctx)
}
// Add creates a new source in the SourcesStore and returns Source with ID
func (s *SourcesStore) Add(ctx context.Context, src chronograf.Source) (chronograf.Source, error) {
return s.AddF(ctx, src)
}
// Delete the Source from the store
func (s *SourcesStore) Delete(ctx context.Context, src chronograf.Source) error {
return s.DeleteF(ctx, src)
}
// Get retrieves Source if `ID` exists
func (s *SourcesStore) Get(ctx context.Context, ID int) (chronograf.Source, error) {
return s.GetF(ctx, ID)
}
// Update the Source in the store.
func (s *SourcesStore) Update(ctx context.Context, src chronograf.Source) error {
return s.UpdateF(ctx, src)
}

41
mocks/timeseries.go Normal file
View File

@ -0,0 +1,41 @@
package mocks
import (
"context"
"github.com/influxdata/chronograf"
)
var _ chronograf.TimeSeries = &TimeSeries{}
// TimeSeries is a mockable chronograf time series by overriding the functions.
type TimeSeries struct {
// Query retrieves time series data from the database.
QueryF func(context.Context, chronograf.Query) (chronograf.Response, error)
// Connect will connect to the time series using the information in `Source`.
ConnectF func(context.Context, *chronograf.Source) error
// UsersStore represents the user accounts within the TimeSeries database
UsersF func(context.Context) chronograf.UsersStore
// Allowances returns all valid names permissions in this database
AllowancesF func(context.Context) chronograf.Allowances
}
// Query retrieves time series data from the database.
func (t *TimeSeries) Query(ctx context.Context, query chronograf.Query) (chronograf.Response, error) {
return t.QueryF(ctx, query)
}
// Connect will connect to the time series using the information in `Source`.
func (t *TimeSeries) Connect(ctx context.Context, src *chronograf.Source) error {
return t.ConnectF(ctx, src)
}
// Users represents the user accounts within the TimeSeries database
func (t *TimeSeries) Users(ctx context.Context) chronograf.UsersStore {
return t.UsersF(ctx)
}
// Allowances returns all valid names permissions in this database
func (t *TimeSeries) Allowances(ctx context.Context) chronograf.Allowances {
return t.AllowancesF(ctx)
}

View File

@ -6,6 +6,8 @@ import (
"github.com/influxdata/chronograf" "github.com/influxdata/chronograf"
) )
var _ chronograf.UsersStore = &UsersStore{}
// UsersStore mock allows all functions to be set for testing // UsersStore mock allows all functions to be set for testing
type UsersStore struct { type UsersStore struct {
AllF func(context.Context) ([]chronograf.User, error) AllF func(context.Context) ([]chronograf.User, error)

View File

@ -40,8 +40,8 @@ type sourceUser struct {
Links sourceUserLinks `json:"links"` // Links are URI locations related to user Links sourceUserLinks `json:"links"` // Links are URI locations related to user
} }
// NewSourceUser creates a new user in the InfluxDB data source // newSourceUser creates a new user in the InfluxDB data source
func NewSourceUser(srcID int, name string, perms chronograf.Permissions) sourceUser { func newSourceUser(srcID int, name string, perms chronograf.Permissions) sourceUser {
u := &url.URL{Path: name} u := &url.URL{Path: name}
encodedUser := u.String() encodedUser := u.String()
httpAPISrcs := "/chronograf/v1/sources" httpAPISrcs := "/chronograf/v1/sources"
@ -88,7 +88,7 @@ func (h *Service) NewSourceUser(w http.ResponseWriter, r *http.Request) {
return return
} }
su := NewSourceUser(srcID, res.Name, req.Permissions) su := newSourceUser(srcID, res.Name, req.Permissions)
w.Header().Add("Location", su.Links.Self) w.Header().Add("Location", su.Links.Self)
encodeJSON(w, http.StatusCreated, su, h.Logger) encodeJSON(w, http.StatusCreated, su, h.Logger)
} }
@ -114,7 +114,7 @@ func (h *Service) SourceUsers(w http.ResponseWriter, r *http.Request) {
su := []sourceUser{} su := []sourceUser{}
for _, u := range users { for _, u := range users {
su = append(su, NewSourceUser(srcID, u.Name, u.Permissions)) su = append(su, newSourceUser(srcID, u.Name, u.Permissions))
} }
res := sourceUsers{ res := sourceUsers{
@ -140,7 +140,7 @@ func (h *Service) SourceUserID(w http.ResponseWriter, r *http.Request) {
return return
} }
res := NewSourceUser(srcID, u.Name, u.Permissions) res := newSourceUser(srcID, u.Name, u.Permissions)
encodeJSON(w, http.StatusOK, res, h.Logger) encodeJSON(w, http.StatusOK, res, h.Logger)
} }
@ -192,9 +192,9 @@ func (h *Service) UpdateSourceUser(w http.ResponseWriter, r *http.Request) {
return return
} }
su := NewSourceUser(srcID, user.Name, user.Permissions) su := newSourceUser(srcID, user.Name, user.Permissions)
w.Header().Add("Location", su.Links.Self) w.Header().Add("Location", su.Links.Self)
encodeJSON(w, http.StatusCreated, su, h.Logger) encodeJSON(w, http.StatusOK, su, h.Logger)
} }
func (h *Service) sourceUsersStore(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.UsersStore, error) { func (h *Service) sourceUsersStore(ctx context.Context, w http.ResponseWriter, r *http.Request) (int, chronograf.UsersStore, error) {
@ -246,11 +246,16 @@ func (h *Service) Permissions(w http.ResponseWriter, r *http.Request) {
Error(w, http.StatusBadRequest, err.Error(), h.Logger) Error(w, http.StatusBadRequest, err.Error(), h.Logger)
return return
} }
httpAPISrcs := "/chronograf/v1/sources"
res := struct { res := struct {
Permissions chronograf.Allowances `json:"permissions"` Permissions chronograf.Allowances `json:"permissions"`
Links map[string]string `json:"links"` // Links are URI locations related to user
}{ }{
Permissions: perms, Permissions: perms,
Links: map[string]string{
"self": fmt.Sprintf("%s/%d/permissions", httpAPISrcs, srcID),
"source": fmt.Sprintf("%s/%d", httpAPISrcs, srcID),
},
} }
encodeJSON(w, http.StatusOK, res, h.Logger) encodeJSON(w, http.StatusOK, res, h.Logger)
} }

797
server/admin_test.go Normal file
View File

@ -0,0 +1,797 @@
package server_test
import (
"bytes"
"context"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"testing"
"github.com/bouk/httprouter"
"github.com/influxdata/chronograf"
"github.com/influxdata/chronograf/log"
"github.com/influxdata/chronograf/mocks"
"github.com/influxdata/chronograf/server"
)
func TestService_NewSourceUser(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
}
tests := []struct {
name string
fields fields
args args
ID string
wantStatus int
wantContentType string
wantBody string
}{
{
name: "New user for data source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
UsersF: func(ctx context.Context) chronograf.UsersStore {
return &mocks.UsersStore{
AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
return u, nil
},
}
},
},
},
ID: "1",
wantStatus: http.StatusCreated,
wantContentType: "application/json",
wantBody: `{"username":"marty","links":{"self":"/chronograf/v1/sources/1/users/marty"}}
`,
},
{
name: "Error adding user",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
UsersF: func(ctx context.Context) chronograf.UsersStore {
return &mocks.UsersStore{
AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) {
return nil, fmt.Errorf("Weight Has Nothing to Do With It")
},
}
},
},
},
ID: "1",
wantStatus: http.StatusBadRequest,
wantContentType: "application/json",
wantBody: `{"code":400,"message":"Weight Has Nothing to Do With It"}`,
},
{
name: "Failure connecting to user store",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return fmt.Errorf("Biff just happens to be my supervisor")
},
},
},
ID: "1",
wantStatus: http.StatusBadRequest,
wantContentType: "application/json",
wantBody: `{"code":400,"message":"Unable to connect to source 1"}`,
},
{
name: "Failure getting source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{}, fmt.Errorf("No McFly ever amounted to anything in the history of Hill Valley")
},
},
},
ID: "1",
wantStatus: http.StatusNotFound,
wantContentType: "application/json",
wantBody: `{"code":404,"message":"ID 1 not found"}`,
},
{
name: "Bad ID",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
},
ID: "BAD",
wantStatus: http.StatusUnprocessableEntity,
wantContentType: "application/json",
wantBody: `{"code":422,"message":"Error converting ID BAD"}`,
},
{
name: "Bad username",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
},
ID: "BAD",
wantStatus: http.StatusUnprocessableEntity,
wantContentType: "application/json",
wantBody: `{"code":422,"message":"Username required"}`,
},
{
name: "Bad JSON",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{password}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
},
ID: "BAD",
wantStatus: http.StatusBadRequest,
wantContentType: "application/json",
wantBody: `{"code":400,"message":"Unparsable JSON"}`,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.NewSourceUser(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. NewSourceUser() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. NewSourceUser() = %v, want %v", tt.name, content, tt.wantContentType)
}
if tt.wantBody != "" && string(body) != tt.wantBody {
t.Errorf("%q. NewSourceUser() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}
func TestService_SourceUsers(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
}
tests := []struct {
name string
fields fields
args args
ID string
wantStatus int
wantContentType string
wantBody string
}{
{
name: "All users for data source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"GET",
"http://server.local/chronograf/v1/sources/1",
nil),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
UsersF: func(ctx context.Context) chronograf.UsersStore {
return &mocks.UsersStore{
AllF: func(ctx context.Context) ([]chronograf.User, error) {
return []chronograf.User{
{
Name: "strickland",
Passwd: "discipline",
Permissions: chronograf.Permissions{
{
Scope: chronograf.AllScope,
Allowed: chronograf.Allowances{"READ"},
},
},
},
}, nil
},
}
},
},
},
ID: "1",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"users":[{"username":"strickland","permissions":[{"scope":"all","allowed":["READ"]}],"links":{"self":"/chronograf/v1/sources/1/users/strickland"}}]}
`,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.SourceUsers(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. SourceUsers() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. SourceUsers() = %v, want %v", tt.name, content, tt.wantContentType)
}
if tt.wantBody != "" && string(body) != tt.wantBody {
t.Errorf("%q. SourceUsers() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}
func TestService_SourceUserID(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
}
tests := []struct {
name string
fields fields
args args
ID string
UID string
wantStatus int
wantContentType string
wantBody string
}{
{
name: "Single user for data source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"GET",
"http://server.local/chronograf/v1/sources/1",
nil),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
UsersF: func(ctx context.Context) chronograf.UsersStore {
return &mocks.UsersStore{
GetF: func(ctx context.Context, uid string) (*chronograf.User, error) {
return &chronograf.User{
Name: "strickland",
Passwd: "discipline",
Permissions: chronograf.Permissions{
{
Scope: chronograf.AllScope,
Allowed: chronograf.Allowances{"READ"},
},
},
}, nil
},
}
},
},
},
ID: "1",
UID: "strickland",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"username":"strickland","permissions":[{"scope":"all","allowed":["READ"]}],"links":{"self":"/chronograf/v1/sources/1/users/strickland"}}
`,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.SourceUserID(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. SourceUserID() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. SourceUserID() = %v, want %v", tt.name, content, tt.wantContentType)
}
if tt.wantBody != "" && string(body) != tt.wantBody {
t.Errorf("%q. SourceUserID() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}
func TestService_RemoveSourceUser(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
}
tests := []struct {
name string
fields fields
args args
ID string
UID string
wantStatus int
wantContentType string
wantBody string
}{
{
name: "Delete user for data source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"GET",
"http://server.local/chronograf/v1/sources/1",
nil),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
UsersF: func(ctx context.Context) chronograf.UsersStore {
return &mocks.UsersStore{
DeleteF: func(ctx context.Context, u *chronograf.User) error {
return nil
},
}
},
},
},
ID: "1",
UID: "strickland",
wantStatus: http.StatusNoContent,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.RemoveSourceUser(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. RemoveSourceUser() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. RemoveSourceUser() = %v, want %v", tt.name, content, tt.wantContentType)
}
if tt.wantBody != "" && string(body) != tt.wantBody {
t.Errorf("%q. RemoveSourceUser() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}
func TestService_UpdateSourceUser(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
}
tests := []struct {
name string
fields fields
args args
ID string
UID string
wantStatus int
wantContentType string
wantBody string
}{
{
name: "Update user password for data source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
UsersF: func(ctx context.Context) chronograf.UsersStore {
return &mocks.UsersStore{
UpdateF: func(ctx context.Context, u *chronograf.User) error {
return nil
},
}
},
},
},
ID: "1",
UID: "marty",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"username":"marty","links":{"self":"/chronograf/v1/sources/1/users/marty"}}
`,
},
{
name: "Invalid update JSON",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
},
ID: "1",
UID: "marty",
wantStatus: http.StatusUnprocessableEntity,
wantContentType: "application/json",
wantBody: `{"code":422,"message":"No fields to update"}`,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
{
Key: "uid",
Value: tt.UID,
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.UpdateSourceUser(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. UpdateSourceUser() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. UpdateSourceUser() = %v, want %v", tt.name, content, tt.wantContentType)
}
if tt.wantBody != "" && string(body) != tt.wantBody {
t.Errorf("%q. UpdateSourceUser() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}
func TestService_Permissions(t *testing.T) {
type fields struct {
SourcesStore chronograf.SourcesStore
TimeSeries chronograf.TimeSeries
Logger chronograf.Logger
UseAuth bool
}
type args struct {
w *httptest.ResponseRecorder
r *http.Request
}
tests := []struct {
name string
fields fields
args args
ID string
wantStatus int
wantContentType string
wantBody string
}{
{
name: "New user for data source",
args: args{
w: httptest.NewRecorder(),
r: httptest.NewRequest(
"POST",
"http://server.local/chronograf/v1/sources/1",
ioutil.NopCloser(
bytes.NewReader([]byte(`{"username": "marty", "password": "the_lake"}`)))),
},
fields: fields{
UseAuth: true,
Logger: log.New(log.DebugLevel),
SourcesStore: &mocks.SourcesStore{
GetF: func(ctx context.Context, ID int) (chronograf.Source, error) {
return chronograf.Source{
ID: 1,
Name: "muh source",
Username: "username",
Password: "hunter2",
URL: "http://localhost:8086",
}, nil
},
},
TimeSeries: &mocks.TimeSeries{
ConnectF: func(ctx context.Context, src *chronograf.Source) error {
return nil
},
AllowancesF: func(ctx context.Context) chronograf.Allowances {
return chronograf.Allowances{"READ", "WRITE"}
},
},
},
ID: "1",
wantStatus: http.StatusOK,
wantContentType: "application/json",
wantBody: `{"permissions":["READ","WRITE"],"links":{"self":"/chronograf/v1/sources/1/permissions","source":"/chronograf/v1/sources/1"}}
`,
},
}
for _, tt := range tests {
tt.args.r = tt.args.r.WithContext(httprouter.WithParams(
context.Background(),
httprouter.Params{
{
Key: "id",
Value: tt.ID,
},
}))
h := &server.Service{
SourcesStore: tt.fields.SourcesStore,
TimeSeries: tt.fields.TimeSeries,
Logger: tt.fields.Logger,
UseAuth: tt.fields.UseAuth,
}
h.Permissions(tt.args.w, tt.args.r)
resp := tt.args.w.Result()
content := resp.Header.Get("Content-Type")
body, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode != tt.wantStatus {
t.Errorf("%q. Permissions() = %v, want %v", tt.name, resp.StatusCode, tt.wantStatus)
}
if tt.wantContentType != "" && content != tt.wantContentType {
t.Errorf("%q. Permissions() = %v, want %v", tt.name, content, tt.wantContentType)
}
if tt.wantBody != "" && string(body) != tt.wantBody {
t.Errorf("%q. Permissions() = \n***%v***\n,\nwant\n***%v***", tt.name, string(body), tt.wantBody)
}
}
}