package service import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" "github.com/gochat/gochat/internal/model" "github.com/gochat/gochat/internal/repository" ) // ========== Test Setup ========== func setupCustomRoleServiceTest(t *testing.T) (*CustomRoleService, *gorm.DB) { t.Helper() db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err, "failed to open SQLite test db") require.NoError(t, db.AutoMigrate( &model.Account{}, &model.User{}, &model.AccountUser{}, &model.CustomRole{}, ), "failed to auto-migrate") t.Cleanup(func() { sqlDB, _ := db.DB() sqlDB.Close() }) repo := repository.NewCustomRoleRepo(db) svc := NewCustomRoleService(repo) return svc, db } func createCRSvcTestAccount(t *testing.T, db *gorm.DB) *model.Account { t.Helper() account := &model.Account{Name: "CustomRoleSvcOrg", Locale: "en", Active: true} require.NoError(t, db.Create(account).Error) return account } // ========== List ========== func TestCustomRoleService_List(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) // Create 2 roles _, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "Supervisor", Permissions: []string{"conversation_manage"}, }) require.NoError(t, err) _, err = svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "TeamLead", Permissions: []string{"conversation_unassigned_manage"}, }) require.NoError(t, err) roles, total, err := svc.List(context.Background(), account.ID, 1, 25) assert.NoError(t, err) assert.Equal(t, int64(2), total) assert.Len(t, roles, 2) } func TestCustomRoleService_List_Empty(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) roles, total, err := svc.List(context.Background(), account.ID, 1, 25) assert.NoError(t, err) assert.Equal(t, int64(0), total) assert.Len(t, roles, 0) } // ========== Create ========== func TestCustomRoleService_Create(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) role, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "Supervisor", Permissions: []string{"conversation_manage", "contact_manage"}, }) assert.NoError(t, err) assert.NotZero(t, role.ID) assert.Equal(t, "Supervisor", role.Name) } func TestCustomRoleService_Create_EmptyPermissions(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) // Create with nil permissions — should default to "{}" role, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "EmptyRole", Permissions: nil, }) assert.NoError(t, err) assert.NotZero(t, role.ID) } func TestCustomRoleService_Create_InvalidPermission(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) _, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "InvalidRole", Permissions: []string{"conversation_delete"}, }) assert.Error(t, err) } // ========== GetByID ========== func TestCustomRoleService_GetByID(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) created, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "TeamLead", Permissions: []string{"conversation_participating_manage"}, }) require.NoError(t, err) found, err := svc.GetByID(context.Background(), created.ID, account.ID) assert.NoError(t, err) assert.Equal(t, created.ID, found.ID) assert.Equal(t, "TeamLead", found.Name) } func TestCustomRoleService_GetByID_WrongAccount(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) created, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "TeamLead", Permissions: []string{"conversation_participating_manage"}, }) require.NoError(t, err) // Wrong account should fail _, err = svc.GetByID(context.Background(), created.ID, 9999) assert.Error(t, err) } // ========== Update ========== func TestCustomRoleService_Update(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) created, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "Supervisor", Permissions: []string{"conversation_manage"}, }) require.NoError(t, err) updated, err := svc.Update(context.Background(), created.ID, account.ID, UpdateCustomRoleRequest{ Name: "Senior Supervisor", }) assert.NoError(t, err) assert.Equal(t, "Senior Supervisor", updated.Name) } func TestCustomRoleService_Update_ClearsDescriptionAndPermissions(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) description := "Can manage conversations" created, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "Supervisor", Description: description, Permissions: []string{"conversation_manage"}, }) require.NoError(t, err) emptyDescription := "" updated, err := svc.Update(context.Background(), created.ID, account.ID, UpdateCustomRoleRequest{ Description: &emptyDescription, Permissions: []string{}, }) require.NoError(t, err) assert.Equal(t, "", updated.Description) permissions, err := updated.GetPermissionKeys() require.NoError(t, err) assert.Empty(t, permissions) } func TestCustomRoleService_Update_NotFound(t *testing.T) { svc, _ := setupCustomRoleServiceTest(t) _, err := svc.Update(context.Background(), 9999, 9999, UpdateCustomRoleRequest{ Name: "Ghost", }) assert.Error(t, err) } // ========== Delete ========== func TestCustomRoleService_Delete(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) created, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "Supervisor", Permissions: []string{"conversation_manage"}, }) require.NoError(t, err) err = svc.Delete(context.Background(), created.ID, account.ID) assert.NoError(t, err) // Should no longer be visible via GetByID _, err = svc.GetByID(context.Background(), created.ID, account.ID) assert.Error(t, err) } func TestCustomRoleService_Delete_NullifiesAccountUsers(t *testing.T) { svc, db := setupCustomRoleServiceTest(t) account := createCRSvcTestAccount(t, db) user := &model.User{AccountID: account.ID, Name: "Role User", Email: "role-user-svc@example.com", Password: "pw", Active: true} require.NoError(t, db.Create(user).Error) created, err := svc.Create(context.Background(), account.ID, CreateCustomRoleRequest{ Name: "Supervisor", Permissions: []string{"conversation_manage"}, }) require.NoError(t, err) require.NoError(t, db.Create(&model.AccountUser{AccountID: account.ID, UserID: user.ID, Role: "agent", CustomRoleID: created.ID}).Error) err = svc.Delete(context.Background(), created.ID, account.ID) assert.NoError(t, err) var au model.AccountUser require.NoError(t, db.Where("account_id = ? AND user_id = ?", account.ID, user.ID).First(&au).Error) assert.Equal(t, uint(0), au.CustomRoleID) assert.Equal(t, "agent", au.Role) } func TestCustomRoleService_Delete_NotFound(t *testing.T) { svc, _ := setupCustomRoleServiceTest(t) err := svc.Delete(context.Background(), 9999, 9999) // GORM soft-delete on non-existent returns no error (0 rows) assert.NoError(t, err) }