diff --git a/.mockery.yaml b/.mockery.yaml index edc3a9dc2..8b8a7f02f 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -58,4 +58,8 @@ packages: github.com/raystack/frontier/core/organization: config: dir: "core/organization/mocks" + all: true + github.com/raystack/frontier/core/permission: + config: + dir: "core/permission/mocks" all: true \ No newline at end of file diff --git a/core/permission/mocks/repository.go b/core/permission/mocks/repository.go new file mode 100644 index 000000000..3a7c97270 --- /dev/null +++ b/core/permission/mocks/repository.go @@ -0,0 +1,371 @@ +// Code generated by mockery v2.40.2. DO NOT EDIT. + +package mocks + +import ( + context "context" + + permission "github.com/raystack/frontier/core/permission" + mock "github.com/stretchr/testify/mock" +) + +// Repository is an autogenerated mock type for the Repository type +type Repository struct { + mock.Mock +} + +type Repository_Expecter struct { + mock *mock.Mock +} + +func (_m *Repository) EXPECT() *Repository_Expecter { + return &Repository_Expecter{mock: &_m.Mock} +} + +// Delete provides a mock function with given fields: ctx, id +func (_m *Repository) Delete(ctx context.Context, id string) error { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for Delete") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, string) error); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// Repository_Delete_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Delete' +type Repository_Delete_Call struct { + *mock.Call +} + +// Delete is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Repository_Expecter) Delete(ctx interface{}, id interface{}) *Repository_Delete_Call { + return &Repository_Delete_Call{Call: _e.mock.On("Delete", ctx, id)} +} + +func (_c *Repository_Delete_Call) Run(run func(ctx context.Context, id string)) *Repository_Delete_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Repository_Delete_Call) Return(_a0 error) *Repository_Delete_Call { + _c.Call.Return(_a0) + return _c +} + +func (_c *Repository_Delete_Call) RunAndReturn(run func(context.Context, string) error) *Repository_Delete_Call { + _c.Call.Return(run) + return _c +} + +// Get provides a mock function with given fields: ctx, id +func (_m *Repository) Get(ctx context.Context, id string) (permission.Permission, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for Get") + } + + var r0 permission.Permission + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (permission.Permission, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string) permission.Permission); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(permission.Permission) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repository_Get_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Get' +type Repository_Get_Call struct { + *mock.Call +} + +// Get is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Repository_Expecter) Get(ctx interface{}, id interface{}) *Repository_Get_Call { + return &Repository_Get_Call{Call: _e.mock.On("Get", ctx, id)} +} + +func (_c *Repository_Get_Call) Run(run func(ctx context.Context, id string)) *Repository_Get_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Repository_Get_Call) Return(_a0 permission.Permission, _a1 error) *Repository_Get_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Repository_Get_Call) RunAndReturn(run func(context.Context, string) (permission.Permission, error)) *Repository_Get_Call { + _c.Call.Return(run) + return _c +} + +// GetBySlug provides a mock function with given fields: ctx, id +func (_m *Repository) GetBySlug(ctx context.Context, id string) (permission.Permission, error) { + ret := _m.Called(ctx, id) + + if len(ret) == 0 { + panic("no return value specified for GetBySlug") + } + + var r0 permission.Permission + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, string) (permission.Permission, error)); ok { + return rf(ctx, id) + } + if rf, ok := ret.Get(0).(func(context.Context, string) permission.Permission); ok { + r0 = rf(ctx, id) + } else { + r0 = ret.Get(0).(permission.Permission) + } + + if rf, ok := ret.Get(1).(func(context.Context, string) error); ok { + r1 = rf(ctx, id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repository_GetBySlug_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetBySlug' +type Repository_GetBySlug_Call struct { + *mock.Call +} + +// GetBySlug is a helper method to define mock.On call +// - ctx context.Context +// - id string +func (_e *Repository_Expecter) GetBySlug(ctx interface{}, id interface{}) *Repository_GetBySlug_Call { + return &Repository_GetBySlug_Call{Call: _e.mock.On("GetBySlug", ctx, id)} +} + +func (_c *Repository_GetBySlug_Call) Run(run func(ctx context.Context, id string)) *Repository_GetBySlug_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(string)) + }) + return _c +} + +func (_c *Repository_GetBySlug_Call) Return(_a0 permission.Permission, _a1 error) *Repository_GetBySlug_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Repository_GetBySlug_Call) RunAndReturn(run func(context.Context, string) (permission.Permission, error)) *Repository_GetBySlug_Call { + _c.Call.Return(run) + return _c +} + +// List provides a mock function with given fields: ctx, flt +func (_m *Repository) List(ctx context.Context, flt permission.Filter) ([]permission.Permission, error) { + ret := _m.Called(ctx, flt) + + if len(ret) == 0 { + panic("no return value specified for List") + } + + var r0 []permission.Permission + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, permission.Filter) ([]permission.Permission, error)); ok { + return rf(ctx, flt) + } + if rf, ok := ret.Get(0).(func(context.Context, permission.Filter) []permission.Permission); ok { + r0 = rf(ctx, flt) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]permission.Permission) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, permission.Filter) error); ok { + r1 = rf(ctx, flt) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repository_List_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'List' +type Repository_List_Call struct { + *mock.Call +} + +// List is a helper method to define mock.On call +// - ctx context.Context +// - flt permission.Filter +func (_e *Repository_Expecter) List(ctx interface{}, flt interface{}) *Repository_List_Call { + return &Repository_List_Call{Call: _e.mock.On("List", ctx, flt)} +} + +func (_c *Repository_List_Call) Run(run func(ctx context.Context, flt permission.Filter)) *Repository_List_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(permission.Filter)) + }) + return _c +} + +func (_c *Repository_List_Call) Return(_a0 []permission.Permission, _a1 error) *Repository_List_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Repository_List_Call) RunAndReturn(run func(context.Context, permission.Filter) ([]permission.Permission, error)) *Repository_List_Call { + _c.Call.Return(run) + return _c +} + +// Update provides a mock function with given fields: ctx, action +func (_m *Repository) Update(ctx context.Context, action permission.Permission) (permission.Permission, error) { + ret := _m.Called(ctx, action) + + if len(ret) == 0 { + panic("no return value specified for Update") + } + + var r0 permission.Permission + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, permission.Permission) (permission.Permission, error)); ok { + return rf(ctx, action) + } + if rf, ok := ret.Get(0).(func(context.Context, permission.Permission) permission.Permission); ok { + r0 = rf(ctx, action) + } else { + r0 = ret.Get(0).(permission.Permission) + } + + if rf, ok := ret.Get(1).(func(context.Context, permission.Permission) error); ok { + r1 = rf(ctx, action) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repository_Update_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Update' +type Repository_Update_Call struct { + *mock.Call +} + +// Update is a helper method to define mock.On call +// - ctx context.Context +// - action permission.Permission +func (_e *Repository_Expecter) Update(ctx interface{}, action interface{}) *Repository_Update_Call { + return &Repository_Update_Call{Call: _e.mock.On("Update", ctx, action)} +} + +func (_c *Repository_Update_Call) Run(run func(ctx context.Context, action permission.Permission)) *Repository_Update_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(permission.Permission)) + }) + return _c +} + +func (_c *Repository_Update_Call) Return(_a0 permission.Permission, _a1 error) *Repository_Update_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Repository_Update_Call) RunAndReturn(run func(context.Context, permission.Permission) (permission.Permission, error)) *Repository_Update_Call { + _c.Call.Return(run) + return _c +} + +// Upsert provides a mock function with given fields: ctx, action +func (_m *Repository) Upsert(ctx context.Context, action permission.Permission) (permission.Permission, error) { + ret := _m.Called(ctx, action) + + if len(ret) == 0 { + panic("no return value specified for Upsert") + } + + var r0 permission.Permission + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, permission.Permission) (permission.Permission, error)); ok { + return rf(ctx, action) + } + if rf, ok := ret.Get(0).(func(context.Context, permission.Permission) permission.Permission); ok { + r0 = rf(ctx, action) + } else { + r0 = ret.Get(0).(permission.Permission) + } + + if rf, ok := ret.Get(1).(func(context.Context, permission.Permission) error); ok { + r1 = rf(ctx, action) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Repository_Upsert_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'Upsert' +type Repository_Upsert_Call struct { + *mock.Call +} + +// Upsert is a helper method to define mock.On call +// - ctx context.Context +// - action permission.Permission +func (_e *Repository_Expecter) Upsert(ctx interface{}, action interface{}) *Repository_Upsert_Call { + return &Repository_Upsert_Call{Call: _e.mock.On("Upsert", ctx, action)} +} + +func (_c *Repository_Upsert_Call) Run(run func(ctx context.Context, action permission.Permission)) *Repository_Upsert_Call { + _c.Call.Run(func(args mock.Arguments) { + run(args[0].(context.Context), args[1].(permission.Permission)) + }) + return _c +} + +func (_c *Repository_Upsert_Call) Return(_a0 permission.Permission, _a1 error) *Repository_Upsert_Call { + _c.Call.Return(_a0, _a1) + return _c +} + +func (_c *Repository_Upsert_Call) RunAndReturn(run func(context.Context, permission.Permission) (permission.Permission, error)) *Repository_Upsert_Call { + _c.Call.Return(run) + return _c +} + +// NewRepository creates a new instance of Repository. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewRepository(t interface { + mock.TestingT + Cleanup(func()) +}) *Repository { + mock := &Repository{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/core/permission/service_test.go b/core/permission/service_test.go new file mode 100644 index 000000000..bc2a3decf --- /dev/null +++ b/core/permission/service_test.go @@ -0,0 +1,234 @@ +package permission_test + +import ( + "context" + "errors" + "testing" + + "github.com/google/uuid" + "github.com/raystack/frontier/core/permission" + "github.com/raystack/frontier/core/permission/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestService_Get(t *testing.T) { + mockRepo := mocks.NewRepository(t) + svc := permission.NewService(mockRepo) + + t.Run("should get permission by id", func(t *testing.T) { + inputID := uuid.New().String() + expectedPermission := permission.Permission{ + ID: inputID, + Name: "permissionname", + Slug: "app/permission_name", + } + + mockRepo.On("Get", mock.Anything, inputID).Return(expectedPermission, nil).Once() + perm, err := svc.Get(context.Background(), inputID) + + assert.Nil(t, err) + assert.Equal(t, expectedPermission, perm) + }) + + t.Run("should get permission by slug", func(t *testing.T) { + inputSlug := "app/somepermission" + expectedPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + Slug: inputSlug, + } + + mockRepo.On("GetBySlug", mock.Anything, inputSlug).Return(expectedPermission, nil).Once() + perm, err := svc.Get(context.Background(), inputSlug) + + assert.Nil(t, err) + assert.Equal(t, expectedPermission, perm) + }) + + t.Run("should return an error if permission cannot be fetched", func(t *testing.T) { + inputID := uuid.New().String() + + expectedError := errors.New("An error occurred") + + mockRepo.On("Get", mock.Anything, inputID).Return(permission.Permission{}, expectedError).Once() + _, err := svc.Get(context.Background(), inputID) + + assert.NotNil(t, err) + assert.Equal(t, expectedError, err) + }) +} + +func TestService_Upsert(t *testing.T) { + mockRepo := mocks.NewRepository(t) + svc := permission.NewService(mockRepo) + + t.Run("should upsert permission", func(t *testing.T) { + inputPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + Slug: "app/permission_name", + } + + mockRepo.On("Upsert", mock.Anything, inputPermission).Return(inputPermission, nil).Once() + perm, err := svc.Upsert(context.Background(), inputPermission) + + assert.Nil(t, err) + assert.Equal(t, inputPermission, perm) + }) + + t.Run("should generate slug if not present in request", func(t *testing.T) { + inputPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + } + + expectedRepoInput := inputPermission + expectedRepoInput.Slug = inputPermission.GenerateSlug() + + mockRepo.On("Upsert", mock.Anything, expectedRepoInput).Return(expectedRepoInput, nil).Once() + perm, err := svc.Upsert(context.Background(), inputPermission) + + assert.Nil(t, err) + assert.Equal(t, expectedRepoInput, perm) + }) + + t.Run("should return an error if permission cannot be upserted", func(t *testing.T) { + inputPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + Slug: "app/permission_name", + } + + expectedErr := errors.New("An error occurred") + + mockRepo.On("Upsert", mock.Anything, inputPermission).Return(permission.Permission{}, expectedErr).Once() + _, err := svc.Upsert(context.Background(), inputPermission) + + assert.NotNil(t, err) + assert.Equal(t, expectedErr, err) + }) +} + +func TestService_List(t *testing.T) { + mockRepo := mocks.NewRepository(t) + svc := permission.NewService(mockRepo) + + t.Run("should list permissions", func(t *testing.T) { + filters := permission.Filter{ + Namespace: "app/organization", + } + + expectedRes := []permission.Permission{ + { + ID: uuid.New().String(), + Name: "firstpermissionname", + Slug: "app/first_permission_name", + }, + { + ID: uuid.New().String(), + Name: "secondpermissionname", + Slug: "app/second_permission_name", + }, + } + + mockRepo.On("List", mock.Anything, filters).Return(expectedRes, nil).Once() + + perms, err := svc.List(context.Background(), filters) + + assert.Nil(t, err) + assert.Equal(t, expectedRes, perms) + }) + + t.Run("should return an error if permissions cannot be list", func(t *testing.T) { + filters := permission.Filter{ + Namespace: "app/organization", + } + + expectedErr := errors.New("An error occurred") + + mockRepo.On("List", mock.Anything, filters).Return([]permission.Permission{}, expectedErr).Once() + + _, err := svc.List(context.Background(), filters) + + assert.NotNil(t, err) + assert.Equal(t, expectedErr, err) + }) +} +func TestService_Update(t *testing.T) { + mockRepo := mocks.NewRepository(t) + svc := permission.NewService(mockRepo) + + t.Run("should update permission", func(t *testing.T) { + inputPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + Slug: "app/permission_name", + } + + mockRepo.On("Update", mock.Anything, inputPermission).Return(inputPermission, nil).Once() + perm, err := svc.Update(context.Background(), inputPermission) + + assert.Nil(t, err) + assert.Equal(t, inputPermission, perm) + }) + + t.Run("should generate slug if not present in request", func(t *testing.T) { + inputPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + } + + expectedRepoInput := inputPermission + expectedRepoInput.Slug = inputPermission.GenerateSlug() + + mockRepo.On("Update", mock.Anything, expectedRepoInput).Return(expectedRepoInput, nil).Once() + perm, err := svc.Update(context.Background(), inputPermission) + + assert.Nil(t, err) + assert.Equal(t, expectedRepoInput, perm) + }) + + t.Run("should return an error if permission cannot be updated", func(t *testing.T) { + inputPermission := permission.Permission{ + ID: uuid.New().String(), + Name: "permissionname", + Slug: "app/permission_name", + } + + expectedErr := errors.New("An error occurred") + + mockRepo.On("Update", mock.Anything, inputPermission).Return(permission.Permission{}, expectedErr).Once() + _, err := svc.Update(context.Background(), inputPermission) + + assert.NotNil(t, err) + assert.Equal(t, expectedErr, err) + }) +} + +func TestService_Delete(t *testing.T) { + mockRepo := mocks.NewRepository(t) + svc := permission.NewService(mockRepo) + + t.Run("should delete permission", func(t *testing.T) { + permissionID := uuid.New().String() + + mockRepo.On("Delete", mock.Anything, permissionID).Return(nil).Once() + + err := svc.Delete(context.Background(), permissionID) + + assert.Nil(t, err) + }) + + t.Run("should return an error if permissions cannot be list", func(t *testing.T) { + permissionID := uuid.New().String() + expectedErr := errors.New("An error occurred") + + mockRepo.On("Delete", mock.Anything, permissionID).Return(expectedErr).Once() + + err := svc.Delete(context.Background(), permissionID) + + assert.NotNil(t, err) + assert.Equal(t, expectedErr, err) + }) +}