125 lines
4.6 KiB
Go
125 lines
4.6 KiB
Go
package service
|
|
|
|
import (
|
|
"encoding/json"
|
|
"testing"
|
|
|
|
"github.com/gochat/gochat/internal/model"
|
|
"github.com/gochat/gochat/internal/repository"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/suite"
|
|
"gorm.io/datatypes"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type ContactMergeServiceTestSuite struct {
|
|
suite.Suite
|
|
db *gorm.DB
|
|
svc *ContactMergeService
|
|
}
|
|
|
|
func (s *ContactMergeServiceTestSuite) SetupTest() {
|
|
db, err := gorm.Open(sqlite.Open("file:merge_test?mode=memory&_busy_timeout=5000"), &gorm.Config{})
|
|
assert.NoError(s.T(), err)
|
|
s.db = db
|
|
s.db.AutoMigrate(
|
|
&model.Contact{},
|
|
&model.Conversation{},
|
|
&model.Message{},
|
|
&model.ContactInbox{},
|
|
&model.ContactNote{},
|
|
&model.Note{},
|
|
)
|
|
mergeRepo := repository.NewContactMergeRepo(db)
|
|
s.svc = NewContactMergeService(mergeRepo, db)
|
|
}
|
|
|
|
func (s *ContactMergeServiceTestSuite) TearDownTest() {
|
|
if s.db != nil {
|
|
sqlDB, _ := s.db.DB()
|
|
sqlDB.Close()
|
|
}
|
|
}
|
|
|
|
func TestContactMergeServiceSuite(t *testing.T) {
|
|
suite.Run(t, new(ContactMergeServiceTestSuite))
|
|
}
|
|
|
|
func (s *ContactMergeServiceTestSuite) TestMerge_SameContact() {
|
|
// Create a contact so same-ID merge can find it
|
|
s.db.Create(&model.Contact{Base: model.Base{}, AccountID: 1, Name: "same"})
|
|
_, err := s.svc.Merge(1, 1, 1)
|
|
// Same contact should succeed — Chatwoot returns base_contact if base.id == mergee.id
|
|
assert.NoError(s.T(), err)
|
|
}
|
|
|
|
func (s *ContactMergeServiceTestSuite) TestMerge_BaseNotFound() {
|
|
s.db.Create(&model.Contact{Base: model.Base{}, AccountID: 1, Name: "mergee"})
|
|
_, err := s.svc.Merge(1, 999, 2)
|
|
assert.Error(s.T(), err)
|
|
}
|
|
|
|
func (s *ContactMergeServiceTestSuite) TestMerge_Success() {
|
|
base := &model.Contact{
|
|
Base: model.Base{},
|
|
AccountID: 1,
|
|
Name: "base",
|
|
Email: "base@example.com",
|
|
Identifier: "base-id",
|
|
AdditionalAttributes: datatypes.JSON(`{"city":"base","nested":{"keep":"base"}}`),
|
|
CustomAttributes: datatypes.JSON(`{"tier":"gold","base_only":"yes"}`),
|
|
}
|
|
mergee := &model.Contact{
|
|
Base: model.Base{},
|
|
AccountID: 1,
|
|
Name: "mergee",
|
|
Email: "mergee@example.com",
|
|
PhoneNumber: "+12212345",
|
|
AdditionalAttributes: datatypes.JSON(`{"city":"mergee","mergee_only":"yes","nested":{"new":"mergee"}}`),
|
|
CustomAttributes: datatypes.JSON(`{"tier":"silver","mergee_only":"yes"}`),
|
|
}
|
|
s.db.Create(base)
|
|
s.db.Create(mergee)
|
|
s.db.Create(&model.Conversation{AccountID: 1, InboxID: 1, ContactID: mergee.ID, Status: "open", ChannelType: "Channel::WebWidget", Channel: "web_widget"})
|
|
s.db.Create(&model.ContactInbox{ContactID: mergee.ID, InboxID: 1, SourceID: "source-mergee"})
|
|
s.db.Create(&model.Note{AccountID: 1, ContactID: mergee.ID, Content: "mergee note"})
|
|
s.db.Create(&model.Message{AccountID: 1, InboxID: 1, ConversationID: 1, SenderID: &mergee.ID, SenderType: "contact", Content: "lowercase sender", ContentType: "text"})
|
|
s.db.Create(&model.Message{AccountID: 1, InboxID: 1, ConversationID: 1, SenderID: &mergee.ID, SenderType: string(model.SenderTypeContact), Content: "polymorphic sender", ContentType: "text"})
|
|
|
|
result, err := s.svc.Merge(1, base.ID, mergee.ID)
|
|
assert.NoError(s.T(), err)
|
|
assert.Equal(s.T(), "base", result.Name)
|
|
assert.Equal(s.T(), "base@example.com", result.Email)
|
|
assert.Equal(s.T(), "base-id", result.Identifier)
|
|
assert.Equal(s.T(), "+12212345", result.PhoneNumber)
|
|
|
|
var attrs map[string]any
|
|
assert.NoError(s.T(), json.Unmarshal(result.CustomAttributes, &attrs))
|
|
assert.Equal(s.T(), "gold", attrs["tier"])
|
|
assert.Equal(s.T(), "yes", attrs["base_only"])
|
|
assert.Equal(s.T(), "yes", attrs["mergee_only"])
|
|
|
|
var additional map[string]any
|
|
assert.NoError(s.T(), json.Unmarshal(result.AdditionalAttributes, &additional))
|
|
assert.Equal(s.T(), "base", additional["city"])
|
|
assert.Equal(s.T(), "yes", additional["mergee_only"])
|
|
nested := additional["nested"].(map[string]any)
|
|
assert.Equal(s.T(), "base", nested["keep"])
|
|
assert.Equal(s.T(), "mergee", nested["new"])
|
|
|
|
// Verify mergee is soft-deleted — GORM Delete sets deleted_at
|
|
var count int64
|
|
s.db.Model(&model.Contact{}).Where("id = ?", mergee.ID).Count(&count)
|
|
assert.Equal(s.T(), int64(0), count) // GORM soft-delete: filtered out by default
|
|
|
|
s.db.Model(&model.Conversation{}).Where("contact_id = ?", base.ID).Count(&count)
|
|
assert.Equal(s.T(), int64(1), count)
|
|
s.db.Model(&model.ContactInbox{}).Where("contact_id = ?", base.ID).Count(&count)
|
|
assert.Equal(s.T(), int64(1), count)
|
|
s.db.Model(&model.Note{}).Where("contact_id = ?", base.ID).Count(&count)
|
|
assert.Equal(s.T(), int64(1), count)
|
|
s.db.Model(&model.Message{}).Where("sender_id = ?", base.ID).Count(&count)
|
|
assert.Equal(s.T(), int64(2), count)
|
|
}
|