package user import ( "errors" "testing" "cardverse/internal/pkg/utils" ) func TestService_Create_Success(t *testing.T) { repo := &mockRepository{ findByEmailFunc: func(email string) (*User, error) { return nil, errors.New("record not found") }, createFunc: func(u *User) error { u.ID = 1 return nil }, } svc := NewService(repo) req := CreateUserRequest{Name: "Budi", Email: "budi@mail.com", Password: "rahasia123"} newUser, err := svc.Create(nil, req) if err != nil { t.Fatalf("expected no error, got: %v", err) } if newUser.Email != req.Email { t.Errorf("expected email %s, got %s", req.Email, newUser.Email) } if newUser.Password == req.Password { t.Error("stored password should be hashed, not plain text") } if !utils.CheckPassword(newUser.Password, req.Password) { t.Error("hashed password does not match original password") } } func TestService_Create_AdminRole_ForbiddenForRegularAdmin(t *testing.T) { adminID := uint(5) repo := &mockRepository{ findByEmailFunc: func(email string) (*User, error) { return nil, errors.New("record not found") }, findByIDFunc: func(id uint) (*User, error) { return &User{ID: id, Role: RoleAdmin}, nil }, } svc := NewService(repo) req := CreateUserRequest{Name: "New Admin", Email: "admin2@mail.com", Password: "rahasia123", Role: RoleAdmin} _, err := svc.Create(&adminID, req) if !errors.Is(err, ErrForbiddenRoleCreation) { t.Fatalf("expected ErrForbiddenRoleCreation for regular admin creating another admin, got: %v", err) } } func TestService_Create_AdminRole_AllowedForSuperAdmin(t *testing.T) { superAdminID := uint(1) repo := &mockRepository{ findByEmailFunc: func(email string) (*User, error) { return nil, errors.New("record not found") }, findByIDFunc: func(id uint) (*User, error) { return &User{ID: id, Role: RoleSuperAdmin}, nil }, createFunc: func(u *User) error { u.ID = 10 return nil }, } svc := NewService(repo) req := CreateUserRequest{Name: "New Admin", Email: "admin2@mail.com", Password: "rahasia123", Role: RoleAdmin} newAdmin, err := svc.Create(&superAdminID, req) if err != nil { t.Fatalf("expected no error for superadmin creating an admin, got: %v", err) } if newAdmin.Role != RoleAdmin { t.Errorf("expected role %s, got %s", RoleAdmin, newAdmin.Role) } } func TestService_Create_EmailAlreadyTaken(t *testing.T) { repo := &mockRepository{ findByEmailFunc: func(email string) (*User, error) { return &User{ID: 99, Email: email}, nil }, } svc := NewService(repo) _, err := svc.Create(nil, CreateUserRequest{Name: "Budi", Email: "budi@mail.com", Password: "rahasia123"}) if !errors.Is(err, ErrEmailTaken) { t.Fatalf("expected ErrEmailTaken, got: %v", err) } } func TestService_GetByID_Success(t *testing.T) { repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return &User{ID: id, Name: "Budi"}, nil }, } svc := NewService(repo) u, err := svc.GetByID(1) if err != nil { t.Fatalf("expected no error, got: %v", err) } if u.ID != 1 { t.Errorf("expected ID 1, got: %d", u.ID) } } func TestService_GetByID_NotFound(t *testing.T) { repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return nil, errors.New("record not found") }, } svc := NewService(repo) _, err := svc.GetByID(999) if !errors.Is(err, ErrUserNotFound) { t.Fatalf("expected ErrUserNotFound, got: %v", err) } } func TestService_Update_Success(t *testing.T) { repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return &User{ID: id, Name: "Old Name"}, nil }, updateFunc: func(u *User) error { return nil }, } svc := NewService(repo) updated, err := svc.Update(1, 1, UpdateUserRequest{Name: "New Name"}) if err != nil { t.Fatalf("expected no error, got: %v", err) } if updated.Name != "New Name" { t.Errorf("expected Name 'New Name', got: '%s'", updated.Name) } } func TestService_UpdateAvatar_Success(t *testing.T) { repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return &User{ID: id, Name: "User Test"}, nil }, updateFunc: func(u *User) error { return nil }, } svc := NewService(repo) updated, err := svc.UpdateAvatar(1, "/images/avatars/avatar_1_123.png") if err != nil { t.Fatalf("expected no error, got: %v", err) } if updated.AvatarURL == nil || *updated.AvatarURL != "/images/avatars/avatar_1_123.png" { t.Errorf("expected AvatarURL '/images/avatars/avatar_1_123.png', got: '%v'", updated.AvatarURL) } } func TestService_Update_NotFound(t *testing.T) { repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return nil, errors.New("record not found") }, } svc := NewService(repo) _, err := svc.Update(1, 1, UpdateUserRequest{Name: "Anything"}) if !errors.Is(err, ErrUserNotFound) { t.Fatalf("expected ErrUserNotFound, got: %v", err) } } func TestService_SuspendAndUnsuspend_Success(t *testing.T) { repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return &User{ID: id, Status: StatusActive}, nil }, updateFunc: func(u *User) error { return nil }, } svc := NewService(repo) suspended, err := svc.SuspendUser(1, 2) if err != nil { t.Fatalf("expected no error, got: %v", err) } if suspended.Status != StatusSuspended { t.Errorf("expected status 'suspended', got: '%s'", suspended.Status) } unsuspended, err := svc.UnsuspendUser(1, 2) if err != nil { t.Fatalf("expected no error, got: %v", err) } if unsuspended.Status != StatusActive { t.Errorf("expected status 'active', got: '%s'", unsuspended.Status) } } func TestService_Delete_Success(t *testing.T) { deleteCalled := false repo := &mockRepository{ findByIDFunc: func(id uint) (*User, error) { return &User{ID: id}, nil }, deleteFunc: func(id uint) error { deleteCalled = true return nil }, } svc := NewService(repo) if err := svc.Delete(1); err != nil { t.Fatalf("expected no error, got: %v", err) } if !deleteCalled { t.Error("repository.Delete should have been called") } } func TestService_GetAllPaginated_DefaultNormalization(t *testing.T) { var capturedPage, capturedLimit int var capturedSearch string repo := &mockRepository{ findAllPaginatedFunc: func(page, limit int, query ListUserQuery) ([]User, int64, error) { capturedPage, capturedLimit, capturedSearch = page, limit, query.Search return []User{{ID: 1}}, 1, nil }, } svc := NewService(repo) _, total, err := svc.GetAllPaginated(ListUserQuery{Page: 0, Limit: 0, Search: "budi"}) if err != nil { t.Fatalf("expected no error, got: %v", err) } if total != 1 { t.Errorf("expected total 1, got: %d", total) } if capturedPage != 1 { t.Errorf("expected page normalized to 1, got: %d", capturedPage) } if capturedLimit != 20 { t.Errorf("expected limit normalized to 20, got: %d", capturedLimit) } if capturedSearch != "budi" { t.Errorf("expected search 'budi', got: '%s'", capturedSearch) } }