Graph test coverage (#5136)

* Add unit tests for users.go

* Do not choke on broken spaces

* Generate mocks for the RoleService

* Increase test coverage

* Increase test coverage
This commit is contained in:
Andre Duffeck
2022-11-29 10:22:40 +01:00
committed by GitHub
parent e07c7bc808
commit 0dc6eaf466
7 changed files with 715 additions and 5 deletions

View File

@@ -29,6 +29,7 @@ ci-go-generate: $(MOCKERY) # CI runs ci-node-generate automatically before this
$(MOCKERY) --dir pkg/service/v0 --case underscore --name HTTPClient
$(MOCKERY) --dir pkg/service/v0 --case underscore --name Publisher
$(MOCKERY) --dir pkg/service/v0 --case underscore --name Permissions
$(MOCKERY) --dir pkg/service/v0 --case underscore --name RoleService
$(MOCKERY) --dir pkg/identity --output pkg/identity/mocks --case underscore --name Backend
$(MOCKERY) --srcpkg github.com/go-ldap/ldap/v3 --case underscore --filename ldapclient.go --name Client

View File

@@ -1,4 +1,4 @@
// Code generated by mockery v2.10.4. DO NOT EDIT.
// Code generated by mockery v2.14.1. DO NOT EDIT.
package mocks
@@ -76,3 +76,18 @@ func (_m *Permissions) ListPermissionsByResource(ctx context.Context, in *v0.Lis
return r0, r1
}
type mockConstructorTestingTNewPermissions interface {
mock.TestingT
Cleanup(func())
}
// NewPermissions creates a new instance of Permissions. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewPermissions(t mockConstructorTestingTNewPermissions) *Permissions {
mock := &Permissions{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -0,0 +1,155 @@
// Code generated by mockery v2.14.1. DO NOT EDIT.
package mocks
import (
context "context"
client "go-micro.dev/v4/client"
emptypb "google.golang.org/protobuf/types/known/emptypb"
mock "github.com/stretchr/testify/mock"
v0 "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
)
// RoleService is an autogenerated mock type for the RoleService type
type RoleService struct {
mock.Mock
}
// AssignRoleToUser provides a mock function with given fields: ctx, in, opts
func (_m *RoleService) AssignRoleToUser(ctx context.Context, in *v0.AssignRoleToUserRequest, opts ...client.CallOption) (*v0.AssignRoleToUserResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *v0.AssignRoleToUserResponse
if rf, ok := ret.Get(0).(func(context.Context, *v0.AssignRoleToUserRequest, ...client.CallOption) *v0.AssignRoleToUserResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v0.AssignRoleToUserResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *v0.AssignRoleToUserRequest, ...client.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListRoleAssignments provides a mock function with given fields: ctx, in, opts
func (_m *RoleService) ListRoleAssignments(ctx context.Context, in *v0.ListRoleAssignmentsRequest, opts ...client.CallOption) (*v0.ListRoleAssignmentsResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *v0.ListRoleAssignmentsResponse
if rf, ok := ret.Get(0).(func(context.Context, *v0.ListRoleAssignmentsRequest, ...client.CallOption) *v0.ListRoleAssignmentsResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v0.ListRoleAssignmentsResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *v0.ListRoleAssignmentsRequest, ...client.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// ListRoles provides a mock function with given fields: ctx, in, opts
func (_m *RoleService) ListRoles(ctx context.Context, in *v0.ListBundlesRequest, opts ...client.CallOption) (*v0.ListBundlesResponse, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *v0.ListBundlesResponse
if rf, ok := ret.Get(0).(func(context.Context, *v0.ListBundlesRequest, ...client.CallOption) *v0.ListBundlesResponse); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*v0.ListBundlesResponse)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *v0.ListBundlesRequest, ...client.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
// RemoveRoleFromUser provides a mock function with given fields: ctx, in, opts
func (_m *RoleService) RemoveRoleFromUser(ctx context.Context, in *v0.RemoveRoleFromUserRequest, opts ...client.CallOption) (*emptypb.Empty, error) {
_va := make([]interface{}, len(opts))
for _i := range opts {
_va[_i] = opts[_i]
}
var _ca []interface{}
_ca = append(_ca, ctx, in)
_ca = append(_ca, _va...)
ret := _m.Called(_ca...)
var r0 *emptypb.Empty
if rf, ok := ret.Get(0).(func(context.Context, *v0.RemoveRoleFromUserRequest, ...client.CallOption) *emptypb.Empty); ok {
r0 = rf(ctx, in, opts...)
} else {
if ret.Get(0) != nil {
r0 = ret.Get(0).(*emptypb.Empty)
}
}
var r1 error
if rf, ok := ret.Get(1).(func(context.Context, *v0.RemoveRoleFromUserRequest, ...client.CallOption) error); ok {
r1 = rf(ctx, in, opts...)
} else {
r1 = ret.Error(1)
}
return r0, r1
}
type mockConstructorTestingTNewRoleService interface {
mock.TestingT
Cleanup(func())
}
// NewRoleService creates a new instance of RoleService. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewRoleService(t mockConstructorTestingTNewRoleService) *RoleService {
mock := &RoleService{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@@ -18,6 +18,7 @@ import (
"go-micro.dev/v4/client"
mevents "go-micro.dev/v4/events"
"google.golang.org/grpc"
"google.golang.org/protobuf/types/known/emptypb"
)
//go:generate make -C ../../.. generate
@@ -77,6 +78,14 @@ type HTTPClient interface {
// GetGatewayServiceClientFunc is a callback used to pass in a mock during testing
type GetGatewayServiceClientFunc func() (GatewayClient, error)
// RoleService is the interface used to access the role service
type RoleService interface {
ListRoles(ctx context.Context, in *settingssvc.ListBundlesRequest, opts ...client.CallOption) (*settingssvc.ListBundlesResponse, error)
ListRoleAssignments(ctx context.Context, in *settingssvc.ListRoleAssignmentsRequest, opts ...client.CallOption) (*settingssvc.ListRoleAssignmentsResponse, error)
AssignRoleToUser(ctx context.Context, in *settingssvc.AssignRoleToUserRequest, opts ...client.CallOption) (*settingssvc.AssignRoleToUserResponse, error)
RemoveRoleFromUser(ctx context.Context, in *settingssvc.RemoveRoleFromUserRequest, opts ...client.CallOption) (*emptypb.Empty, error)
}
// Graph defines implements the business logic for Service.
type Graph struct {
config *config.Config
@@ -84,7 +93,7 @@ type Graph struct {
logger *log.Logger
identityBackend identity.Backend
gatewayClient GatewayClient
roleService settingssvc.RoleService
roleService RoleService
permissionsService Permissions
spacePropertiesCache *ttlcache.Cache
eventsPublisher events.Publisher

View File

@@ -21,7 +21,7 @@ type Options struct {
Middleware []func(http.Handler) http.Handler
GatewayClient GatewayClient
IdentityBackend identity.Backend
RoleService settingssvc.RoleService
RoleService RoleService
PermissionService Permissions
RoleManager *roles.Manager
EventsPublisher events.Publisher
@@ -73,8 +73,8 @@ func WithIdentityBackend(val identity.Backend) Option {
}
}
// RoleService provides a function to set the RoleService option.
func RoleService(val settingssvc.RoleService) Option {
// WithRoleService provides a function to set the RoleService option.
func WithRoleService(val RoleService) Option {
return func(o *Options) {
o.RoleService = val
}

View File

@@ -257,6 +257,7 @@ func (g Graph) GetUser(w http.ResponseWriter, r *http.Request) {
d, err := g.cs3StorageSpaceToDrive(r.Context(), wdu, sp)
if err != nil {
logger.Debug().Err(err).Interface("id", sp.Id).Msg("error converting space to drive")
continue
}
quota, err := g.getDriveQuota(r.Context(), sp)
if err != nil {

View File

@@ -0,0 +1,529 @@
package svc_test
import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"net/http"
"net/http/httptest"
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
typesv1beta1 "github.com/cs3org/go-cs3apis/cs3/types/v1beta1"
ctxpkg "github.com/cs3org/reva/v2/pkg/ctx"
"github.com/cs3org/reva/v2/pkg/rgrpc/status"
"github.com/go-chi/chi/v5"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/stretchr/testify/mock"
libregraph "github.com/owncloud/libre-graph-api-go"
ogrpc "github.com/owncloud/ocis/v2/ocis-pkg/service/grpc"
"github.com/owncloud/ocis/v2/ocis-pkg/shared"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
"github.com/owncloud/ocis/v2/services/graph/mocks"
"github.com/owncloud/ocis/v2/services/graph/pkg/config"
"github.com/owncloud/ocis/v2/services/graph/pkg/config/defaults"
identitymocks "github.com/owncloud/ocis/v2/services/graph/pkg/identity/mocks"
service "github.com/owncloud/ocis/v2/services/graph/pkg/service/v0"
)
type userList struct {
Value []*libregraph.User
}
var _ = Describe("Users", func() {
var (
svc service.Service
ctx context.Context
cfg *config.Config
gatewayClient *mocks.GatewayClient
eventsPublisher mocks.Publisher
roleService *mocks.RoleService
identityBackend *identitymocks.Backend
rr *httptest.ResponseRecorder
currentUser = &userv1beta1.User{
Id: &userv1beta1.UserId{
OpaqueId: "user",
},
}
)
BeforeEach(func() {
eventsPublisher.On("Publish", mock.Anything, mock.Anything, mock.Anything).Return(nil)
identityBackend = &identitymocks.Backend{}
roleService = &mocks.RoleService{}
gatewayClient = &mocks.GatewayClient{}
rr = httptest.NewRecorder()
ctx = context.Background()
cfg = defaults.FullDefaultConfig()
cfg.Identity.LDAP.CACert = "" // skip the startup checks, we don't use LDAP at all in this tests
cfg.TokenManager.JWTSecret = "loremipsum"
cfg.Commons = &shared.Commons{}
cfg.GRPCClientTLS = &shared.GRPCClientTLS{}
_ = ogrpc.Configure(ogrpc.GetClientOptions(cfg.GRPCClientTLS)...)
svc = service.NewService(
service.Config(cfg),
service.WithGatewayClient(gatewayClient),
service.EventsPublisher(&eventsPublisher),
service.WithIdentityBackend(identityBackend),
service.WithRoleService(roleService),
)
})
Describe("GetMe", func() {
It("handles missing user", func() {
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me", nil)
svc.GetMe(rr, r)
Expect(rr.Code).To(Equal(http.StatusInternalServerError))
})
It("gets the information", func() {
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me", nil)
r = r.WithContext(ctxpkg.ContextSetUser(ctx, currentUser))
svc.GetMe(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
})
It("expands the user", func() {
user := &libregraph.User{}
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me?$expand=memberOf", nil)
r = r.WithContext(ctxpkg.ContextSetUser(ctx, currentUser))
svc.GetMe(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
})
})
Describe("GetUsers", func() {
It("handles invalid requests", func() {
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users?$invalid=true", nil)
svc.GetUsers(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("lists the users", func() {
user := &libregraph.User{}
user.SetId("user1")
users := []*libregraph.User{user}
identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users", nil)
svc.GetUsers(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
data, err := ioutil.ReadAll(rr.Body)
Expect(err).ToNot(HaveOccurred())
res := userList{}
err = json.Unmarshal(data, &res)
Expect(err).ToNot(HaveOccurred())
Expect(len(res.Value)).To(Equal(1))
Expect(res.Value[0].GetId()).To(Equal("user1"))
})
It("sorts", func() {
user := &libregraph.User{}
user.SetId("user1")
user.SetMail("z@example.com")
user.SetDisplayName("9")
user.SetOnPremisesSamAccountName("9")
user2 := &libregraph.User{}
user2.SetId("user2")
user2.SetMail("a@example.com")
user2.SetDisplayName("1")
user2.SetOnPremisesSamAccountName("1")
users := []*libregraph.User{user, user2}
identityBackend.On("GetUsers", mock.Anything, mock.Anything, mock.Anything).Return(users, nil)
getUsers := func(path string) []*libregraph.User {
r := httptest.NewRequest(http.MethodGet, path, nil)
rec := httptest.NewRecorder()
svc.GetUsers(rec, r)
Expect(rec.Code).To(Equal(http.StatusOK))
data, err := ioutil.ReadAll(rec.Body)
Expect(err).ToNot(HaveOccurred())
res := userList{}
err = json.Unmarshal(data, &res)
Expect(err).ToNot(HaveOccurred())
return res.Value
}
unsorted := getUsers("/graph/v1.0/me/users")
Expect(len(unsorted)).To(Equal(2))
Expect(unsorted[0].GetId()).To(Equal("user1"))
Expect(unsorted[1].GetId()).To(Equal("user2"))
byMail := getUsers("/graph/v1.0/me/users?$orderby=mail")
Expect(len(byMail)).To(Equal(2))
Expect(byMail[0].GetId()).To(Equal("user2"))
Expect(byMail[1].GetId()).To(Equal("user1"))
byMail = getUsers("/graph/v1.0/me/users?$orderby=mail%20asc")
Expect(len(byMail)).To(Equal(2))
Expect(byMail[0].GetId()).To(Equal("user2"))
Expect(byMail[1].GetId()).To(Equal("user1"))
byMail = getUsers("/graph/v1.0/me/users?$orderby=mail%20desc")
Expect(len(byMail)).To(Equal(2))
Expect(byMail[0].GetId()).To(Equal("user1"))
Expect(byMail[1].GetId()).To(Equal("user2"))
byDisplayName := getUsers("/graph/v1.0/me/users?$orderby=displayName")
Expect(len(byDisplayName)).To(Equal(2))
Expect(byDisplayName[0].GetId()).To(Equal("user2"))
Expect(byDisplayName[1].GetId()).To(Equal("user1"))
byDisplayName = getUsers("/graph/v1.0/me/users?$orderby=displayName%20asc")
Expect(len(byDisplayName)).To(Equal(2))
Expect(byDisplayName[0].GetId()).To(Equal("user2"))
Expect(byDisplayName[1].GetId()).To(Equal("user1"))
byDisplayName = getUsers("/graph/v1.0/me/users?$orderby=displayName%20desc")
Expect(len(byDisplayName)).To(Equal(2))
Expect(byDisplayName[0].GetId()).To(Equal("user1"))
Expect(byDisplayName[1].GetId()).To(Equal("user2"))
byOnPremisesSamAccountName := getUsers("/graph/v1.0/me/users?$orderby=onPremisesSamAccountName")
Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
byOnPremisesSamAccountName = getUsers("/graph/v1.0/me/users?$orderby=onPremisesSamAccountName%20asc")
Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user2"))
Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user1"))
byOnPremisesSamAccountName = getUsers("/graph/v1.0/me/users?$orderby=onPremisesSamAccountName%20desc")
Expect(len(byOnPremisesSamAccountName)).To(Equal(2))
Expect(byOnPremisesSamAccountName[0].GetId()).To(Equal("user1"))
Expect(byOnPremisesSamAccountName[1].GetId()).To(Equal("user2"))
// Handles invalid sort field
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users?$orderby=invalid", nil)
svc.GetUsers(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
})
Describe("GetUser", func() {
It("handles missing userids", func() {
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users", nil)
svc.GetUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("gets the user", func() {
user := &libregraph.User{}
user.SetId("user1")
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", *user.Id)
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.GetUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
data, err := ioutil.ReadAll(rr.Body)
Expect(err).ToNot(HaveOccurred())
responseUser := &libregraph.User{}
err = json.Unmarshal(data, &responseUser)
Expect(err).ToNot(HaveOccurred())
Expect(responseUser.GetId()).To(Equal("user1"))
Expect(len(responseUser.GetDrives())).To(Equal(0))
})
It("includes the personal space if requested", func() {
user := &libregraph.User{}
user.SetId("user1")
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
Status: status.NewOK(ctx),
TotalBytes: 10,
}, nil)
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
Status: status.NewOK(ctx),
StorageSpaces: []*provider.StorageSpace{
{
Id: &provider.StorageSpaceId{OpaqueId: "drive1"},
Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
SpaceType: "project",
},
{
Id: &provider.StorageSpaceId{OpaqueId: "personal"},
Root: &provider.ResourceId{SpaceId: "personal", OpaqueId: "personal"},
SpaceType: "personal",
},
},
}, nil)
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users?$expand=drive", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", *user.Id)
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.GetUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
data, err := ioutil.ReadAll(rr.Body)
Expect(err).ToNot(HaveOccurred())
responseUser := &libregraph.User{}
err = json.Unmarshal(data, &responseUser)
Expect(err).ToNot(HaveOccurred())
Expect(responseUser.GetId()).To(Equal("user1"))
Expect(*responseUser.GetDrive().Id).To(Equal("personal"))
})
It("includes the drives if requested", func() {
user := &libregraph.User{}
user.SetId("user1")
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(user, nil)
gatewayClient.On("GetQuota", mock.Anything, mock.Anything, mock.Anything).Return(&provider.GetQuotaResponse{
Status: status.NewOK(ctx),
TotalBytes: 10,
}, nil)
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
Status: status.NewOK(ctx),
StorageSpaces: []*provider.StorageSpace{
{
Id: &provider.StorageSpaceId{OpaqueId: "drive1"},
Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
},
},
}, nil)
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/me/users?$expand=drives", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", *user.Id)
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.GetUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
data, err := ioutil.ReadAll(rr.Body)
Expect(err).ToNot(HaveOccurred())
responseUser := &libregraph.User{}
err = json.Unmarshal(data, &responseUser)
Expect(err).ToNot(HaveOccurred())
Expect(responseUser.GetId()).To(Equal("user1"))
Expect(len(responseUser.GetDrives())).To(Equal(1))
})
})
Describe("PostUser", func() {
var (
user *libregraph.User
assertHandleBadAttributes = func(user *libregraph.User) {
userJson, err := json.Marshal(user)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users", bytes.NewBuffer(userJson))
svc.PostUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
}
)
BeforeEach(func() {
user = &libregraph.User{}
user.SetDisplayName("Display Name")
user.SetOnPremisesSamAccountName("user")
user.SetMail("user@example.com")
})
It("handles invalid bodies", func() {
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users?$invalid=true", nil)
svc.PostUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("handles missing display names", func() {
user.DisplayName = nil
assertHandleBadAttributes(user)
})
It("handles missing OnPremisesSamAccountName", func() {
user.OnPremisesSamAccountName = nil
assertHandleBadAttributes(user)
user.SetOnPremisesSamAccountName("")
assertHandleBadAttributes(user)
})
It("handles bad Mails", func() {
user.Mail = nil
assertHandleBadAttributes(user)
user.SetMail("not-a-mail-address")
assertHandleBadAttributes(user)
})
It("handles set Ids - they are read-only", func() {
user.SetId("/users/user")
assertHandleBadAttributes(user)
})
It("creates a user", func() {
roleService.On("AssignRoleToUser", mock.Anything, mock.Anything).Return(&settingssvc.AssignRoleToUserResponse{}, nil)
identityBackend.On("CreateUser", mock.Anything, mock.Anything).Return(func(ctx context.Context, user libregraph.User) *libregraph.User {
user.SetId("/users/user")
return &user
}, nil)
userJson, err := json.Marshal(user)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users", bytes.NewBuffer(userJson))
r = r.WithContext(ctxpkg.ContextSetUser(ctx, currentUser))
svc.PostUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
})
})
Describe("DeleteUser", func() {
It("handles missing userids", func() {
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/users/{userid}", nil)
svc.DeleteUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("prevents a user from deleting themselves", func() {
lu := libregraph.User{}
lu.SetId(currentUser.Id.OpaqueId)
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/users/{userid}", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", currentUser.Id.OpaqueId)
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.DeleteUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusForbidden))
})
It("deletes a user from deleting themselves", func() {
otheruser := &userv1beta1.User{
Id: &userv1beta1.UserId{
OpaqueId: "otheruser",
},
}
lu := libregraph.User{}
lu.SetId(otheruser.Id.OpaqueId)
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&lu, nil)
identityBackend.On("DeleteUser", mock.Anything, mock.Anything).Return(nil)
gatewayClient.On("DeleteStorageSpace", mock.Anything, mock.Anything).Return(&provider.DeleteStorageSpaceResponse{
Status: status.NewOK(ctx),
}, nil)
gatewayClient.On("ListStorageSpaces", mock.Anything, mock.Anything, mock.Anything).Return(&provider.ListStorageSpacesResponse{
Status: status.NewOK(ctx),
StorageSpaces: []*provider.StorageSpace{
{
Opaque: &typesv1beta1.Opaque{},
Id: &provider.StorageSpaceId{OpaqueId: "drive1"},
Root: &provider.ResourceId{SpaceId: "space", OpaqueId: "space"},
SpaceType: "personal",
Owner: otheruser,
},
},
}, nil)
r := httptest.NewRequest(http.MethodDelete, "/graph/v1.0/me/users/{userid}", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", lu.GetId())
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.DeleteUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusNoContent))
gatewayClient.AssertNumberOfCalls(GinkgoT(), "DeleteStorageSpace", 2) // 2 calls for the home space. first trash, then purge
})
})
Describe("PatchUser", func() {
var (
user *libregraph.User
)
BeforeEach(func() {
user = &libregraph.User{}
user.SetDisplayName("Display Name")
user.SetOnPremisesSamAccountName("user")
user.SetMail("user@example.com")
user.SetId("/users/user")
identityBackend.On("GetUser", mock.Anything, mock.Anything, mock.Anything).Return(&user, nil)
})
It("handles missing userids", func() {
r := httptest.NewRequest(http.MethodPatch, "/graph/v1.0/me/users/{userid}", nil)
svc.PatchUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("handles invalid bodies", func() {
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users?$invalid=true", nil)
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", user.GetId())
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PatchUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("handles invalid email", func() {
user.SetMail("invalid")
data, err := json.Marshal(user)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users?$invalid=true", bytes.NewBuffer(data))
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", user.GetId())
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PatchUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusBadRequest))
})
It("updates attributes", func() {
identityBackend.On("UpdateUser", mock.Anything, user.GetId(), mock.Anything).Return(user, nil)
user.SetDisplayName("New Display Name")
data, err := json.Marshal(user)
Expect(err).ToNot(HaveOccurred())
r := httptest.NewRequest(http.MethodPost, "/graph/v1.0/me/users?$invalid=true", bytes.NewBuffer(data))
rctx := chi.NewRouteContext()
rctx.URLParams.Add("userID", user.GetId())
r = r.WithContext(context.WithValue(ctxpkg.ContextSetUser(ctx, currentUser), chi.RouteCtxKey, rctx))
svc.PatchUser(rr, r)
Expect(rr.Code).To(Equal(http.StatusOK))
data, err = ioutil.ReadAll(rr.Body)
Expect(err).ToNot(HaveOccurred())
updatedUser := libregraph.User{}
err = json.Unmarshal(data, &updatedUser)
Expect(err).ToNot(HaveOccurred())
Expect(updatedUser.GetDisplayName()).To(Equal("New Display Name"))
})
})
})