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 setupDraftMessageServiceTestDB(t *testing.T) *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.Inbox{}, &model.Contact{}, &model.ContactInbox{}, &model.Conversation{}, &model.DraftMessage{}, ), "failed to auto-migrate") t.Cleanup(func() { sqlDB, _ := db.DB() sqlDB.Close() }) return db } func createDraftTestAccount(t *testing.T, db *gorm.DB) *model.Account { t.Helper() account := &model.Account{Name: "DraftTestOrg", Locale: "en", Active: true} require.NoError(t, db.Create(account).Error) return account } func createDraftTestUser(t *testing.T, db *gorm.DB) *model.User { t.Helper() user := &model.User{Name: "DraftAgent", Email: "draftagent@test.com", Provider: "email", Role: "agent"} require.NoError(t, db.Create(user).Error) return user } func createDraftTestInbox(t *testing.T, db *gorm.DB, accountID uint) *model.Inbox { t.Helper() inbox := &model.Inbox{AccountID: accountID, Name: "DraftInbox", ChannelType: "web_widget", ChannelID: 1, Enabled: true} require.NoError(t, db.Create(inbox).Error) return inbox } func createDraftTestContact(t *testing.T, db *gorm.DB, accountID uint) *model.Contact { t.Helper() contact := &model.Contact{AccountID: accountID, Name: "DraftContact", Email: "draftcontact@test.com"} require.NoError(t, db.Create(contact).Error) return contact } func createDraftTestConversation(t *testing.T, db *gorm.DB, accountID, inboxID, contactID uint) *model.Conversation { t.Helper() conversation := &model.Conversation{ AccountID: accountID, InboxID: inboxID, ContactID: contactID, ChannelType: "web_widget", Channel: "web_widget", Status: "open", } require.NoError(t, db.Create(conversation).Error) return conversation } func setupDraftMessageService(t *testing.T) (*DraftMessageService, *gorm.DB) { t.Helper() db := setupDraftMessageServiceTestDB(t) repo := repository.NewDraftMessageRepo(db) conversationRepo := repository.NewConversationRepo(db) svc := NewDraftMessageService(repo, conversationRepo) return svc, db } // ========== Create Draft Tests ========== func TestDraftMessageService_Create_Success(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) draft, err := svc.Create(ctx, account.ID, conversation.ID, user.ID, "Hello, this is a draft message") require.NoError(t, err) assert.NotNil(t, draft) assert.Equal(t, conversation.ID, draft.ConversationID) assert.Equal(t, user.ID, draft.UserID) assert.Equal(t, "Hello, this is a draft message", draft.Content) } func TestDraftMessageService_Create_ConversationNotFound(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) draft, err := svc.Create(ctx, account.ID, 99999, user.ID, "draft content") assert.Error(t, err) assert.Nil(t, draft) } func TestDraftMessageService_Create_AccountMismatch(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) // Use a wrong accountID wrongAccountID := uint(99999) draft, err := svc.Create(ctx, wrongAccountID, conversation.ID, user.ID, "draft content") assert.Error(t, err) assert.Nil(t, draft) } // ========== Get Draft Tests ========== func TestDraftMessageService_Get_Success(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) draft, err := svc.Create(ctx, account.ID, conversation.ID, user.ID, "draft content") require.NoError(t, err) fetched, err := svc.Get(ctx, draft.ID) require.NoError(t, err) assert.Equal(t, draft.ID, fetched.ID) assert.Equal(t, draft.Content, fetched.Content) } func TestDraftMessageService_Get_NotFound(t *testing.T) { svc, _ := setupDraftMessageService(t) ctx := context.Background() draft, err := svc.Get(ctx, 99999) assert.Error(t, err) assert.Nil(t, draft) } // ========== List Drafts Tests ========== func TestDraftMessageService_List_Success(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) // Create two drafts _, err := svc.Create(ctx, account.ID, conversation.ID, user.ID, "draft 1") require.NoError(t, err) _, err = svc.Create(ctx, account.ID, conversation.ID, user.ID, "draft 2") require.NoError(t, err) // List drafts filtered by user (userID > 0) drafts, err := svc.List(ctx, account.ID, conversation.ID, user.ID) require.NoError(t, err) assert.Len(t, drafts, 2) } func TestDraftMessageService_List_AllUsers(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user1 := createDraftTestUser(t, db) // Create a second user user2 := &model.User{Name: "DraftAgent2", Email: "draftagent2@test.com", Provider: "email", Role: "agent"} require.NoError(t, db.Create(user2).Error) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) // Create drafts for two different users _, err := svc.Create(ctx, account.ID, conversation.ID, user1.ID, "draft by user1") require.NoError(t, err) _, err = svc.Create(ctx, account.ID, conversation.ID, user2.ID, "draft by user2") require.NoError(t, err) // List all drafts (userID = 0 means no filter) drafts, err := svc.List(ctx, account.ID, conversation.ID, 0) require.NoError(t, err) assert.Len(t, drafts, 2) } func TestDraftMessageService_List_Empty(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) drafts, err := svc.List(ctx, account.ID, conversation.ID, 0) require.NoError(t, err) assert.Empty(t, drafts) } func TestDraftMessageService_List_ConversationNotFound(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) drafts, err := svc.List(ctx, account.ID, 99999, 0) assert.Error(t, err) assert.Nil(t, drafts) } func TestDraftMessageService_List_AccountMismatch(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) drafts, err := svc.List(ctx, 99999, conversation.ID, 0) assert.Error(t, err) assert.Nil(t, drafts) } // ========== Update Draft Tests ========== func TestDraftMessageService_Update_Success(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) draft, err := svc.Create(ctx, account.ID, conversation.ID, user.ID, "original content") require.NoError(t, err) updated, err := svc.Update(ctx, draft.ID, "updated content") require.NoError(t, err) assert.Equal(t, "updated content", updated.Content) } func TestDraftMessageService_Update_NotFound(t *testing.T) { svc, _ := setupDraftMessageService(t) ctx := context.Background() updated, err := svc.Update(ctx, 99999, "updated content") assert.Error(t, err) assert.Nil(t, updated) } // ========== Delete Draft Tests ========== func TestDraftMessageService_Delete_Success(t *testing.T) { svc, db := setupDraftMessageService(t) ctx := context.Background() account := createDraftTestAccount(t, db) user := createDraftTestUser(t, db) inbox := createDraftTestInbox(t, db, account.ID) contact := createDraftTestContact(t, db, account.ID) conversation := createDraftTestConversation(t, db, account.ID, inbox.ID, contact.ID) draft, err := svc.Create(ctx, account.ID, conversation.ID, user.ID, "draft to delete") require.NoError(t, err) err = svc.Delete(ctx, draft.ID) require.NoError(t, err) // Verify draft is deleted _, err = svc.Get(ctx, draft.ID) assert.Error(t, err) } func TestDraftMessageService_Delete_NotFound(t *testing.T) { svc, _ := setupDraftMessageService(t) ctx := context.Background() err := svc.Delete(ctx, 99999) assert.Error(t, err) }