init push

This commit is contained in:
2026-07-16 22:16:45 +07:00
commit 8b068bdb10
1021 changed files with 332816 additions and 0 deletions

View File

@@ -0,0 +1,501 @@
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)
}
}
}