package organizations_test import ( "context" "testing" "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/influxdata/chronograf" "github.com/influxdata/chronograf/mocks" "github.com/influxdata/chronograf/organizations" ) // IgnoreFields is used because ID cannot be predicted reliably // EquateEmpty is used because we want nil slices, arrays, and maps to be equal to the empty map var userCmpOptions = cmp.Options{ cmpopts.IgnoreFields(chronograf.User{}, "ID"), cmpopts.EquateEmpty(), } func TestUsersStore_Get(t *testing.T) { type fields struct { UsersStore chronograf.UsersStore } type args struct { ctx context.Context usr *chronograf.User userID uint64 orgID string } tests := []struct { name string fields fields args args want *chronograf.User wantErr bool }{ { name: "Get user with no role in organization", fields: fields{ UsersStore: &mocks.UsersStore{ GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "billietta", Provider: "google", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "The HillBilliettas", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), userID: 1234, orgID: "1336", }, wantErr: true, }, { name: "Get user no organization set", fields: fields{ UsersStore: &mocks.UsersStore{ GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "billietta", Provider: "google", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "The HillBilliettas", }, }, }, nil }, }, }, args: args{ userID: 1234, ctx: context.Background(), }, wantErr: true, }, { name: "Get user scoped to an organization", fields: fields{ UsersStore: &mocks.UsersStore{ GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "billietta", Provider: "google", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "The HillBilliettas", }, { Organization: "1336", Name: "The BillHilliettos", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), userID: 1234, orgID: "1336", }, want: &chronograf.User{ Name: "billietta", Provider: "google", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1336", Name: "The BillHilliettos", }, }, }, }, } for _, tt := range tests { s := organizations.NewUsersStore(tt.fields.UsersStore, tt.args.orgID) tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.orgID) got, err := s.Get(tt.args.ctx, chronograf.UserQuery{ID: &tt.args.userID}) if (err != nil) != tt.wantErr { t.Errorf("%q. UsersStore.Get() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue } if diff := cmp.Diff(got, tt.want, userCmpOptions...); diff != "" { t.Errorf("%q. UsersStore.Get():\n-got/+want\ndiff %s", tt.name, diff) } } } func TestUsersStore_Add(t *testing.T) { type fields struct { UsersStore chronograf.UsersStore } type args struct { ctx context.Context u *chronograf.User orgID string uInitial *chronograf.User } tests := []struct { name string fields fields args args want *chronograf.User wantErr bool }{ { name: "Add new user - no org", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, }, }, args: args{ ctx: context.Background(), u: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1336", Name: "editor", }, }, }, }, wantErr: true, }, { name: "Add new user", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return nil, chronograf.ErrUserNotFound }, }, }, args: args{ ctx: context.Background(), u: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1336", Name: "editor", }, }, }, orgID: "1336", }, want: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1336", Name: "editor", }, }, }, }, { name: "Add non-new user without Role", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{}, }, nil }, }, }, args: args{ ctx: context.Background(), u: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{}, }, orgID: "1336", }, want: &chronograf.User{ Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{}, }, }, { name: "Add non-new user with Role", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), u: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1336", Name: "admin", }, }, }, orgID: "1336", }, want: &chronograf.User{ Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1336", Name: "admin", }, }, }, }, { name: "Add non-new user with Role. Stored user is not super admin. Provided user is super admin", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", SuperAdmin: false, Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), u: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", SuperAdmin: true, Roles: []chronograf.Role{ { Organization: "1336", Name: "admin", }, }, }, orgID: "1336", }, want: &chronograf.User{ Name: "docbrown", Provider: "github", Scheme: "oauth2", SuperAdmin: true, Roles: []chronograf.Role{ { Organization: "1336", Name: "admin", }, }, }, }, { name: "Add user that already exists", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), u: &chronograf.User{ ID: 1234, Name: "docbrown", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "admin", }, }, }, orgID: "1337", }, wantErr: true, }, { name: "Has invalid Role: missing Organization", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return nil, nil }, }, }, args: args{ ctx: context.Background(), orgID: "1338", u: &chronograf.User{ Name: "henrietta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Name: "editor", }, }, }, }, wantErr: true, }, { name: "Has invalid Role: missing Name", fields: fields{ UsersStore: &mocks.UsersStore{ AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { return u, nil }, UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return nil, nil }, }, }, args: args{ ctx: context.Background(), orgID: "1337", u: &chronograf.User{ Name: "henrietta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", }, }, }, }, wantErr: true, }, { name: "Has invalid Organization", fields: fields{ UsersStore: &mocks.UsersStore{}, }, args: args{ ctx: context.Background(), u: &chronograf.User{ Name: "henrietta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ chronograf.Role{}, }, }, orgID: "1337", }, wantErr: true, }, { name: "Organization does not match orgID", fields: fields{ UsersStore: &mocks.UsersStore{}, }, args: args{ ctx: context.Background(), u: &chronograf.User{ Name: "henrietta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, orgID: "1337", }, wantErr: true, }, { name: "Role Name not specified", args: args{ ctx: context.Background(), u: &chronograf.User{ Name: "henrietta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", }, }, }, orgID: "1337", }, wantErr: true, }, } for _, tt := range tests { tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.orgID) s := organizations.NewUsersStore(tt.fields.UsersStore, tt.args.orgID) got, err := s.Add(tt.args.ctx, tt.args.u) if (err != nil) != tt.wantErr { t.Errorf("%q. UsersStore.Add() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue } if got == nil && tt.want == nil { continue } if diff := cmp.Diff(got, tt.want, userCmpOptions...); diff != "" { t.Errorf("%q. UsersStore.Add():\n-got/+want\ndiff %s", tt.name, diff) } } } func TestUsersStore_Delete(t *testing.T) { type fields struct { UsersStore chronograf.UsersStore } type args struct { ctx context.Context user *chronograf.User orgID string } tests := []struct { name string fields fields args args wantErr bool wantRaw *chronograf.User }{ { name: "No such user", fields: fields{ UsersStore: &mocks.UsersStore{ //AddF: func(ctx context.Context, u *chronograf.User) (*chronograf.User, error) { // return u, nil //}, //UpdateF: func(ctx context.Context, u *chronograf.User) error { // return nil //}, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return nil, chronograf.ErrUserNotFound }, }, }, args: args{ ctx: context.Background(), user: &chronograf.User{ ID: 10, }, orgID: "1336", }, wantErr: true, }, { name: "Derlete user", fields: fields{ UsersStore: &mocks.UsersStore{ UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ ID: 1234, Name: "noone", Roles: []chronograf.Role{ { Organization: "1338", Name: "The BillHilliettas", }, { Organization: "1336", Name: "The HillBilliettas", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), user: &chronograf.User{ ID: 1234, Name: "noone", Roles: []chronograf.Role{ { Organization: "1338", Name: "The BillHilliettas", }, { Organization: "1336", Name: "The HillBilliettas", }, }, }, orgID: "1336", }, }, } for _, tt := range tests { tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.orgID) s := organizations.NewUsersStore(tt.fields.UsersStore, tt.args.orgID) if err := s.Delete(tt.args.ctx, tt.args.user); (err != nil) != tt.wantErr { t.Errorf("%q. UsersStore.Delete() error = %v, wantErr %v", tt.name, err, tt.wantErr) } } } func TestUsersStore_Update(t *testing.T) { type fields struct { UsersStore chronograf.UsersStore } type args struct { ctx context.Context usr *chronograf.User roles []chronograf.Role superAdmin bool orgID string } tests := []struct { name string fields fields args args want *chronograf.User wantRaw *chronograf.User wantErr bool }{ { name: "No such user", fields: fields{ UsersStore: &mocks.UsersStore{ GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return nil, chronograf.ErrUserNotFound }, }, }, args: args{ ctx: context.Background(), usr: &chronograf.User{ ID: 10, }, orgID: "1338", }, wantErr: true, }, { name: "Update user role", fields: fields{ UsersStore: &mocks.UsersStore{ UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ Name: "bobetta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "viewer", }, { Organization: "1338", Name: "editor", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), usr: &chronograf.User{ Name: "bobetta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{}, }, roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, orgID: "1338", }, want: &chronograf.User{ Name: "bobetta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, }, { name: "Update user super admin", fields: fields{ UsersStore: &mocks.UsersStore{ UpdateF: func(ctx context.Context, u *chronograf.User) error { return nil }, GetF: func(ctx context.Context, q chronograf.UserQuery) (*chronograf.User, error) { return &chronograf.User{ Name: "bobetta", Provider: "github", Scheme: "oauth2", SuperAdmin: false, Roles: []chronograf.Role{ { Organization: "1337", Name: "viewer", }, { Organization: "1338", Name: "editor", }, }, }, nil }, }, }, args: args{ ctx: context.Background(), usr: &chronograf.User{ Name: "bobetta", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{}, }, superAdmin: true, orgID: "1338", }, want: &chronograf.User{ Name: "bobetta", Provider: "github", Scheme: "oauth2", SuperAdmin: true, }, }, } for _, tt := range tests { tt.args.ctx = context.WithValue(tt.args.ctx, organizations.ContextKey, tt.args.orgID) s := organizations.NewUsersStore(tt.fields.UsersStore, tt.args.orgID) if tt.args.roles != nil { tt.args.usr.Roles = tt.args.roles } if tt.args.superAdmin { tt.args.usr.SuperAdmin = tt.args.superAdmin } if err := s.Update(tt.args.ctx, tt.args.usr); (err != nil) != tt.wantErr { t.Errorf("%q. UsersStore.Update() error = %v, wantErr %v", tt.name, err, tt.wantErr) } // for the empty test if tt.want == nil { continue } if diff := cmp.Diff(tt.args.usr, tt.want, userCmpOptions...); diff != "" { t.Errorf("%q. UsersStore.Update():\n-got/+want\ndiff %s", tt.name, diff) } } } func TestUsersStore_All(t *testing.T) { type fields struct { UsersStore chronograf.UsersStore } tests := []struct { name string fields fields ctx context.Context want []chronograf.User wantRaw []chronograf.User orgID string wantErr bool }{ { name: "No users", fields: fields{ UsersStore: &mocks.UsersStore{ AllF: func(ctx context.Context) ([]chronograf.User, error) { return []chronograf.User{ { Name: "howdy", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "viewer", }, { Organization: "1336", Name: "viewer", }, }, }, { Name: "doody2", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, { Name: "doody", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, }, nil }, }, }, ctx: context.Background(), orgID: "2330", }, { name: "get all users", orgID: "1338", fields: fields{ UsersStore: &mocks.UsersStore{ AllF: func(ctx context.Context) ([]chronograf.User, error) { return []chronograf.User{ { Name: "howdy", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "viewer", }, { Organization: "1336", Name: "viewer", }, }, }, { Name: "doody2", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, { Name: "doody", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, }, nil }, }, }, ctx: context.Background(), want: []chronograf.User{ { Name: "howdy", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "viewer", }, }, }, { Name: "doody", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, }, }, } for _, tt := range tests { tt.ctx = context.WithValue(tt.ctx, organizations.ContextKey, tt.orgID) for _, u := range tt.wantRaw { tt.fields.UsersStore.Add(tt.ctx, &u) } s := organizations.NewUsersStore(tt.fields.UsersStore, tt.orgID) gots, err := s.All(tt.ctx) if (err != nil) != tt.wantErr { t.Errorf("%q. UsersStore.All() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue } if diff := cmp.Diff(gots, tt.want, userCmpOptions...); diff != "" { t.Errorf("%q. UsersStore.All():\n-got/+want\ndiff %s", tt.name, diff) } } } func TestUsersStore_Num(t *testing.T) { type fields struct { UsersStore chronograf.UsersStore } tests := []struct { name string fields fields ctx context.Context orgID string want int wantErr bool }{ { name: "No users", fields: fields{ UsersStore: &mocks.UsersStore{ AllF: func(ctx context.Context) ([]chronograf.User, error) { return []chronograf.User{ { Name: "howdy", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "viewer", }, { Organization: "1336", Name: "viewer", }, }, }, { Name: "doody2", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, { Name: "doody", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, }, nil }, }, }, ctx: context.Background(), orgID: "2330", }, { name: "get all users", orgID: "1338", fields: fields{ UsersStore: &mocks.UsersStore{ AllF: func(ctx context.Context) ([]chronograf.User, error) { return []chronograf.User{ { Name: "howdy", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "viewer", }, { Organization: "1336", Name: "viewer", }, }, }, { Name: "doody2", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1337", Name: "editor", }, }, }, { Name: "doody", Provider: "github", Scheme: "oauth2", Roles: []chronograf.Role{ { Organization: "1338", Name: "editor", }, }, }, }, nil }, }, }, ctx: context.Background(), want: 2, }, } for _, tt := range tests { tt.ctx = context.WithValue(tt.ctx, organizations.ContextKey, tt.orgID) s := organizations.NewUsersStore(tt.fields.UsersStore, tt.orgID) got, err := s.Num(tt.ctx) if (err != nil) != tt.wantErr { t.Errorf("%q. UsersStore.Num() error = %v, wantErr %v", tt.name, err, tt.wantErr) continue } if got != tt.want { t.Errorf("%q. UsersStore.Num() = %d. want %d", tt.name, got, tt.want) } } }