package repository import ( "context" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" "github.com/gochat/gochat/internal/model" ) // createTestAttachment is a helper that creates and persists an Attachment. func createTestAttachment(t *testing.T, db *gorm.DB, messageID, accountID uint, fileType, fileName string) *model.Attachment { t.Helper() att := &model.Attachment{ MessageID: messageID, AccountID: accountID, FileType: fileType, FileName: fileName, FileSize: 1024, } require.NoError(t, db.Create(att).Error) return att } // setupAttachmentTest prepares the full dependency chain (account → inbox → contact → conversation → message) // and returns the db, repo, and pre-created message for convenience. func setupAttachmentTest(t *testing.T) (*gorm.DB, *AttachmentRepo, *model.Message) { t.Helper() db := setupTestDB(t, &model.Attachment{}) repo := NewAttachmentRepo(db) account := &model.Account{Name: "AttTestOrg", Locale: "en", Active: true} require.NoError(t, db.Create(account).Error) inbox := &model.Inbox{AccountID: account.ID, Name: "AttTestInbox", ChannelType: "web_widget", ChannelID: 1} require.NoError(t, db.Create(inbox).Error) contact := &model.Contact{AccountID: account.ID, Name: "AttTestContact"} require.NoError(t, db.Create(contact).Error) conv := &model.Conversation{ AccountID: account.ID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open", ChannelType: "web_widget", Channel: "web_widget", } require.NoError(t, db.Create(conv).Error) msg := createTestMessage(t, db, conv.ID, account.ID, inbox.ID, "Hello with attachment") return db, repo, msg } // --- FindByID --- func TestAttachmentRepo_FindByID_Success(t *testing.T) { db, repo, msg := setupAttachmentTest(t) att := createTestAttachment(t, db, msg.ID, msg.AccountID, "image", "photo.png") found, err := repo.FindByID(context.Background(), att.ID) assert.NoError(t, err) assert.Equal(t, att.ID, found.ID) assert.Equal(t, att.MessageID, found.MessageID) assert.Equal(t, att.FileType, found.FileType) assert.Equal(t, att.FileName, found.FileName) } func TestAttachmentRepo_FindByID_NotFound(t *testing.T) { _, repo, _ := setupAttachmentTest(t) found, err := repo.FindByID(context.Background(), 99999) assert.Error(t, err) assert.Nil(t, found) } // --- FindByMessage --- func TestAttachmentRepo_FindByMessage_Multiple(t *testing.T) { db, repo, msg := setupAttachmentTest(t) att1 := createTestAttachment(t, db, msg.ID, msg.AccountID, "image", "photo.png") att2 := createTestAttachment(t, db, msg.ID, msg.AccountID, "file", "doc.pdf") attachments, err := repo.FindByMessage(context.Background(), msg.ID) assert.NoError(t, err) assert.Len(t, attachments, 2) // Should be ordered by id ASC assert.Equal(t, att1.ID, attachments[0].ID) assert.Equal(t, att2.ID, attachments[1].ID) } func TestAttachmentRepo_FindByMessage_Empty(t *testing.T) { _, repo, msg := setupAttachmentTest(t) attachments, err := repo.FindByMessage(context.Background(), msg.ID) assert.NoError(t, err) assert.Len(t, attachments, 0) } // --- Create --- func TestAttachmentRepo_Create_Success(t *testing.T) { db, repo, msg := setupAttachmentTest(t) att := &model.Attachment{ MessageID: msg.ID, AccountID: msg.AccountID, FileType: "video", FileURL: "https://example.com/video.mp4", ThumbURL: "https://example.com/thumb.jpg", FileName: "video.mp4", FileSize: 2048, ExternalURL: "https://example.com/external", } err := repo.Create(context.Background(), att) assert.NoError(t, err) assert.NotZero(t, att.ID) // Verify persisted var fetched model.Attachment require.NoError(t, db.First(&fetched, att.ID).Error) assert.Equal(t, "video", fetched.FileType) assert.Equal(t, "https://example.com/video.mp4", fetched.FileURL) } // --- Update --- func TestAttachmentRepo_Update_Success(t *testing.T) { db, repo, msg := setupAttachmentTest(t) att := createTestAttachment(t, db, msg.ID, msg.AccountID, "image", "old.png") att.FileName = "new.png" att.FileSize = 4096 err := repo.Update(context.Background(), att) assert.NoError(t, err) // Verify updated var fetched model.Attachment require.NoError(t, db.First(&fetched, att.ID).Error) assert.Equal(t, "new.png", fetched.FileName) assert.Equal(t, 4096, fetched.FileSize) } // --- Delete --- func TestAttachmentRepo_Delete_Success(t *testing.T) { db, repo, msg := setupAttachmentTest(t) att := createTestAttachment(t, db, msg.ID, msg.AccountID, "image", "photo.png") err := repo.Delete(context.Background(), att.ID) assert.NoError(t, err) // Verify soft-deleted: normal query should not find it var found model.Attachment err = db.First(&found, att.ID).Error assert.Error(t, err) // record not found after soft delete // Verify unscoped query finds it with DeletedAt set var softDeleted model.Attachment err = db.Unscoped().First(&softDeleted, att.ID).Error assert.NoError(t, err) assert.NotNil(t, softDeleted.DeletedAt) } func TestAttachmentRepo_Delete_NotFound(t *testing.T) { _, repo, _ := setupAttachmentTest(t) // Deleting a non-existent ID should not error (GORM soft delete behaviour) err := repo.Delete(context.Background(), 99999) assert.NoError(t, err) } // --- DeleteByMessage --- func TestAttachmentRepo_DeleteByMessage_Success(t *testing.T) { db, repo, msg := setupAttachmentTest(t) att1 := createTestAttachment(t, db, msg.ID, msg.AccountID, "image", "photo1.png") att2 := createTestAttachment(t, db, msg.ID, msg.AccountID, "file", "doc.pdf") err := repo.DeleteByMessage(context.Background(), msg.ID) assert.NoError(t, err) // Both attachments should be soft-deleted var count int64 db.Model(&model.Attachment{}).Where("message_id = ?", msg.ID).Count(&count) assert.Equal(t, int64(0), count) // Unscoped: both should still exist var allCount int64 db.Unscoped().Model(&model.Attachment{}).Where("message_id = ?", msg.ID).Count(&count) allCount = count assert.Equal(t, int64(2), allCount) // Verify individual DeletedAt var sd1, sd2 model.Attachment require.NoError(t, db.Unscoped().First(&sd1, att1.ID).Error) assert.NotNil(t, sd1.DeletedAt) require.NoError(t, db.Unscoped().First(&sd2, att2.ID).Error) assert.NotNil(t, sd2.DeletedAt) }