package model import ( "testing" "github.com/stretchr/testify/assert" "gorm.io/driver/sqlite" "gorm.io/gorm" ) // setupModelTestDB creates an in-memory SQLite database with core model tables. func setupModelTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{}) if err != nil { t.Fatalf("failed to open test db: %v", err) } err = db.AutoMigrate( &Account{}, &User{}, &AccountUser{}, &Inbox{}, &Contact{}, &ContactInbox{}, &Conversation{}, &Message{}, &Attachment{}, &Notification{}, &CustomRole{}, &PlatformApp{}, ) if err != nil { t.Fatalf("failed to migrate: %v", err) } return db } func cleanupDB(t *testing.T, db *gorm.DB) { t.Helper() sqlDB, err := db.DB() if err != nil { t.Fatalf("failed to get sql.DB: %v", err) } sqlDB.Close() } // --- Account CRUD Tests --- func TestAccount_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) account := &Account{Name: "TestOrg", Locale: "en", Timezone: "UTC", Active: true} err := db.Create(account).Error assert.NoError(t, err) assert.NotZero(t, account.ID) assert.Equal(t, "TestOrg", account.Name) assert.Equal(t, "en", account.Locale) assert.Equal(t, "UTC", account.Timezone) assert.True(t, account.Active) assert.Equal(t, "active", account.Status) } func TestAccount_GetByID(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "LookupOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) var found Account err := db.First(&found, acc.ID).Error assert.NoError(t, err) assert.Equal(t, "LookupOrg", found.Name) assert.Equal(t, acc.ID, found.ID) } func TestAccount_Update(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "UpdateOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) acc.Name = "UpdatedOrg" acc.Domain = "updated.example.com" err := db.Save(acc).Error assert.NoError(t, err) var found Account db.First(&found, acc.ID) assert.Equal(t, "UpdatedOrg", found.Name) assert.Equal(t, "updated.example.com", found.Domain) } func TestAccount_SoftDelete(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "DeleteOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) err := db.Delete(acc).Error assert.NoError(t, err) var found Account err = db.First(&found, acc.ID).Error assert.Error(t, err) assert.Equal(t, gorm.ErrRecordNotFound, err) } func TestAccount_FeatureFlags(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{ Name: "FlagsOrg", Locale: "en", Timezone: "UTC", Active: true, FeatureFlags: `{"auto_resolve":true,"copilot":false}`, AutoResolveDuration: 7, } err := db.Create(acc).Error assert.NoError(t, err) var found Account db.First(&found, acc.ID) assert.Equal(t, `{"auto_resolve":true,"copilot":false}`, found.FeatureFlags) assert.Equal(t, 7, found.AutoResolveDuration) } // --- User CRUD Tests --- func TestUser_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "UserOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) user := &User{ AccountID: acc.ID, Name: "John Agent", Email: "john@example.com", Password: "hashed_password_placeholder", Provider: "email", Role: string(AccountUserRoleAgent), Active: true, } err := db.Create(user).Error assert.NoError(t, err) assert.NotZero(t, user.ID) assert.Equal(t, acc.ID, user.AccountID) assert.Equal(t, "John Agent", user.Name) assert.Equal(t, "john@example.com", user.Email) } func TestUser_UpdateRole(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "RoleOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) user := &User{ AccountID: acc.ID, Name: "AgentUser", Email: "agent_role@example.com", Password: "hashed", Provider: "email", Role: string(AccountUserRoleAgent), Active: true, } db.Create(user) user.Role = string(AccountUserRoleAdministrator) err := db.Save(user).Error assert.NoError(t, err) var found User db.First(&found, user.ID) assert.Equal(t, string(AccountUserRoleAdministrator), found.Role) } func TestUser_TOTPFields(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "TOTPOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) user := &User{ AccountID: acc.ID, Name: "TOTP User", Email: "totp@example.com", Password: "hashed", Provider: "email", Role: string(AccountUserRoleAgent), Active: true, TOTPSecret: "JBSWY3DPEHPK3PXP", TOTPEnabled: true, } err := db.Create(user).Error assert.NoError(t, err) var found User db.First(&found, user.ID) assert.Equal(t, "JBSWY3DPEHPK3PXP", found.TOTPSecret) assert.True(t, found.TOTPEnabled) } // --- AccountUser Tests --- func TestAccountUser_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "AssocOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) user := &User{ AccountID: acc.ID, Name: "AssocUser", Email: "assoc@example.com", Password: "hashed", Provider: "email", Role: string(AccountUserRoleAgent), Active: true, } db.Create(user) au := &AccountUser{ UserID: user.ID, AccountID: acc.ID, Role: string(AccountUserRoleAgent), Availability: "online", } err := db.Create(au).Error assert.NoError(t, err) assert.NotZero(t, au.ID) assert.Equal(t, user.ID, au.UserID) assert.Equal(t, acc.ID, au.AccountID) } func TestAccountUser_IsAdministrator(t *testing.T) { au := &AccountUser{Role: string(AccountUserRoleAdministrator)} assert.True(t, au.IsAdministrator()) au2 := &AccountUser{Role: string(AccountUserRoleAgent)} assert.False(t, au2.IsAdministrator()) } // --- Inbox CRUD Tests --- func TestInbox_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "InboxOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) inbox := &Inbox{ AccountID: acc.ID, Name: "Support", ChannelType: "web_widget", ChannelID: 1, EnableAutoAssignment: false, AutoAssignmentLimit: 0, } err := db.Create(inbox).Error assert.NoError(t, err) assert.NotZero(t, inbox.ID) assert.Equal(t, acc.ID, inbox.AccountID) assert.Equal(t, "Support", inbox.Name) assert.Equal(t, "web_widget", inbox.ChannelType) } // --- Conversation CRUD Tests --- func TestConversation_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "ConvOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) contact := &Contact{AccountID: acc.ID, Name: "Test Contact"} db.Create(contact) inbox := &Inbox{ AccountID: acc.ID, Name: "Chat", ChannelType: "web_widget", ChannelID: 1, } db.Create(inbox) conv := &Conversation{ AccountID: acc.ID, InboxID: inbox.ID, ContactID: contact.ID, Status: string(ConversationStatusOpen), ChannelType: "web_widget", Channel: "web_widget", } err := db.Create(conv).Error assert.NoError(t, err) assert.NotZero(t, conv.ID) assert.Equal(t, string(ConversationStatusOpen), conv.Status) } // --- Message CRUD Tests --- func TestMessage_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "MsgOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) contact := &Contact{AccountID: acc.ID, Name: "Msg Contact"} db.Create(contact) inbox := &Inbox{ AccountID: acc.ID, Name: "Chat", ChannelType: "web_widget", ChannelID: 1, } db.Create(inbox) conv := &Conversation{ AccountID: acc.ID, InboxID: inbox.ID, ContactID: contact.ID, Status: string(ConversationStatusOpen), ChannelType: "web_widget", Channel: "web_widget", } db.Create(conv) msg := &Message{ ConversationID: conv.ID, AccountID: acc.ID, InboxID: inbox.ID, SenderType: "contact", Content: "Hello world", ContentType: "text", MessageType: "incoming", } err := db.Create(msg).Error assert.NoError(t, err) assert.NotZero(t, msg.ID) assert.Equal(t, "Hello world", msg.Content) assert.Equal(t, "incoming", msg.MessageType) assert.Equal(t, "contact", msg.SenderType) } // --- Contact CRUD Tests --- func TestContact_Create(t *testing.T) { db := setupModelTestDB(t) defer cleanupDB(t, db) acc := &Account{Name: "ContactOrg", Locale: "en", Timezone: "UTC", Active: true} db.Create(acc) contact := &Contact{ AccountID: acc.ID, Name: "Jane Doe", Email: "jane@example.com", PhoneNumber: "+1234567890", } err := db.Create(contact).Error assert.NoError(t, err) assert.NotZero(t, contact.ID) assert.Equal(t, "Jane Doe", contact.Name) } // --- Enum Tests --- func TestConversationStatus_Values(t *testing.T) { assert.Equal(t, "open", string(ConversationStatusOpen)) assert.Equal(t, "resolved", string(ConversationStatusResolved)) assert.Equal(t, "pending", string(ConversationStatusPending)) assert.Equal(t, "snoozed", string(ConversationStatusSnoozed)) } func TestMessageType_Values(t *testing.T) { assert.Equal(t, "incoming", string(MessageTypeIncoming)) assert.Equal(t, "outgoing", string(MessageTypeOutgoing)) assert.Equal(t, "activity", string(MessageTypeActivity)) } func TestAccountUserRole_Values(t *testing.T) { assert.Equal(t, "agent", string(AccountUserRoleAgent)) assert.Equal(t, "administrator", string(AccountUserRoleAdministrator)) } func TestMessageContentType_Values(t *testing.T) { assert.Equal(t, "text", string(MessageContentTypeText)) } // --- PlatformApp Tests --- func uintPtr(v uint) *uint { return &v } func TestPlatformApp_IsPlatformLevel(t *testing.T) { pa := &PlatformApp{AccountID: nil} assert.True(t, pa.IsPlatformLevel()) pa2 := &PlatformApp{AccountID: uintPtr(1)} assert.False(t, pa2.IsPlatformLevel()) } // --- CustomRole Permission Level Tests (using model's own constants) --- func TestPermissionLevel_Constants(t *testing.T) { assert.Equal(t, "full", string(PermissionLevelFull)) assert.Equal(t, "read", string(PermissionLevelRead)) assert.Equal(t, "none", string(PermissionLevelNone)) } func TestPermissionDimension_Constants(t *testing.T) { assert.Equal(t, "conversation_manage", string(DimensionConversationManage)) assert.Equal(t, "conversation_delete", string(DimensionConversationDelete)) assert.Equal(t, "contact_manage", string(DimensionContactManage)) assert.Equal(t, "report_manage", string(DimensionReportManage)) assert.Equal(t, "knowledge_base_manage", string(DimensionKnowledgeBaseManage)) assert.Equal(t, "automation_manage", string(DimensionAutomationManage)) }