502 lines
14 KiB
Go
502 lines
14 KiB
Go
package service
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"wucher/internal/domain/auth"
|
|
)
|
|
|
|
type roleServiceRepoMock struct {
|
|
createCalled bool
|
|
updateCalled bool
|
|
deleteCalled bool
|
|
getByIDCalled bool
|
|
getByNameCalled bool
|
|
listCalled bool
|
|
getPermByIDCalled bool
|
|
getPermByKeyCalled bool
|
|
updatePermCalled bool
|
|
assignPermCalled bool
|
|
removePermCalled bool
|
|
listPermCalled bool
|
|
|
|
lastCreate *auth.Role
|
|
lastUpdate *auth.Role
|
|
lastDelete []byte
|
|
lastListFilter string
|
|
lastListSort string
|
|
lastListLimit int
|
|
lastListOffset int
|
|
lastGetPermID []byte
|
|
lastGetPermKey string
|
|
lastAssignRoleID []byte
|
|
lastAssignPermissionID []byte
|
|
lastRemoveRoleID []byte
|
|
lastRemovePermissionID []byte
|
|
lastPermFilter string
|
|
lastPermSort string
|
|
lastPermLimit int
|
|
lastPermOffset int
|
|
|
|
roleByID *auth.Role
|
|
roleByName *auth.Role
|
|
listRoles []auth.Role
|
|
listTotal int64
|
|
permissionByID *auth.Permission
|
|
permissionByKey *auth.Permission
|
|
assignedPermission *auth.RolePermission
|
|
listPermissions []auth.Permission
|
|
listPermissionsTotal int64
|
|
|
|
createErr error
|
|
updateErr error
|
|
deleteErr error
|
|
getByIDErr error
|
|
getByNameErr error
|
|
listErr error
|
|
getPermByIDErr error
|
|
getPermByKeyErr error
|
|
updatePermErr error
|
|
assignPermErr error
|
|
removePermErr error
|
|
listPermErr error
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) CreateRole(_ context.Context, role *auth.Role) error {
|
|
m.createCalled = true
|
|
m.lastCreate = role
|
|
return m.createErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) UpdateRole(_ context.Context, role *auth.Role) error {
|
|
m.updateCalled = true
|
|
m.lastUpdate = role
|
|
return m.updateErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) DeleteRole(_ context.Context, id []byte) error {
|
|
m.deleteCalled = true
|
|
m.lastDelete = id
|
|
return m.deleteErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) GetRoleByID(_ context.Context, _ []byte) (*auth.Role, error) {
|
|
m.getByIDCalled = true
|
|
return m.roleByID, m.getByIDErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) GetRoleByName(_ context.Context, _ string) (*auth.Role, error) {
|
|
m.getByNameCalled = true
|
|
return m.roleByName, m.getByNameErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) ListRoles(_ context.Context, filterName string, sort string, limit, offset int) ([]auth.Role, int64, error) {
|
|
m.listCalled = true
|
|
m.lastListFilter = filterName
|
|
m.lastListSort = sort
|
|
m.lastListLimit = limit
|
|
m.lastListOffset = offset
|
|
return m.listRoles, m.listTotal, m.listErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) GetPermissionByID(_ context.Context, id []byte) (*auth.Permission, error) {
|
|
m.getPermByIDCalled = true
|
|
m.lastGetPermID = id
|
|
return m.permissionByID, m.getPermByIDErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) GetPermissionByKey(_ context.Context, key string) (*auth.Permission, error) {
|
|
m.getPermByKeyCalled = true
|
|
m.lastGetPermKey = key
|
|
return m.permissionByKey, m.getPermByKeyErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) UpdatePermission(_ context.Context, permission *auth.Permission) error {
|
|
m.updatePermCalled = true
|
|
m.permissionByID = permission
|
|
return m.updatePermErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) ListPermissions(_ context.Context, filter, sort string, limit, offset int) ([]auth.Permission, int64, error) {
|
|
m.listPermCalled = true
|
|
m.lastPermFilter = filter
|
|
m.lastPermSort = sort
|
|
m.lastPermLimit = limit
|
|
m.lastPermOffset = offset
|
|
return m.listPermissions, m.listPermissionsTotal, m.listPermErr
|
|
}
|
|
func (m *roleServiceRepoMock) ListPermissionsByRoleID(_ context.Context, _ []byte) ([]auth.Permission, error) {
|
|
return []auth.Permission{}, nil
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) HasRolePermission(_ context.Context, _ []byte, _ string) (bool, error) {
|
|
return true, nil
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) AssignPermission(_ context.Context, roleID, permissionID []byte) (*auth.RolePermission, error) {
|
|
m.assignPermCalled = true
|
|
m.lastAssignRoleID = roleID
|
|
m.lastAssignPermissionID = permissionID
|
|
if m.assignedPermission == nil {
|
|
m.assignedPermission = &auth.RolePermission{RoleID: roleID, PermissionID: permissionID}
|
|
}
|
|
return m.assignedPermission, m.assignPermErr
|
|
}
|
|
|
|
func (m *roleServiceRepoMock) RemovePermission(_ context.Context, roleID []byte, permissionID []byte) error {
|
|
m.removePermCalled = true
|
|
m.lastRemoveRoleID = roleID
|
|
m.lastRemovePermissionID = permissionID
|
|
return m.removePermErr
|
|
}
|
|
|
|
func TestRoleService_Create_Validation(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
if err := svc.Create(context.Background(), &auth.Role{Name: " "}); err == nil {
|
|
t.Fatalf("expected validation error")
|
|
}
|
|
if repo.createCalled {
|
|
t.Fatalf("expected repo.CreateRole not called")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_Create_RepoError(t *testing.T) {
|
|
repo := &roleServiceRepoMock{createErr: errors.New("db error")}
|
|
svc := NewRoleService(repo)
|
|
|
|
err := svc.Create(context.Background(), &auth.Role{Name: "admin"})
|
|
if err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
if !repo.createCalled {
|
|
t.Fatalf("expected repo.CreateRole called")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_Update_Validation(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
if err := svc.Update(context.Background(), &auth.Role{Name: ""}); err == nil {
|
|
t.Fatalf("expected validation error")
|
|
}
|
|
if repo.updateCalled {
|
|
t.Fatalf("expected repo.UpdateRole not called")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_Update_Success(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
err := svc.Update(context.Background(), &auth.Role{Name: "admin"})
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !repo.updateCalled {
|
|
t.Fatalf("expected repo.UpdateRole called")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_Delete_Success(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
id := []byte("id")
|
|
err := svc.Delete(context.Background(), id)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !repo.deleteCalled {
|
|
t.Fatalf("expected repo.DeleteRole called")
|
|
}
|
|
if string(repo.lastDelete) != string(id) {
|
|
t.Fatalf("expected delete id passed to repo")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_GetByID_Success(t *testing.T) {
|
|
repo := &roleServiceRepoMock{roleByID: &auth.Role{Name: "admin"}}
|
|
svc := NewRoleService(repo)
|
|
|
|
role, err := svc.GetByID(context.Background(), []byte("id"))
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if role == nil || role.Name != "admin" {
|
|
t.Fatalf("unexpected role")
|
|
}
|
|
if !repo.getByIDCalled {
|
|
t.Fatalf("expected repo.GetRoleByID called")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_GetByName_Success(t *testing.T) {
|
|
repo := &roleServiceRepoMock{roleByName: &auth.Role{Name: "admin"}}
|
|
svc := NewRoleService(repo)
|
|
|
|
role, err := svc.GetByName(context.Background(), "admin")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if role == nil || role.Name != "admin" {
|
|
t.Fatalf("unexpected role")
|
|
}
|
|
if !repo.getByNameCalled {
|
|
t.Fatalf("expected repo.GetRoleByName called")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_GetPermissionByID(t *testing.T) {
|
|
t.Run("success", func(t *testing.T) {
|
|
perm := &auth.Permission{Name: "Users Read", Key: "users.read"}
|
|
repo := &roleServiceRepoMock{permissionByID: perm}
|
|
svc := NewRoleService(repo)
|
|
|
|
id := []byte("perm-id")
|
|
got, err := svc.GetPermissionByID(context.Background(), id)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got == nil || got.Key != "users.read" {
|
|
t.Fatalf("unexpected permission: %+v", got)
|
|
}
|
|
if !repo.getPermByIDCalled {
|
|
t.Fatalf("expected repo.GetPermissionByID called")
|
|
}
|
|
if string(repo.lastGetPermID) != string(id) {
|
|
t.Fatalf("expected id forwarded to repo")
|
|
}
|
|
})
|
|
|
|
t.Run("repo error", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{getPermByIDErr: errors.New("db error")}
|
|
svc := NewRoleService(repo)
|
|
|
|
if _, err := svc.GetPermissionByID(context.Background(), []byte("perm-id")); err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRoleService_GetPermissionByKey(t *testing.T) {
|
|
repo := &roleServiceRepoMock{
|
|
permissionByKey: &auth.Permission{Name: "Delete User", Key: "user.delete"},
|
|
}
|
|
svc := NewRoleService(repo)
|
|
|
|
got, err := svc.GetPermissionByKey(context.Background(), "user.delete")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if got == nil || got.Key != "user.delete" {
|
|
t.Fatalf("unexpected permission: %+v", got)
|
|
}
|
|
if !repo.getPermByKeyCalled {
|
|
t.Fatalf("expected repo.GetPermissionByKey called")
|
|
}
|
|
if repo.lastGetPermKey != "user.delete" {
|
|
t.Fatalf("expected key forwarded to repo")
|
|
}
|
|
}
|
|
|
|
func TestRoleService_UpdatePermission(t *testing.T) {
|
|
t.Run("validation", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
if err := svc.UpdatePermission(context.Background(), &auth.Permission{Name: " ", Key: "user.delete"}); err == nil {
|
|
t.Fatalf("expected validation error for empty name")
|
|
}
|
|
if err := svc.UpdatePermission(context.Background(), &auth.Permission{Name: "Delete User", Key: " "}); err == nil {
|
|
t.Fatalf("expected validation error for empty key")
|
|
}
|
|
if repo.updatePermCalled {
|
|
t.Fatalf("expected repo.UpdatePermission not called")
|
|
}
|
|
})
|
|
|
|
t.Run("success", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
perm := &auth.Permission{Name: "Delete User", Key: "user.delete", RequiresPIN: true}
|
|
if err := svc.UpdatePermission(context.Background(), perm); err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !repo.updatePermCalled {
|
|
t.Fatalf("expected repo.UpdatePermission called")
|
|
}
|
|
if repo.permissionByID == nil || !repo.permissionByID.RequiresPIN {
|
|
t.Fatalf("expected permission forwarded to repo")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRoleService_AssignPermission(t *testing.T) {
|
|
t.Run("success", func(t *testing.T) {
|
|
roleID := []byte("role-id")
|
|
permID := []byte("perm-id")
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
rp, err := svc.AssignPermission(context.Background(), roleID, permID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if rp == nil {
|
|
t.Fatalf("expected role permission")
|
|
}
|
|
if !repo.assignPermCalled {
|
|
t.Fatalf("expected repo.AssignPermission called")
|
|
}
|
|
if string(repo.lastAssignRoleID) != string(roleID) || string(repo.lastAssignPermissionID) != string(permID) {
|
|
t.Fatalf("expected roleID/permissionID forwarded to repo")
|
|
}
|
|
})
|
|
|
|
t.Run("repo error", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{assignPermErr: errors.New("assign error")}
|
|
svc := NewRoleService(repo)
|
|
|
|
if _, err := svc.AssignPermission(context.Background(), []byte("role-id"), []byte("perm-id")); err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRoleService_RemovePermission(t *testing.T) {
|
|
t.Run("success", func(t *testing.T) {
|
|
roleID := []byte("role-id")
|
|
permID := []byte("perm-id")
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
err := svc.RemovePermission(context.Background(), roleID, permID)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !repo.removePermCalled {
|
|
t.Fatalf("expected repo.RemovePermission called")
|
|
}
|
|
if string(repo.lastRemoveRoleID) != string(roleID) || string(repo.lastRemovePermissionID) != string(permID) {
|
|
t.Fatalf("expected roleID/permissionID forwarded to repo")
|
|
}
|
|
})
|
|
|
|
t.Run("repo error", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{removePermErr: errors.New("remove error")}
|
|
svc := NewRoleService(repo)
|
|
|
|
if err := svc.RemovePermission(context.Background(), []byte("role-id"), []byte("perm-id")); err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRoleService_ListPermissions(t *testing.T) {
|
|
t.Run("normalize sort and forward args", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{
|
|
listPermissions: []auth.Permission{{Name: "Users Read", Key: "users.read"}},
|
|
listPermissionsTotal: 1,
|
|
}
|
|
svc := NewRoleService(repo)
|
|
|
|
perms, total, err := svc.ListPermissions(context.Background(), "users", "-key", 10, 20)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if len(perms) != 1 || perms[0].Key != "users.read" || total != 1 {
|
|
t.Fatalf("unexpected list result: perms=%+v total=%d", perms, total)
|
|
}
|
|
if !repo.listPermCalled {
|
|
t.Fatalf("expected repo.ListPermissions called")
|
|
}
|
|
if repo.lastPermFilter != "users" {
|
|
t.Fatalf("expected filter forwarded")
|
|
}
|
|
if repo.lastPermSort != "`key` DESC" {
|
|
t.Fatalf("expected normalized permission sort, got %q", repo.lastPermSort)
|
|
}
|
|
if repo.lastPermLimit != 10 || repo.lastPermOffset != 20 {
|
|
t.Fatalf("expected limit/offset forwarded")
|
|
}
|
|
})
|
|
|
|
t.Run("repo error", func(t *testing.T) {
|
|
repo := &roleServiceRepoMock{listPermErr: errors.New("list error")}
|
|
svc := NewRoleService(repo)
|
|
|
|
if _, _, err := svc.ListPermissions(context.Background(), "", "bad", 10, 0); err == nil {
|
|
t.Fatalf("expected error")
|
|
}
|
|
if repo.lastPermSort != "" {
|
|
t.Fatalf("unexpected sort for invalid input, got %q", repo.lastPermSort)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestRoleService_List_NormalizeSort(t *testing.T) {
|
|
repo := &roleServiceRepoMock{}
|
|
svc := NewRoleService(repo)
|
|
|
|
_, _, err := svc.List(context.Background(), "adm", "-created_at", 10, 20)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
if !repo.listCalled {
|
|
t.Fatalf("expected repo.ListRoles called")
|
|
}
|
|
if repo.lastListFilter != "adm" {
|
|
t.Fatalf("expected filter passed to repo")
|
|
}
|
|
if repo.lastListSort != "created_at DESC" {
|
|
t.Fatalf("expected normalized sort, got %q", repo.lastListSort)
|
|
}
|
|
if repo.lastListLimit != 10 || repo.lastListOffset != 20 {
|
|
t.Fatalf("expected limit/offset passed to repo")
|
|
}
|
|
}
|
|
|
|
func TestNormalizeSort(t *testing.T) {
|
|
cases := map[string]string{
|
|
"name": "name ASC",
|
|
"-name": "name DESC",
|
|
"created_at": "created_at ASC",
|
|
"-created_at": "created_at DESC",
|
|
"updated_at": "updated_at ASC",
|
|
"-updated_at": "updated_at DESC",
|
|
"": "",
|
|
"bad": "",
|
|
}
|
|
for in, want := range cases {
|
|
if got := normalizeSort(in); got != want {
|
|
t.Fatalf("normalizeSort(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestNormalizePermissionSort(t *testing.T) {
|
|
cases := map[string]string{
|
|
"name": "name ASC",
|
|
"-name": "name DESC",
|
|
"key": "`key` ASC",
|
|
"-key": "`key` DESC",
|
|
"created_at": "created_at ASC",
|
|
"-created_at": "created_at DESC",
|
|
"updated_at": "updated_at ASC",
|
|
"-updated_at": "updated_at DESC",
|
|
"": "",
|
|
"bad": "",
|
|
}
|
|
for in, want := range cases {
|
|
if got := normalizePermissionSort(in); got != want {
|
|
t.Fatalf("normalizePermissionSort(%q) = %q, want %q", in, got, want)
|
|
}
|
|
}
|
|
}
|