package service import ( "context" "testing" "gorm.io/gorm" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/gochat/gochat/internal/model" ) // Helper: create prerequisite Account + Conversation + Tag for label tests func createPrerequisitesForLabelTest(t *testing.T, db *gorm.DB, tagSvc *TagService) (uint, uint, uint) { t.Helper() // Create account acct := &model.Account{Name: "test-acct", Status: "active"} require.NoError(t, db.Create(acct).Error) // Create inbox (required by Conversation foreign key) inbox := &model.Inbox{Name: "test-inbox", AccountID: acct.ID} require.NoError(t, db.Create(inbox).Error) // Create conversation conv := &model.Conversation{AccountID: acct.ID, InboxID: inbox.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) // Create tag tag, err := tagSvc.Create(context.Background(), acct.ID, &CreateTagRequest{Name: "urgent", Color: "#FF5733"}) require.NoError(t, err) return acct.ID, conv.ID, tag.ID } // ========== LabelService AddLabelToConversation ========== func TestLabelService_AddLabelToConversation_成功(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) cl, err := svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) require.NoError(t, err) assert.NotZero(t, cl.ID) assert.Equal(t, convID, cl.ConversationID) assert.Equal(t, tagID, cl.TagID) assert.Equal(t, accountID, cl.AccountID) } func TestLabelService_AddLabelToConversation_重复标签(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) _, err := svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) require.NoError(t, err) _, err = svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) assert.Error(t, err) assert.Contains(t, err.Error(), "already attached") } func TestLabelService_AddLabelToConversation_标签不属于账户(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) // Try to add tag belonging to accountID to a different account _, err := svc.AddLabelToConversation(context.Background(), accountID+1, convID, tagID) assert.Error(t, err) assert.Contains(t, err.Error(), "does not belong") } func TestLabelService_AddLabelToConversation_标签不存在(t *testing.T) { _, _, _, svc := setupLabelService(t) _, err := svc.AddLabelToConversation(context.Background(), 1, 1, 9999) assert.Error(t, err) assert.Contains(t, err.Error(), "tag not found") } // ========== LabelService RemoveLabelFromConversation ========== func TestLabelService_RemoveLabelFromConversation_成功(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) _, err := svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) require.NoError(t, err) err = svc.RemoveLabelFromConversation(context.Background(), convID, tagID) require.NoError(t, err) labels, err := svc.GetConversationLabels(context.Background(), convID) require.NoError(t, err) assert.Len(t, labels, 0) } // ========== LabelService GetConversationLabels ========== func TestLabelService_GetConversationLabels_成功(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) _, err := svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) require.NoError(t, err) // Create second tag and add it tag2, err := tagSvc.Create(context.Background(), accountID, &CreateTagRequest{Name: "feature", Color: "#00FF00"}) require.NoError(t, err) _, err = svc.AddLabelToConversation(context.Background(), accountID, convID, tag2.ID) require.NoError(t, err) labels, err := svc.GetConversationLabels(context.Background(), convID) require.NoError(t, err) assert.Len(t, labels, 2) } // ========== LabelService ReplaceConversationLabels ========== func TestLabelService_ReplaceConversationLabels_成功(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) // Add initial label _, err := svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) require.NoError(t, err) // Create a second tag tag2, err := tagSvc.Create(context.Background(), accountID, &CreateTagRequest{Name: "new-label", Color: "#0000FF"}) require.NoError(t, err) // Replace labels — remove old, add new result, err := svc.ReplaceConversationLabels(context.Background(), accountID, convID, []uint{tag2.ID}) require.NoError(t, err) assert.Len(t, result, 1) assert.Equal(t, tag2.ID, result[0].TagID) } func TestLabelService_ReplaceConversationLabels_清空标签(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) accountID, convID, tagID := createPrerequisitesForLabelTest(t, db, tagSvc) // Add initial label _, err := svc.AddLabelToConversation(context.Background(), accountID, convID, tagID) require.NoError(t, err) // Replace with empty list result, err := svc.ReplaceConversationLabels(context.Background(), accountID, convID, []uint{}) require.NoError(t, err) assert.Len(t, result, 0) } // ========== LabelService BatchAddLabel ========== func TestLabelService_BatchAddLabel_成功(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) // Create account + inbox + 2 conversations + tag acct := &model.Account{Name: "test-acct", Status: "active"} require.NoError(t, db.Create(acct).Error) inbox := &model.Inbox{Name: "test-inbox", AccountID: acct.ID} require.NoError(t, db.Create(inbox).Error) conv1 := &model.Conversation{AccountID: acct.ID, InboxID: inbox.ID, Status: "open"} require.NoError(t, db.Create(conv1).Error) conv2 := &model.Conversation{AccountID: acct.ID, InboxID: inbox.ID, Status: "open"} require.NoError(t, db.Create(conv2).Error) tag, err := tagSvc.Create(context.Background(), acct.ID, &CreateTagRequest{Name: "bulk-tag", Color: "#123456"}) require.NoError(t, err) err = svc.BatchAddLabel(context.Background(), acct.ID, &BatchAddLabelRequest{ TagID: tag.ID, ConversationIDs: []uint{conv1.ID, conv2.ID}, }) require.NoError(t, err) labels1, err := svc.GetConversationLabels(context.Background(), conv1.ID) require.NoError(t, err) assert.Len(t, labels1, 1) labels2, err := svc.GetConversationLabels(context.Background(), conv2.ID) require.NoError(t, err) assert.Len(t, labels2, 1) } // ========== LabelService BatchRemoveLabel ========== func TestLabelService_BatchRemoveLabel_成功(t *testing.T) { db, _, tagRepo, svc := setupLabelService(t) tagSvc := NewTagService(tagRepo) // Create account + inbox + 2 conversations + tag acct := &model.Account{Name: "test-acct", Status: "active"} require.NoError(t, db.Create(acct).Error) inbox := &model.Inbox{Name: "test-inbox", AccountID: acct.ID} require.NoError(t, db.Create(inbox).Error) conv1 := &model.Conversation{AccountID: acct.ID, InboxID: inbox.ID, Status: "open"} require.NoError(t, db.Create(conv1).Error) conv2 := &model.Conversation{AccountID: acct.ID, InboxID: inbox.ID, Status: "open"} require.NoError(t, db.Create(conv2).Error) tag, err := tagSvc.Create(context.Background(), acct.ID, &CreateTagRequest{Name: "bulk-tag", Color: "#123456"}) require.NoError(t, err) // First add labels err = svc.BatchAddLabel(context.Background(), acct.ID, &BatchAddLabelRequest{ TagID: tag.ID, ConversationIDs: []uint{conv1.ID, conv2.ID}, }) require.NoError(t, err) // Then remove them err = svc.BatchRemoveLabel(context.Background(), acct.ID, &BatchRemoveLabelRequest{ TagID: tag.ID, ConversationIDs: []uint{conv1.ID, conv2.ID}, }) require.NoError(t, err) labels1, err := svc.GetConversationLabels(context.Background(), conv1.ID) require.NoError(t, err) assert.Len(t, labels1, 0) labels2, err := svc.GetConversationLabels(context.Background(), conv2.ID) require.NoError(t, err) assert.Len(t, labels2, 0) }