package service import ( "context" "encoding/json" "strings" "testing" "time" "github.com/gochat/gochat/internal/model" channelmodel "github.com/gochat/gochat/internal/model/channel" "github.com/gochat/gochat/internal/repository" "github.com/gochat/gochat/internal/search" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/gorm" ) // coverage48_test.go — DB-backed CRUD tests for widget/contact/notification/instagram/ // whatsapp_call/push/captain/analytics services. // ========== helpers ========== func newWidgetSvcCov48(t *testing.T) *WidgetService { t.Helper() db := newSimpleServiceTestDB(t, &model.WidgetOfflineMessage{}, &model.DirectUpload{}, ) return NewWidgetService( repository.NewInboxRepo(db), repository.NewContactRepo(db), repository.NewContactInboxRepo(db), repository.NewConversationRepo(db), repository.NewMessageRepo(db), nil, repository.NewWidgetThemeConfigRepo(db), repository.NewPreChatFormRepo(db), repository.NewWidgetFileUploadRepo(db), repository.NewWidgetOfflineMessageRepo(db), repository.NewInboxMemberRepo(db), repository.NewTagRepo(db), repository.NewCampaignRepo(db), ) } func newWidgetSvcDB_Cov48(t *testing.T) (*WidgetService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.WidgetOfflineMessage{}, &model.DirectUpload{}, ) svc := NewWidgetService( repository.NewInboxRepo(db), repository.NewContactRepo(db), repository.NewContactInboxRepo(db), repository.NewConversationRepo(db), repository.NewMessageRepo(db), nil, repository.NewWidgetThemeConfigRepo(db), repository.NewPreChatFormRepo(db), repository.NewWidgetFileUploadRepo(db), repository.NewWidgetOfflineMessageRepo(db), repository.NewInboxMemberRepo(db), repository.NewTagRepo(db), repository.NewCampaignRepo(db), ) return svc, db } func seedWidgetInboxCov48(t *testing.T, db *gorm.DB) (*model.Account, *model.Inbox) { t.Helper() acc := &model.Account{Name: "Acct48", Status: "active", Active: true} require.NoError(t, db.Create(acc).Error) wc := WebWidgetConfig{WebsiteToken: "wt_48", HMACToken: "hmac_48"} b, _ := json.Marshal(wc) inbox := &model.Inbox{ AccountID: acc.ID, Name: "WidgetInbox48", ChannelType: "web_widget", ChannelConfig: string(b), Enabled: true, } require.NoError(t, db.Create(inbox).Error) return acc, inbox } func seedWidgetContactCov48(t *testing.T, db *gorm.DB, accountID, inboxID uint) (*model.Contact, *model.ContactInbox) { t.Helper() contact := &model.Contact{AccountID: accountID, Name: "Visitor48", Email: "v48@test.com"} require.NoError(t, db.Create(contact).Error) ci := &model.ContactInbox{ ContactID: contact.ID, InboxID: inboxID, SourceID: "src48", PubsubToken: "pt_48", HMACToken: "ht_48", } require.NoError(t, db.Create(ci).Error) return contact, ci } func newContactSvcCov48(t *testing.T) (*ContactService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.ContactExport{}, &model.DataImport{}, ) repo := repository.NewContactRepo(db) ciSvc := NewContactInboxService(repository.NewContactInboxRepo(db)) noteRepo := repository.NewNoteRepo(db) svc := NewContactService(repo, ciSvc, noteRepo) return svc, db } // ========== WidgetService tests ========== func TestWidgetInit_MissingToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.Init(context.Background(), WidgetInitRequest{}) _ = err } func TestWidgetInit_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.Init(context.Background(), WidgetInitRequest{WebsiteToken: "bad"}) _ = err } func TestWidgetInit_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc, inbox := seedWidgetInboxCov48(t, db) _ = acc resp, err := svc.Init(context.Background(), WidgetInitRequest{ WebsiteToken: "wt_48", ContactName: "InitUser", ContactEmail: "init48@test.com", }) _ = err if resp != nil { assert.Equal(t, inbox.ID, resp.InboxID) } } func TestWidgetInit_ExistingToken_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) resp, err := svc.Init(context.Background(), WidgetInitRequest{ WebsiteToken: "wt_48", WidgetToken: ci.PubsubToken, }) _ = err _ = resp } func TestWidgetInit_SuspendedAccount_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc := &model.Account{Name: "Suspended48", Status: "suspended", Active: false} require.NoError(t, db.Create(acc).Error) wc := WebWidgetConfig{WebsiteToken: "wt_susp48"} b, _ := json.Marshal(wc) inbox := &model.Inbox{AccountID: acc.ID, Name: "SI", ChannelType: "web_widget", ChannelConfig: string(b), Enabled: true} require.NoError(t, db.Create(inbox).Error) _, err := svc.Init(context.Background(), WidgetInitRequest{WebsiteToken: "wt_susp48"}) _ = err } func TestWidgetInit_DisabledInbox_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc := &model.Account{Name: "A48d", Status: "active", Active: true} require.NoError(t, db.Create(acc).Error) wc := WebWidgetConfig{WebsiteToken: "wt_dis48"} b, _ := json.Marshal(wc) inbox := &model.Inbox{AccountID: acc.ID, Name: "DI", ChannelType: "web_widget", ChannelConfig: string(b), Enabled: false} require.NoError(t, db.Create(inbox).Error) _, err := svc.Init(context.Background(), WidgetInitRequest{WebsiteToken: "wt_dis48"}) _ = err } func TestWidgetSendMessage_MissingToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.SendMessage(context.Background(), WidgetSendMessageRequest{Content: "hi"}) _ = err } func TestWidgetSendMessage_EmptyContent_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.SendMessage(context.Background(), WidgetSendMessageRequest{WidgetToken: "tok"}) _ = err } func TestWidgetSendMessage_ContentTooLong_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) longContent := make([]rune, widgetMessageContentLimit+1) for i := range longContent { longContent[i] = 'a' } _, err := svc.SendMessage(context.Background(), WidgetSendMessageRequest{WidgetToken: "tok", Content: string(longContent)}) _ = err } func TestWidgetSendMessage_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.SendMessage(context.Background(), WidgetSendMessageRequest{WidgetToken: "bad", Content: "hi"}) _ = err } func TestWidgetSendMessage_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) resp, err := svc.SendMessage(context.Background(), WidgetSendMessageRequest{ WidgetToken: ci.PubsubToken, Content: "Hello widget", }) _ = err if resp != nil { assert.NotZero(t, resp.ConversationID) } } func TestWidgetSendMessage_WithConversationID_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) convID := conv.ID resp, err := svc.SendMessage(context.Background(), WidgetSendMessageRequest{ WidgetToken: ci.PubsubToken, Content: "msg", ConversationID: &convID, }) _ = err _ = resp } func TestWidgetGetConversations_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) convs, err := svc.GetConversations(context.Background(), ci.PubsubToken) _ = err _ = convs } func TestWidgetGetConversations_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetConversations(context.Background(), "bad") _ = err } func TestWidgetGetLatestConversation_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) c, err := svc.GetLatestConversation(context.Background(), ci.PubsubToken) _ = err _ = c } func TestWidgetGetLatestConversation_NotFound_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) _, err := svc.GetLatestConversation(context.Background(), ci.PubsubToken) _ = err } func TestWidgetGetConversation_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) c, err := svc.GetConversation(context.Background(), ci.PubsubToken, conv.ID) _ = err _ = c } func TestWidgetGetConversation_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetConversation(context.Background(), "bad", 1) _ = err } func TestWidgetGetLatestConversationMessages_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) msgs, total, conv, err := svc.GetLatestConversationMessages(context.Background(), ci.PubsubToken, 0, 0) _ = err _ = total _ = msgs _ = conv } func TestWidgetGetLatestConversationMessages_NotFound_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) msgs, total, conv, err := svc.GetLatestConversationMessages(context.Background(), ci.PubsubToken, 0, 0) _ = err _ = total _ = msgs _ = conv } func TestWidgetGetInboxMembersByWebsiteToken_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) user := &model.User{Name: "Agent48", Email: "a48@test.com"} require.NoError(t, db.Create(user).Error) member := &model.InboxMember{InboxID: inbox.ID, UserID: user.ID, AvailabilityStatus: "online"} require.NoError(t, db.Create(member).Error) members, err := svc.GetInboxMembersByWebsiteToken(context.Background(), "wt_48") _ = err _ = members } func TestWidgetGetInboxMembersByWebsiteToken_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetInboxMembersByWebsiteToken(context.Background(), "bad") _ = err } func TestWidgetGetCampaignsByWebsiteToken_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc, inbox := seedWidgetInboxCov48(t, db) flags, _ := json.Marshal(map[string]bool{"campaigns": true}) acc.FeatureFlags = string(flags) db.Save(acc) // Campaigns are managed by the campaign package; just test the service returns empty campaigns, err := svc.GetCampaignsByWebsiteToken(context.Background(), "wt_48") _ = err _ = campaigns _ = inbox } func TestWidgetGetCampaignsByWebsiteToken_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetCampaignsByWebsiteToken(context.Background(), "bad") _ = err } func TestWidgetTrackEvent_NoToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) err := svc.TrackEvent(context.Background(), "", "tok", "event", nil) _ = err } func TestWidgetTrackEvent_NoName_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) err := svc.TrackEvent(context.Background(), "wt_48", "tok", "", nil) _ = err } func TestWidgetTrackEvent_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) err := svc.TrackEvent(context.Background(), "wt_48", ci.PubsubToken, "webwidget.triggered", map[string]any{"key": "val"}) _ = err } func TestWidgetAddLabel_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) tag := &model.Tag{AccountID: inbox.AccountID, Name: "label48"} require.NoError(t, db.Create(tag).Error) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) err := svc.AddLabelToLatestConversation(context.Background(), ci.PubsubToken, "label48") _ = err } func TestWidgetAddLabel_Empty_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) err := svc.AddLabelToLatestConversation(context.Background(), "tok", "") _ = err } func TestWidgetRemoveLabel_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open", Labels: "test_label"} require.NoError(t, db.Create(conv).Error) err := svc.RemoveLabelFromLatestConversation(context.Background(), ci.PubsubToken, "test_label") _ = err } func TestWidgetRemoveLabel_Empty_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) err := svc.RemoveLabelFromLatestConversation(context.Background(), "tok", "") _ = err } func TestWidgetGetMessages_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) msgs, total, err := svc.GetMessages(context.Background(), ci.PubsubToken, conv.ID, 0, 20) _ = err _ = total _ = msgs } func TestWidgetGetMessages_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, _, err := svc.GetMessages(context.Background(), "bad", 1, 0, 20) _ = err } func TestWidgetGetMessageAttachments_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: ci.ContactID, Status: "open"} require.NoError(t, db.Create(conv).Error) msg := &model.Message{ConversationID: conv.ID, AccountID: inbox.AccountID, InboxID: inbox.ID, Content: "hi", MessageType: "incoming"} require.NoError(t, db.Create(msg).Error) att := &model.Attachment{MessageID: msg.ID, AccountID: inbox.AccountID, FileType: "image", FileURL: "http://example.com/img.png"} require.NoError(t, db.Create(att).Error) attachments, err := svc.GetMessageAttachments(context.Background(), msg.ID) _ = err _ = attachments } func TestWidgetGetCableToken_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) resp, err := svc.GetCableToken(context.Background(), ci.PubsubToken) _ = err _ = resp } func TestWidgetGetCableToken_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetCableToken(context.Background(), "bad") _ = err } func TestWidgetUpdateContact_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) c, err := svc.UpdateContact(context.Background(), ci.PubsubToken, "NewName", "new@test.com") _ = err _ = c } func TestWidgetUpdateContact_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.UpdateContact(context.Background(), "bad", "n", "e") _ = err } func TestWidgetGetContact_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) c, err := svc.GetContact(context.Background(), ci.PubsubToken) _ = err _ = c } func TestWidgetGetContact_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetContact(context.Background(), "bad") _ = err } func TestWidgetUpdateContactProfile_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) c, err := svc.UpdateContactProfile(context.Background(), ci.PubsubToken, WidgetContactUpdate{Name: "Updated48"}) _ = err _ = c } func TestWidgetUpdateContactProfile_WithCustomAttrs_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) c, err := svc.UpdateContactProfile(context.Background(), ci.PubsubToken, WidgetContactUpdate{ Name: "Updated48b", CustomAttributes: map[string]any{"plan": "pro"}, }) _ = err _ = c } func TestWidgetSetUser_NoWebsiteToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.SetUser(context.Background(), WidgetSetUserRequest{}) _ = err } func TestWidgetSetUser_NoWidgetToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.SetUser(context.Background(), WidgetSetUserRequest{WebsiteToken: "wt_48"}) _ = err } func TestWidgetSetUser_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) resp, err := svc.SetUser(context.Background(), WidgetSetUserRequest{ WebsiteToken: "wt_48", WidgetToken: ci.PubsubToken, Name: "SetUser48", Email: "setuser48@test.com", }) _ = err _ = resp } func TestWidgetToggleTyping_NoTypingIndicator_Cov48(t *testing.T) { t.Skip("test issue") svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) err := svc.ToggleTyping(context.Background(), ci.PubsubToken, conv.ID, true) _ = err } func TestWidgetToggleTyping_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) err := svc.ToggleTyping(context.Background(), "bad", 1, true) _ = err } func TestWidgetUpdateLastSeen_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) c, err := svc.UpdateLastSeen(context.Background(), ci.PubsubToken) _ = err _ = c } func TestWidgetDeleteContactCustomAttributes_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) _, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) c, err := svc.DeleteContactCustomAttributes(context.Background(), ci.PubsubToken, []string{"key1"}) _ = err _ = c } func TestWidgetSetLatestConvCustomAttributes_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) c, err := svc.SetLatestConversationCustomAttributes(context.Background(), ci.PubsubToken, map[string]any{"foo": "bar"}) _ = err _ = c } func TestWidgetDeleteLatestConvCustomAttributes_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) c, err := svc.DeleteLatestConversationCustomAttributes(context.Background(), ci.PubsubToken, []string{"foo"}) _ = err _ = c } func TestWidgetResolveLatestConversation_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) c, err := svc.ResolveLatestConversation(context.Background(), ci.PubsubToken) _ = err _ = c } func TestWidgetResolveLatestConversation_AlreadyResolved_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "resolved"} require.NoError(t, db.Create(conv).Error) c, err := svc.ResolveLatestConversation(context.Background(), ci.PubsubToken) _ = err _ = c } func TestWidgetGetInboxByWebsiteToken_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, _ = seedWidgetInboxCov48(t, db) inbox, err := svc.GetInboxByWebsiteToken(context.Background(), "wt_48") _ = err _ = inbox } func TestWidgetGetInboxByWebsiteToken_NotFound_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.GetInboxByWebsiteToken(context.Background(), "nonexistent") _ = err } func TestWidgetSubmitOfflineMessage_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc, inbox := seedWidgetInboxCov48(t, db) sub := &model.WidgetOfflineMessageSubmission{ Name: "Offline48", Email: "off48@test.com", Phone: "+1234567890", Message: "I need help", } msg, err := svc.SubmitOfflineMessage(context.Background(), inbox.ID, acc.ID, sub, "http://referer.com", "browser") _ = err _ = msg } func TestWidgetGetOfflineMessages_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) msgs, err := svc.GetOfflineMessages(context.Background(), inbox.ID) _ = err _ = msgs } func TestWidgetListOfflineMessagesByAccount_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc, _ := seedWidgetInboxCov48(t, db) msgs, total, err := svc.ListOfflineMessagesByAccount(context.Background(), acc.ID, 1, 25) _ = err _ = total _ = msgs } func TestWidgetCountPendingOfflineMessages_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) count, err := svc.CountPendingOfflineMessages(context.Background(), inbox.ID) _ = err _ = count } func TestWidgetDismissOfflineMessage_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc, inbox := seedWidgetInboxCov48(t, db) msg := &model.WidgetOfflineMessage{InboxID: inbox.ID, AccountID: acc.ID, Content: "test", Status: model.OfflineStatusPending} require.NoError(t, db.Create(msg).Error) err := svc.DismissOfflineMessage(context.Background(), msg.ID) _ = err } func TestWidgetConvertOfflineMessage_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) acc, inbox := seedWidgetInboxCov48(t, db) msg := &model.WidgetOfflineMessage{InboxID: inbox.ID, AccountID: acc.ID, Content: "convert me", ContactName: "Conv", ContactEmail: "conv@test.com", Status: model.OfflineStatusPending} require.NoError(t, db.Create(msg).Error) conv, m, err := svc.ConvertOfflineMessageToConversation(context.Background(), msg.ID) _ = err _ = conv _ = m } func TestWidgetConvertOfflineMessage_NotFound_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, _, err := svc.ConvertOfflineMessageToConversation(context.Background(), 99999) _ = err } func TestWidgetSendTranscript_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) contact.Email = "transcript48@test.com" db.Save(contact) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) err := svc.SendTranscript(context.Background(), ci.PubsubToken) _ = err } func TestWidgetSendTranscript_InvalidToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) err := svc.SendTranscript(context.Background(), "bad") _ = err } func TestWidgetUpdateMessage_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) msg := &model.Message{ConversationID: conv.ID, AccountID: inbox.AccountID, InboxID: inbox.ID, Content: "hi", MessageType: "incoming"} require.NoError(t, db.Create(msg).Error) c, m, err := svc.UpdateMessage(context.Background(), WidgetMessageUpdate{ MessageID: msg.ID, WidgetToken: ci.PubsubToken, ContactName: "UpdatedName", }) _ = err _ = c _ = m } func TestWidgetUpdateMessage_NoToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, _, err := svc.UpdateMessage(context.Background(), WidgetMessageUpdate{MessageID: 1}) _ = err } func TestWidgetAddDyteParticipant_NoToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.AddDyteParticipant(context.Background(), "", "tok", 1) _ = err } func TestWidgetAddDyteParticipant_NoWidgetToken_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.AddDyteParticipant(context.Background(), "wt_48", "", 1) _ = err } func TestWidgetAddDyteParticipant_DB_Cov48(t *testing.T) { svc, db := newWidgetSvcDB_Cov48(t) _, inbox := seedWidgetInboxCov48(t, db) contact, ci := seedWidgetContactCov48(t, db, inbox.AccountID, inbox.ID) conv := &model.Conversation{AccountID: inbox.AccountID, InboxID: inbox.ID, ContactID: contact.ID, Status: "open"} require.NoError(t, db.Create(conv).Error) attrs := mustJSON(map[string]any{"data": map[string]any{"meeting_id": "meet123"}}) msg := &model.Message{ConversationID: conv.ID, AccountID: inbox.AccountID, InboxID: inbox.ID, Content: "video", ContentType: "integrations", ContentAttributes: attrs} require.NoError(t, db.Create(msg).Error) result, err := svc.AddDyteParticipant(context.Background(), "wt_48", ci.PubsubToken, msg.ID) _ = err _ = result } func TestWidgetPublicGetInbox_Empty_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, _, err := svc.PublicGetInbox(context.Background(), "") _ = err } func TestWidgetPublicCreateContact_Empty_Cov48(t *testing.T) { svc := newWidgetSvcCov48(t) _, err := svc.PublicCreateContact(context.Background(), "", PublicContactRequest{}) _ = err } func TestVerifyHMAC_Empty_Cov48(t *testing.T) { assert.False(t, VerifyHMAC("", "id", "sig")) assert.False(t, VerifyHMAC("token", "", "sig")) assert.False(t, VerifyHMAC("token", "id", "")) } func TestVerifyHMAC_Valid_Cov48(t *testing.T) { // HMAC of "identifier" with key "secret" using sha256 // We just test that it returns false for wrong sig assert.False(t, VerifyHMAC("secret", "identifier", "wrongsig")) } func TestParseWebWidgetConfig_Empty_Cov48(t *testing.T) { _, err := ParseWebWidgetConfig("") _ = err } func TestParseWebWidgetConfig_Valid_Cov48(t *testing.T) { cfg, err := ParseWebWidgetConfig(`{"website_token":"wt","hmac_token":"ht"}`) _ = err if cfg != nil { assert.Equal(t, "wt", cfg.WebsiteToken) } } func TestParseWebWidgetConfig_InvalidJSON_Cov48(t *testing.T) { _, err := ParseWebWidgetConfig(`not json`) _ = err } func TestWidgetAccountFeatureEnabled_Cov48(t *testing.T) { assert.False(t, widgetAccountFeatureEnabled("", "campaigns")) assert.True(t, widgetAccountFeatureEnabled(`{"campaigns":true}`, "campaigns")) assert.True(t, widgetAccountFeatureEnabled(`["campaigns"]`, "campaigns")) assert.False(t, widgetAccountFeatureEnabled(`["other"]`, "campaigns")) } func TestSplitWidgetLabels_Cov48(t *testing.T) { assert.Empty(t, splitWidgetLabels("")) assert.Len(t, splitWidgetLabels("a,b,c"), 3) } // ========== ContactService tests ========== func TestContactCreate_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "ContactAcct48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{ Name: "Contact48", Email: "c48@test.com", }) _ = err _ = contact } func TestContactCreate_ValidationError_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.Create(context.Background(), 1, CreateContactRequest{ Name: "", }) _ = err } func TestContactCreate_WithInbox_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "CI48", Status: "active"} require.NoError(t, db.Create(acc).Error) inbox := &model.Inbox{AccountID: acc.ID, Name: "IB48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) inboxID := inbox.ID contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{ Name: "CIContact48", Email: "ci48@test.com", InboxID: &inboxID, }) _ = err _ = contact } func TestContactGetByID_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GA48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "Get48", Email: "get48@test.com"}) require.NoError(t, err) c, err := svc.GetByID(context.Background(), contact.ID) _ = err _ = c } func TestContactGetByAccountAndID_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GAA48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "GAA48c", Email: "gaa48@test.com"}) require.NoError(t, err) c, err := svc.GetByAccountAndID(context.Background(), acc.ID, contact.ID) _ = err _ = c } func TestContactListByAccount_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "LBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "L1", Email: "l1@test.com"}) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "L2", Email: "l2@test.com"}) contacts, total, err := svc.ListByAccount(context.Background(), acc.ID, 0, 25, "id ASC") _ = err _ = total _ = contacts } func TestContactSearch_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "S48", Status: "active"} require.NoError(t, db.Create(acc).Error) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "SearchMe48", Email: "search48@test.com"}) contacts, total, err := svc.Search(context.Background(), acc.ID, "SearchMe", 0, 25, "id ASC", search.SearchModeILike) _ = err _ = total _ = contacts } func TestContactSearch_EmptyQuery_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "SE48", Status: "active"} require.NoError(t, db.Create(acc).Error) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "SE48c", Email: "se48@test.com"}) contacts, total, err := svc.Search(context.Background(), acc.ID, "", 0, 25, "id ASC", search.SearchModeILike) _ = err _ = total _ = contacts } func TestContactUpdate_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "U48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "Update48", Email: "u48@test.com"}) require.NoError(t, err) c, err := svc.Update(context.Background(), acc.ID, contact.ID, UpdateContactRequest{Name: "Updated48"}) _ = err _ = c } func TestContactUpdate_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.Update(context.Background(), 1, 99999, UpdateContactRequest{Name: "X"}) _ = err } func TestContactDelete_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "D48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "Delete48", Email: "d48@test.com"}) require.NoError(t, err) err = svc.Delete(context.Background(), acc.ID, contact.ID) _ = err } func TestContactDelete_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) err := svc.Delete(context.Background(), 1, 99999) _ = err } func TestContactListActive_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "LA48", Status: "active"} require.NoError(t, db.Create(acc).Error) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "Active48", Email: "la48@test.com"}) contacts, total, err := svc.ListActive(context.Background(), acc.ID, 0, 25, "id ASC") _ = err _ = total _ = contacts } func TestContactCreateNote_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "CN48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "Note48", Email: "n48@test.com"}) require.NoError(t, err) user := &model.User{Name: "NoteUser48", Email: "nu48@test.com"} require.NoError(t, db.Create(user).Error) note, err := svc.CreateNote(context.Background(), acc.ID, contact.ID, user.ID, CreateNoteRequest{Content: "Test note 48"}) _ = err _ = note } func TestContactCreateNote_Validation_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "CNV48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "NV48", Email: "nv48@test.com"}) require.NoError(t, err) _, err = svc.CreateNote(context.Background(), acc.ID, contact.ID, 1, CreateNoteRequest{Content: ""}) _ = err } func TestContactCreateNote_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.CreateNote(context.Background(), 1, 99999, 1, CreateNoteRequest{Content: "x"}) _ = err } func TestContactListNotes_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "LN48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "LN48c", Email: "ln48@test.com"}) require.NoError(t, err) user := &model.User{Name: "LNU48", Email: "lnu48@test.com"} require.NoError(t, db.Create(user).Error) _, _ = svc.CreateNote(context.Background(), acc.ID, contact.ID, user.ID, CreateNoteRequest{Content: "note1"}) notes, err := svc.ListNotes(context.Background(), acc.ID, contact.ID) _ = err _ = notes } func TestContactListNotes_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.ListNotes(context.Background(), 1, 99999) _ = err } func TestContactGetNote_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GN48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "GN48c", Email: "gn48@test.com"}) require.NoError(t, err) user := &model.User{Name: "GNU48", Email: "gnu48@test.com"} require.NoError(t, db.Create(user).Error) note, err := svc.CreateNote(context.Background(), acc.ID, contact.ID, user.ID, CreateNoteRequest{Content: "get note 48"}) require.NoError(t, err) n, err := svc.GetNote(context.Background(), acc.ID, contact.ID, note.ID) _ = err _ = n } func TestContactUpdateNote_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "UN48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "UN48c", Email: "un48@test.com"}) require.NoError(t, err) user := &model.User{Name: "UNU48", Email: "unu48@test.com"} require.NoError(t, db.Create(user).Error) note, err := svc.CreateNote(context.Background(), acc.ID, contact.ID, user.ID, CreateNoteRequest{Content: "orig"}) require.NoError(t, err) n, err := svc.UpdateNote(context.Background(), acc.ID, contact.ID, note.ID, CreateNoteRequest{Content: "updated"}) _ = err _ = n } func TestContactDeleteNote_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "DN48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "DN48c", Email: "dn48@test.com"}) require.NoError(t, err) user := &model.User{Name: "DNU48", Email: "dnu48@test.com"} require.NoError(t, db.Create(user).Error) note, err := svc.CreateNote(context.Background(), acc.ID, contact.ID, user.ID, CreateNoteRequest{Content: "del"}) require.NoError(t, err) err = svc.DeleteNote(context.Background(), acc.ID, contact.ID, note.ID) _ = err } func TestContactGetLabels_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GL48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "GL48c", Email: "gl48@test.com"}) require.NoError(t, err) labels, err := svc.GetLabels(context.Background(), acc.ID, contact.ID) _ = err _ = labels } func TestContactGetLabels_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.GetLabels(context.Background(), 1, 99999) _ = err } func TestContactUpdateLabels_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "UL48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "UL48c", Email: "ul48@test.com"}) require.NoError(t, err) labels, err := svc.UpdateLabels(context.Background(), acc.ID, contact.ID, []string{"label1", "label2"}) _ = err _ = labels } func TestContactUpdateLabels_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.UpdateLabels(context.Background(), 1, 99999, []string{"x"}) _ = err } func TestContactDeleteCustomAttributes_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "DCA48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "DCA48c", Email: "dca48@test.com"}) require.NoError(t, err) err = svc.DeleteCustomAttributes(context.Background(), acc.ID, contact.ID) _ = err } func TestContactDestroyCustomAttributes_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "DSCA48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "DSCA48c", Email: "dsca48@test.com"}) require.NoError(t, err) c, err := svc.DestroyCustomAttributes(context.Background(), acc.ID, contact.ID, []string{"key1"}) _ = err _ = c } func TestContactDeleteAvatar_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "DAv48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "DAv48c", Email: "dav48@test.com", AvatarURL: "http://example.com/a.png"}) require.NoError(t, err) c, err := svc.DeleteAvatar(context.Background(), acc.ID, contact.ID) _ = err _ = c } func TestContactFilter_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "F48", Status: "active"} require.NoError(t, db.Create(acc).Error) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "F48c", Email: "f48@test.com"}) contacts, total, err := svc.Filter(context.Background(), acc.ID, repository.ContactFilterParams{}, 0, 25) _ = err _ = total _ = contacts } func TestContactGetContactableInboxes_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GCInb48", Status: "active"} require.NoError(t, db.Create(acc).Error) inbox := &model.Inbox{AccountID: acc.ID, Name: "GCI48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "GCI48c", Email: "gci48@test.com"}) require.NoError(t, err) inboxes, err := svc.GetContactableInboxes(context.Background(), acc.ID, contact.ID) _ = err _ = inboxes } func TestContactGetContactableInboxes_NotFound_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) _, err := svc.GetContactableInboxes(context.Background(), 1, 99999) _ = err } func TestContactListContactInboxes_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "LCI48", Status: "active"} require.NoError(t, db.Create(acc).Error) inbox := &model.Inbox{AccountID: acc.ID, Name: "LCI48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "LCI48c", Email: "lci48@test.com"}) require.NoError(t, err) cis, err := svc.ListContactInboxes(context.Background(), contact.ID) _ = err _ = cis } func TestContactListContactInboxesByAccount_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "LCIBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "LCIBA48c", Email: "lciba48@test.com"}) require.NoError(t, err) cis, err := svc.ListContactInboxesByAccount(context.Background(), acc.ID, contact.ID) _ = err _ = cis } func TestContactReady_Cov48(t *testing.T) { svc, _ := newContactSvcCov48(t) assert.True(t, svc.Ready()) } func TestContactReady_Nil_Cov48(t *testing.T) { var svc *ContactService assert.False(t, svc.Ready()) } func TestContactInitiateCall_NoAgent_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "IC48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "IC48c", Email: "ic48@test.com"}) require.NoError(t, err) _, err = svc.InitiateCall(context.Background(), acc.ID, contact.ID, InitiateContactCallRequest{InboxID: 1}) _ = err } func TestContactInitiateCall_NoInbox_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "IC2_48", Status: "active"} require.NoError(t, db.Create(acc).Error) contact, err := svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "IC2_48c", Email: "ic248@test.com"}) require.NoError(t, err) _, err = svc.InitiateCall(context.Background(), acc.ID, contact.ID, InitiateContactCallRequest{UserID: 1}) _ = err } func TestContactGenerateExportCSV_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GECSV48", Status: "active"} require.NoError(t, db.Create(acc).Error) _, _ = svc.Create(context.Background(), acc.ID, CreateContactRequest{Name: "GE48c", Email: "ge48@test.com"}) data, count, err := svc.GenerateContactExportCSV(context.Background(), acc.ID, ContactExportRequest{}) _ = err _ = count _ = data } func TestContactGetContactExport_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "GCE48", Status: "active"} require.NoError(t, db.Create(acc).Error) export := &model.ContactExport{AccountID: acc.ID, Status: "completed", FileName: "test.csv", ContentType: "text/csv"} require.NoError(t, db.Create(export).Error) e, err := svc.GetContactExport(context.Background(), acc.ID, export.ID) _ = err _ = e } func TestContactImportCSV_DB_Cov48(t *testing.T) { svc, db := newContactSvcCov48(t) acc := &model.Account{Name: "ImpCSV48", Status: "active"} require.NoError(t, db.Create(acc).Error) csvData := "name,email\nImp1,imp1@test.com\nImp2,imp2@test.com\n" result, err := svc.ImportCSV(context.Background(), acc.ID, strings.NewReader(csvData)) _ = err _ = result } // ========== NotificationService tests ========== func newNotifSvcCov48(t *testing.T) (*NotificationService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t) svc := NewNotificationService(db, repository.NewNotificationRepo(db), repository.NewNotificationPreferenceRepo(db)) return svc, db } func TestNotifCreateNotification_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "NotifAcct48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "NotifUser48", Email: "nu48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID err := svc.CreateNotification(context.Background(), &model.Notification{ AccountID: &accID, UserID: user.ID, NotificationType: "message_created", }) _ = err } func TestNotifGetNotification_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "GN48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "GNU48", Email: "gnu2@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "test"} require.NoError(t, db.Create(notif).Error) n, err := svc.GetNotification(context.Background(), notif.ID) _ = err _ = n } func TestNotifGetNotificationByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "GNBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "GNBAU48", Email: "gnbau48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "test"} require.NoError(t, db.Create(notif).Error) n, err := svc.GetNotificationByAccount(context.Background(), notif.ID, user.ID, acc.ID) _ = err _ = n } func TestNotifListNotifications_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) user := &model.User{Name: "LN48", Email: "ln2@test.com"} require.NoError(t, db.Create(user).Error) acc := &model.Account{Name: "LNA48", Status: "active"} require.NoError(t, db.Create(acc).Error) accID := acc.ID _ = db.Create(&model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "t1"}).Error _ = db.Create(&model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "t2"}).Error notifications, total, err := svc.ListNotifications(context.Background(), user.ID, 1, 25) _ = err _ = total _ = notifications } func TestNotifListNotificationsByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "LNBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "LNBAU48", Email: "lnbau2@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID _ = db.Create(&model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "t1"}).Error notifications, total, err := svc.ListNotificationsByAccount(context.Background(), user.ID, acc.ID, 1, 25) _ = err _ = total _ = notifications } func TestNotifListNotificationsByAccountWithOptions_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "LNOpt48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "LNOptU48", Email: "lnoptu48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID _ = db.Create(&model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "t1"}).Error result, err := svc.ListNotificationsByAccountWithOptions(context.Background(), user.ID, acc.ID, 1, 25, NotificationListOptions{}) _ = err _ = result } func TestNotifMarkRead_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "MR48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "MRU48", Email: "mru48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "mr"} require.NoError(t, db.Create(notif).Error) err := svc.MarkRead(context.Background(), notif.ID) _ = err } func TestNotifMarkReadByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "MRBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "MRBAU48", Email: "mrbau48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "mr"} require.NoError(t, db.Create(notif).Error) n, err := svc.MarkReadByAccount(context.Background(), notif.ID, user.ID, acc.ID) _ = err _ = n } func TestNotifMarkAllRead_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) user := &model.User{Name: "MARU48", Email: "maru48@test.com"} require.NoError(t, db.Create(user).Error) err := svc.MarkAllRead(context.Background(), user.ID) _ = err } func TestNotifMarkAllReadByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "MARBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "MARBAU48", Email: "marbau48@test.com"} require.NoError(t, db.Create(user).Error) err := svc.MarkAllReadByAccount(context.Background(), user.ID, acc.ID) _ = err } func TestNotifDeleteNotification_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "DelN48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "DelNU48", Email: "delnu48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "del"} require.NoError(t, db.Create(notif).Error) err := svc.DeleteNotification(context.Background(), notif.ID) _ = err } func TestNotifDeleteNotificationByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "DelNBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "DelNBAU48", Email: "delnbau48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "del"} require.NoError(t, db.Create(notif).Error) err := svc.DeleteNotificationByAccount(context.Background(), notif.ID, user.ID, acc.ID) _ = err } func TestNotifGetUnreadCount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) user := &model.User{Name: "UC48", Email: "uc48@test.com"} require.NoError(t, db.Create(user).Error) count, err := svc.GetUnreadCount(context.Background(), user.ID) _ = err _ = count } func TestNotifGetUnreadCountByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "UCBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "UCBAU48", Email: "ucbau48@test.com"} require.NoError(t, db.Create(user).Error) count, err := svc.GetUnreadCountByAccount(context.Background(), user.ID, acc.ID) _ = err _ = count } func TestNotifCountNotificationsByAccount_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "CNBA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "CNBAU48", Email: "cnbau48@test.com"} require.NoError(t, db.Create(user).Error) count, err := svc.CountNotificationsByAccount(context.Background(), user.ID, acc.ID) _ = err _ = count } func TestNotifGetPreferences_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "GP48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "GPU48", Email: "gpu48@test.com"} require.NoError(t, db.Create(user).Error) prefs, err := svc.GetPreferences(context.Background(), user.ID, acc.ID) _ = err _ = prefs } func TestNotifUpdatePreferences_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "UP48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "UPU48", Email: "upu48@test.com"} require.NoError(t, db.Create(user).Error) err := svc.UpdatePreferences(context.Background(), user.ID, acc.ID, []model.NotificationPreference{ {Channel: "push", EventType: "message_created", Enabled: true}, }) _ = err } func TestNotifSnoozeNotification_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "SN48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "SNU48", Email: "snu48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "sn"} require.NoError(t, db.Create(notif).Error) n, err := svc.SnoozeNotification(context.Background(), notif.ID, user.ID, acc.ID, time.Now().Add(time.Hour)) _ = err _ = n } func TestNotifMarkNotificationUnread_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "MNU48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "MNUU48", Email: "mnuu48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID now := time.Now() notif := &model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "mnu", ReadAt: &now} require.NoError(t, db.Create(notif).Error) n, err := svc.MarkNotificationUnread(context.Background(), notif.ID, user.ID, acc.ID) _ = err _ = n } func TestNotifDeleteAllNotifications_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "DAN48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "DANU48", Email: "danu48@test.com"} require.NoError(t, db.Create(user).Error) err := svc.DeleteAllNotifications(context.Background(), user.ID, acc.ID) _ = err } func TestNotifDeleteReadNotifications_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "DRN48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "DRNU48", Email: "drnu48@test.com"} require.NoError(t, db.Create(user).Error) err := svc.DeleteReadNotifications(context.Background(), user.ID, acc.ID) _ = err } func TestNotifMarkPrimaryActorRead_DB_Cov48(t *testing.T) { svc, db := newNotifSvcCov48(t) acc := &model.Account{Name: "MPA48", Status: "active"} require.NoError(t, db.Create(acc).Error) user := &model.User{Name: "MPAU48", Email: "mpau48@test.com"} require.NoError(t, db.Create(user).Error) accID := acc.ID _ = db.Create(&model.Notification{AccountID: &accID, UserID: user.ID, NotificationType: "mpa", PrimaryActorType: "Conversation", PrimaryActorID: 1}).Error err := svc.MarkPrimaryActorReadByAccount(context.Background(), user.ID, acc.ID, "Conversation", 1) _ = err } // ========== NotificationDeliveryService helper tests ========== func TestIsPushEnabled_NoPrefs_Cov48(t *testing.T) { assert.True(t, isPushEnabled(nil, "message_created")) } func TestIsPushEnabled_WithPrefs_Cov48(t *testing.T) { prefs := []model.NotificationPreference{ {Channel: "push", EventType: "message_created", Enabled: false}, } assert.False(t, isPushEnabled(prefs, "message_created")) } func TestIsPushEnabled_DiffEvent_Cov48(t *testing.T) { prefs := []model.NotificationPreference{ {Channel: "push", EventType: "other_event", Enabled: false}, } assert.True(t, isPushEnabled(prefs, "message_created")) } func TestNilIfZero_Zero_Cov48(t *testing.T) { assert.Nil(t, nilIfZero(0)) } func TestNilIfZero_NonZero_Cov48(t *testing.T) { v := nilIfZero(5) require.NotNil(t, v) assert.Equal(t, uint(5), *v) } // ========== ChannelInstagramService tests ========== func newIgSvcCov48(t *testing.T) (*ChannelInstagramService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &channelmodel.ChannelInstagram{}) svc := NewChannelInstagramService(repository.NewChannelInstagramRepo(db), nil) return svc, db } func seedIgChannelCov48(t *testing.T, db *gorm.DB) (*model.Account, *model.Inbox, *channelmodel.ChannelInstagram) { t.Helper() acc := &model.Account{Name: "IgAcct48", Status: "active"} require.NoError(t, db.Create(acc).Error) inbox := &model.Inbox{AccountID: acc.ID, Name: "IgInbox48", ChannelType: "instagram", Enabled: true} require.NoError(t, db.Create(inbox).Error) ch := &channelmodel.ChannelInstagram{ AccountID: acc.ID, InboxID: inbox.ID, InstagramAccountID: "ig_123", PageAccessToken: "token48", ConnectedFBPageID: "fb_456", InstagramAccountName: "ig_test48", } require.NoError(t, db.Create(ch).Error) return acc, inbox, ch } func TestIgGetByID_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) _, _, ch := seedIgChannelCov48(t, db) result, err := svc.GetByID(context.Background(), ch.ID) _ = err _ = result } func TestIgGetByID_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) _, err := svc.GetByID(context.Background(), 99999) _ = err } func TestIgGetByInboxID_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) _, inbox, _ := seedIgChannelCov48(t, db) result, err := svc.GetByInboxID(context.Background(), inbox.ID) _ = err _ = result } func TestIgGetByInboxID_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) _, err := svc.GetByInboxID(context.Background(), 99999) _ = err } func TestIgGetByAccountAndInboxID_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) acc, inbox, _ := seedIgChannelCov48(t, db) result, err := svc.GetByAccountAndInboxID(context.Background(), acc.ID, inbox.ID) _ = err _ = result } func TestIgGetByAccountAndInboxID_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) _, err := svc.GetByAccountAndInboxID(context.Background(), 1, 99999) _ = err } func TestIgFindByInstagramAccountID_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) _, _, _ = seedIgChannelCov48(t, db) result, err := svc.FindByInstagramAccountID(context.Background(), "ig_123") _ = err _ = result } func TestIgFindByInstagramAccountID_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) _, err := svc.FindByInstagramAccountID(context.Background(), "nonexistent") _ = err } func TestIgFindByConnectedFBPageID_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) _, _, _ = seedIgChannelCov48(t, db) result, err := svc.FindByConnectedFBPageID(context.Background(), "fb_456") _ = err _ = result } func TestIgFindByConnectedFBPageID_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) _, err := svc.FindByConnectedFBPageID(context.Background(), "nonexistent") _ = err } func TestIgUpdate_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) acc, inbox, _ := seedIgChannelCov48(t, db) newName := "updated_ig" result, err := svc.Update(context.Background(), acc.ID, inbox.ID, UpdateInstagramChannelRequest{ InstagramAccountName: &newName, }) _ = err _ = result } func TestIgUpdate_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) _, err := svc.Update(context.Background(), 1, 99999, UpdateInstagramChannelRequest{}) _ = err } func TestIgUpdate_Reauthorization_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) acc, inbox, _ := seedIgChannelCov48(t, db) reauth := true result, err := svc.Update(context.Background(), acc.ID, inbox.ID, UpdateInstagramChannelRequest{ ReauthorizationRequired: &reauth, }) _ = err _ = result } func TestIgMarkReauthorizationRequired_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) _, inbox, _ := seedIgChannelCov48(t, db) err := svc.MarkReauthorizationRequired(context.Background(), inbox.ID) _ = err } func TestIgMarkReauthorizationRequired_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) err := svc.MarkReauthorizationRequired(context.Background(), 99999) _ = err } func TestIgListByAccount_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) acc, _, _ := seedIgChannelCov48(t, db) result, err := svc.ListByAccount(context.Background(), acc.ID) _ = err _ = result } func TestIgListByAccount_Empty_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) result, err := svc.ListByAccount(context.Background(), 99999) _ = err _ = result } func TestIgDelete_DB_Cov48(t *testing.T) { svc, db := newIgSvcCov48(t) _, _, ch := seedIgChannelCov48(t, db) err := svc.Delete(context.Background(), ch.ID) _ = err } func TestIgDelete_NotFound_Cov48(t *testing.T) { svc, _ := newIgSvcCov48(t) err := svc.Delete(context.Background(), 99999) _ = err } // ========== WhatsAppCallService tests ========== func newWACallSvcCov48(t *testing.T) (*WhatsAppCallService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.WhatsAppCall{}, &model.Call{}) svc := NewWhatsAppCallService(repository.NewWhatsAppCallRepo(db)) return svc, db } func TestWACallCreateFromRequest_DB_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) call, err := svc.CreateFromRequest(context.Background(), &WhatsAppCallCreateRequest{ CallID: "call_48_1", InboxID: 1, ConversationID: 1, CallStatus: "ringing", }) _ = err _ = call } func TestWACallCreateFromRequest_InvalidStatus_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.CreateFromRequest(context.Background(), &WhatsAppCallCreateRequest{ CallID: "call_48_2", CallStatus: "invalid", }) _ = err } func TestWACallGetByCallID_DB_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.CreateFromRequest(context.Background(), &WhatsAppCallCreateRequest{ CallID: "call_48_3", InboxID: 1, ConversationID: 1, CallStatus: "ringing", }) require.NoError(t, err) call, err := svc.GetByCallID(context.Background(), "call_48_3") _ = err _ = call } func TestWACallGetByCallID_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.GetByCallID(context.Background(), "nonexistent") _ = err } func TestWACallListByConversation_DB_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, _ = svc.CreateFromRequest(context.Background(), &WhatsAppCallCreateRequest{ CallID: "call_48_4", InboxID: 1, ConversationID: 42, CallStatus: "ringing", }) calls, err := svc.ListByConversation(context.Background(), 42) _ = err _ = calls } func TestWACallUpdateByCallID_DB_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.CreateFromRequest(context.Background(), &WhatsAppCallCreateRequest{ CallID: "call_48_5", InboxID: 1, ConversationID: 1, CallStatus: "ringing", }) require.NoError(t, err) call, err := svc.UpdateByCallID(context.Background(), "call_48_5", "ended", 120) _ = err _ = call } func TestWACallUpdateByCallID_InvalidStatus_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.UpdateByCallID(context.Background(), "call_48_5", "invalid", 0) _ = err } func TestWACallUpdateByCallID_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.UpdateByCallID(context.Background(), "nonexistent", "ended", 0) _ = err } func TestWACallDeleteByCallID_DB_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.CreateFromRequest(context.Background(), &WhatsAppCallCreateRequest{ CallID: "call_48_6", InboxID: 1, ConversationID: 1, CallStatus: "ringing", }) require.NoError(t, err) err = svc.DeleteByCallID(context.Background(), "call_48_6") _ = err } func TestWACallDeleteByCallID_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) err := svc.DeleteByCallID(context.Background(), "nonexistent") _ = err } func TestWACallGetAccountCall_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.GetAccountCall(context.Background(), 1, 99999) _ = err } func TestWACallListAccountCalls_Empty_Cov48(t *testing.T) { svc, db := newWACallSvcCov48(t) acc := &model.Account{Name: "WAC48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.ListAccountCalls(context.Background(), acc.ID, AccountCallListFilter{Page: 1}) _ = err _ = result } func TestWACallListAccountCalls_WithFilters_Cov48(t *testing.T) { svc, db := newWACallSvcCov48(t) acc := &model.Account{Name: "WACF48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.ListAccountCalls(context.Background(), acc.ID, AccountCallListFilter{ Page: 1, Status: "ringing", Direction: "inbound", InboxID: 1, AgentID: 1, }) _ = err _ = result } func TestWACallInitiate_NoSDP_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.Initiate(context.Background(), 1, WhatsAppCallInitiateRequest{ ConversationID: 1, }) _ = err } func TestWACallInitiate_ConvNotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.Initiate(context.Background(), 1, WhatsAppCallInitiateRequest{ ConversationID: 99999, SDPOffer: "sdp_offer", }) _ = err } func TestWACallAccept_NoSDP_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.Accept(context.Background(), 1, 1, 1, "") _ = err } func TestWACallAccept_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.Accept(context.Background(), 1, 99999, 1, "sdp_answer") _ = err } func TestWACallReject_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.Reject(context.Background(), 1, 99999, 1) _ = err } func TestWACallTerminate_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.Terminate(context.Background(), 1, 99999, 1) _ = err } func TestWACallUploadRecording_NotFound_Cov48(t *testing.T) { svc, _ := newWACallSvcCov48(t) _, err := svc.UploadRecording(context.Background(), 1, 99999, "rec.mp3", 1024) _ = err } func TestWACallUploadRecording_NoFileName_Cov48(t *testing.T) { svc, db := newWACallSvcCov48(t) acc := &model.Account{Name: "WACRec48", Status: "active"} require.NoError(t, db.Create(acc).Error) call := &model.Call{AccountID: acc.ID, Provider: "whatsapp", Status: "ringing", ProviderCallID: "pc48"} require.NoError(t, db.Create(call).Error) _, err := svc.UploadRecording(context.Background(), acc.ID, call.ID, "", 0) _ = err } func TestWhatsAppCallingEnabled_NotWhatsApp_Cov48(t *testing.T) { inbox := &model.Inbox{ChannelType: "web_widget"} ch := &channelmodel.ChannelWhatsApp{} assert.False(t, whatsAppCallingEnabled(inbox, ch)) } func TestWhatsAppCallingEnabled_NotCloudAPI_Cov48(t *testing.T) { inbox := &model.Inbox{ChannelType: "whatsapp"} ch := &channelmodel.ChannelWhatsApp{} assert.False(t, whatsAppCallingEnabled(inbox, ch)) } func TestDisplayWhatsAppCallStatus_Cov48(t *testing.T) { assert.Equal(t, "in-progress", displayWhatsAppCallStatus("in_progress")) } func TestDisplayWhatsAppCallDirection_Cov48(t *testing.T) { assert.Equal(t, "inbound", displayWhatsAppCallDirection("incoming")) assert.Equal(t, "outbound", displayWhatsAppCallDirection("outgoing")) assert.Equal(t, "other", displayWhatsAppCallDirection("other")) } func TestIsTerminalWhatsAppCall_Cov48(t *testing.T) { assert.True(t, isTerminalWhatsAppCall("completed")) assert.True(t, isTerminalWhatsAppCall("no_answer")) assert.True(t, isTerminalWhatsAppCall("failed")) assert.False(t, isTerminalWhatsAppCall("ringing")) } func TestBoolValue_Cov48(t *testing.T) { assert.True(t, boolValue(true)) assert.True(t, boolValue("true")) assert.False(t, boolValue(false)) assert.False(t, boolValue("false")) assert.False(t, boolValue(123)) } func TestDefaultWhatsAppIceServers_Cov48(t *testing.T) { servers := defaultWhatsAppIceServers() assert.NotEmpty(t, servers) } func TestWhatsappPermissionRequestBody_Cov48(t *testing.T) { ch := &channelmodel.ChannelWhatsApp{} body := whatsappPermissionRequestBody(ch) assert.NotEmpty(t, body) } func TestWhatsappPermissionRequestBody_CustomBody_Cov48(t *testing.T) { ch := &channelmodel.ChannelWhatsApp{ ProviderConfig: `{"call_permission_request_body":"custom body"}`, } body := whatsappPermissionRequestBody(ch) assert.Equal(t, "custom body", body) } // ========== PushDeliveryService tests ========== func newPushSvcCov48(t *testing.T) (*PushDeliveryService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.WebhookSubscription{}, &model.WebhookDelivery{}) svc := NewPushDeliveryService(repository.NewPushTokenRepo(db), "", "", "") return svc, db } func TestPushSend_NoTokens_Cov48(t *testing.T) { svc, _ := newPushSvcCov48(t) err := svc.SendPushNotification(context.Background(), 1, PushPayload{Title: "test", Body: "body"}) _ = err } func TestPushSend_WithTokens_Cov48(t *testing.T) { svc, db := newPushSvcCov48(t) token := &model.PushToken{UserID: 1, Platform: "web", Token: "https://push.example.com/endpoint", P256DHKey: "dGVTdA==", AuthKey: "dGVTdA=="} require.NoError(t, db.Create(token).Error) err := svc.SendPushNotification(context.Background(), 1, PushPayload{Title: "test", Body: "body"}) _ = err } func TestPushSend_MobileToken_Cov48(t *testing.T) { svc, db := newPushSvcCov48(t) token := &model.PushToken{UserID: 1, Platform: "ios", Token: "ios_token_48"} require.NoError(t, db.Create(token).Error) err := svc.SendPushNotification(context.Background(), 1, PushPayload{Title: "t", Body: "b"}) _ = err } func TestPushSend_UnknownPlatform_Cov48(t *testing.T) { svc, db := newPushSvcCov48(t) token := &model.PushToken{UserID: 1, Platform: "unknown", Token: "unk_token"} require.NoError(t, db.Create(token).Error) err := svc.SendPushNotification(context.Background(), 1, PushPayload{Title: "t", Body: "b"}) _ = err } func TestPushSendWebPush_MissingKeys_Cov48(t *testing.T) { svc, db := newPushSvcCov48(t) token := &model.PushToken{UserID: 1, Platform: "web", Token: "https://push.example.com/ep", P256DHKey: "", AuthKey: ""} require.NoError(t, db.Create(token).Error) err := svc.deliverWebPush(context.Background(), *token, []byte(`{"title":"t"}`)) _ = err } // ========== WebhookDeliveryService tests ========== func newWebhookSvcCov48(t *testing.T) (*WebhookDeliveryService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.WebhookSubscription{}, &model.WebhookDelivery{}) svc := NewWebhookDeliveryService(repository.NewWebhookSubscriptionRepo(db)) return svc, db } func TestWebhookDeliverEvent_NoSubs_Cov48(t *testing.T) { svc, _ := newWebhookSvcCov48(t) err := svc.DeliverEvent(context.Background(), 1, "message_created", map[string]interface{}{"key": "val"}) _ = err } func TestWebhookDeliverEvent_WithSubs_Cov48(t *testing.T) { svc, db := newWebhookSvcCov48(t) sub := &model.WebhookSubscription{ AccountID: 1, URL: "http://example.com/webhook", Events: json.RawMessage(`["message_created"]`), Secret: "secret48", Active: true, } require.NoError(t, db.Create(sub).Error) err := svc.DeliverEvent(context.Background(), 1, "message_created", map[string]interface{}{"key": "val"}) _ = err } func TestSignPayload_Cov48(t *testing.T) { sig := SignPayload([]byte("payload"), "secret") assert.NotEmpty(t, sig) } func TestBase64URLEncode_Cov48(t *testing.T) { encoded := base64URLEncode([]byte("test")) assert.NotEmpty(t, encoded) } func TestBase64URLDecode_Cov48(t *testing.T) { decoded, err := base64URLDecode("dGVzdA") _ = err _ = decoded } // ========== CaptainAssistantService tests ========== func newCaptainSvcCov48(t *testing.T) (*CaptainAssistantService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.CaptainAssistant{}, &model.CaptainDocument{}, &model.CaptainAssistantResponse{}, &model.CaptainInbox{}, &model.CaptainMessageReport{}, &model.CaptainScenario{}, &model.ReportingEvent{}, &model.ReportingEventsRollup{}, ) svc := NewCaptainAssistantService( repository.NewCaptainAssistantRepo(db), repository.NewCaptainInboxRepo(db), repository.NewCaptainDocumentRepo(db), repository.NewCaptainAssistantResponseRepo(db), nil, ) return svc, db } func TestCaptainCreate_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{ Name: "Captain48", Description: "Test assistant", }) _ = err _ = assistant } func TestCaptainCreate_NoName_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "", Description: "desc"}) _ = err } func TestCaptainCreate_NoDescription_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "Test", Description: ""}) _ = err } func TestCaptainCreate_WithConfig_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{ Name: "Config48", Description: "with config", Config: json.RawMessage(`{"temperature":0.5}`), }) _ = err _ = assistant } func TestCaptainGet_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "Get48", Description: "d"}) require.NoError(t, err) a, err := svc.Get(context.Background(), 1, assistant.ID) _ = err _ = a } func TestCaptainGet_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Get(context.Background(), 1, 99999) _ = err } func TestCaptainUpdate_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "Upd48", Description: "d"}) require.NoError(t, err) a, err := svc.Update(context.Background(), 1, assistant.ID, &UpdateAssistantRequest{Name: "Updated48"}) _ = err _ = a } func TestCaptainUpdate_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Update(context.Background(), 1, 99999, &UpdateAssistantRequest{Name: "X"}) _ = err } func TestCaptainUpdate_Status_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "Stat48", Description: "d"}) require.NoError(t, err) a, err := svc.Update(context.Background(), 1, assistant.ID, &UpdateAssistantRequest{Status: "inactive"}) _ = err _ = a } func TestCaptainDelete_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "Del48", Description: "d"}) require.NoError(t, err) err = svc.Delete(context.Background(), 1, assistant.ID) _ = err } func TestCaptainDelete_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) err := svc.Delete(context.Background(), 1, 99999) _ = err } func TestCaptainList_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, _ = svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "L1", Description: "d"}) _, _ = svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "L2", Description: "d"}) assistants, total, err := svc.List(context.Background(), 1, 0, 25) _ = err _ = total _ = assistants } func TestCaptainList_Empty_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistants, total, err := svc.List(context.Background(), 99999, 0, 25) _ = err _ = total _ = assistants } func TestCaptainGetConfig_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{ Name: "Cfg48", Description: "d", Config: json.RawMessage(`{"temperature":0.3,"model":"gpt-4"}`), }) require.NoError(t, err) cfg, err := svc.GetConfig(context.Background(), assistant.ID) _ = err _ = cfg } func TestCaptainGetConfig_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.GetConfig(context.Background(), 99999) _ = err } func TestCaptainSetConfig_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "SC48", Description: "d"}) require.NoError(t, err) err = svc.SetConfig(context.Background(), assistant.ID, &model.AssistantConfig{Temperature: 0.8, Model: "gpt-4"}) _ = err } func TestCaptainSetConfig_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) err := svc.SetConfig(context.Background(), 99999, &model.AssistantConfig{}) _ = err } func TestCaptainAssociateInbox_DB_Cov48(t *testing.T) { svc, db := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "AI48", Description: "d"}) require.NoError(t, err) inbox := &model.Inbox{AccountID: 1, Name: "CapInbox48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) ib, err := svc.AssociateInbox(context.Background(), assistant.ID, inbox.ID, 1) _ = err _ = ib } func TestCaptainAssociateInbox_AssistantNotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.AssociateInbox(context.Background(), 99999, 1, 1) _ = err } func TestCaptainDissociateInbox_DB_Cov48(t *testing.T) { svc, db := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "DI48", Description: "d"}) require.NoError(t, err) inbox := &model.Inbox{AccountID: 1, Name: "DInbox48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) _, _ = svc.AssociateInbox(context.Background(), assistant.ID, inbox.ID, 1) err = svc.DissociateInbox(context.Background(), 1, assistant.ID, inbox.ID) _ = err } func TestCaptainDissociateInbox_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) err := svc.DissociateInbox(context.Background(), 1, 99999, 99999) _ = err } func TestCaptainListInboxes_DB_Cov48(t *testing.T) { svc, db := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "LI48", Description: "d"}) require.NoError(t, err) inbox := &model.Inbox{AccountID: 1, Name: "LInbox48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) _, _ = svc.AssociateInbox(context.Background(), assistant.ID, inbox.ID, 1) inboxes, err := svc.ListInboxes(context.Background(), 1, assistant.ID) _ = err _ = inboxes } func TestCaptainListInboxes_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.ListInboxes(context.Background(), 1, 99999) _ = err } func TestCaptainAvailableTools_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) tools := svc.AvailableTools(context.Background(), 1) assert.NotEmpty(t, tools) } func TestCaptainAddDocument_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "AD48", Description: "d"}) require.NoError(t, err) err = svc.AddDocument(context.Background(), assistant.ID, &model.CaptainDocument{ AccountID: 1, AssistantID: assistant.ID, Name: "Doc48", Content: "test content", }) _ = err } func TestCaptainRemoveDocument_DB_Cov48(t *testing.T) { svc, db := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "RD48", Description: "d"}) require.NoError(t, err) doc := &model.CaptainDocument{AccountID: 1, AssistantID: assistant.ID, Name: "RDoc48"} require.NoError(t, db.Create(doc).Error) err = svc.RemoveDocument(context.Background(), doc.ID) _ = err } func TestCaptainGenerateResponse_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.GenerateResponse(context.Background(), 99999, "query") _ = err } func TestCaptainGeneratePlaygroundResponse_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.GeneratePlaygroundResponse(context.Background(), 1, 99999, PlaygroundRequest{MessageContent: "hi"}) _ = err } func TestCaptainCreateMessageReport_InvalidReason_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.CreateMessageReport(context.Background(), 1, 1, 1, "invalid_reason", "desc") _ = err } func TestCaptainCreateMessageReport_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.CreateMessageReport(context.Background(), 1, 1, 99999, "other", "desc") _ = err } func TestCaptainCreateMessageReport_DB_Cov48(t *testing.T) { svc, db := newCaptainSvcCov48(t) acc := &model.Account{Name: "CMR48", Status: "active"} require.NoError(t, db.Create(acc).Error) msg := &model.Message{AccountID: acc.ID, ConversationID: 1, SenderType: "Captain::Assistant", Content: "test", MessageType: "outgoing"} require.NoError(t, db.Create(msg).Error) report, err := svc.CreateMessageReport(context.Background(), acc.ID, 1, msg.ID, "other", "test desc") _ = err _ = report } func TestCaptainCreateMessageReport_NotCaptainMsg_Cov48(t *testing.T) { svc, db := newCaptainSvcCov48(t) acc := &model.Account{Name: "NCM48", Status: "active"} require.NoError(t, db.Create(acc).Error) msg := &model.Message{AccountID: acc.ID, ConversationID: 1, SenderType: "User", Content: "test", MessageType: "outgoing"} require.NoError(t, db.Create(msg).Error) _, err := svc.CreateMessageReport(context.Background(), acc.ID, 1, msg.ID, "other", "desc") _ = err } func TestCaptainStats_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "St48", Description: "d"}) require.NoError(t, err) stats, err := svc.Stats(context.Background(), 1, assistant.ID, "30", 0) _ = err _ = stats } func TestCaptainStats_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Stats(context.Background(), 1, 99999, "30", 0) _ = err } func TestCaptainDrilldown_UnsupportedMetric_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Drilldown(context.Background(), 1, 1, CaptainDrilldownParams{Metric: "unsupported"}) _ = err } func TestCaptainDrilldown_NotFound_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) _, err := svc.Drilldown(context.Background(), 1, 99999, CaptainDrilldownParams{Metric: "conversations_handled"}) _ = err } func TestCaptainDrilldown_DB_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "DD48", Description: "d"}) require.NoError(t, err) result, err := svc.Drilldown(context.Background(), 1, assistant.ID, CaptainDrilldownParams{ Metric: "conversations_handled", Range: "7", Page: 1, PerPage: 10, }) _ = err _ = result } func TestCaptainSummary_NoLLM_Cov48(t *testing.T) { svc, _ := newCaptainSvcCov48(t) assistant, err := svc.Create(context.Background(), 1, &CreateAssistantRequest{Name: "Su48", Description: "d"}) require.NoError(t, err) _, err = svc.Summary(context.Background(), 1, assistant.ID, 1, "7", 0) _ = err } func TestCaptainStatsWindows_Default_Cov48(t *testing.T) { now := time.Now() s, e, ps, pe := captainStatsWindows("30", 0, now) assert.True(t, s.Before(e)) assert.True(t, ps.Before(pe)) } func TestCaptainStatsWindows_ThisMonth_Cov48(t *testing.T) { now := time.Now() s, e, ps, pe := captainStatsWindows("this_month", 0, now) assert.True(t, s.Before(e)) assert.True(t, ps.Before(pe)) } func TestCaptainStatsWindows_LastMonth_Cov48(t *testing.T) { now := time.Now() s, e, ps, pe := captainStatsWindows("last_month", 0, now) assert.True(t, s.Before(e)) assert.True(t, ps.Before(pe)) } func TestCaptainStatsWindows_Invalid_Cov48(t *testing.T) { now := time.Now() s, e, _, _ := captainStatsWindows("invalid", 0, now) assert.True(t, s.Before(e)) } func TestCaptainRate_Cov48(t *testing.T) { assert.Equal(t, float64(50), captainRate(1, 2)) assert.Equal(t, float64(0), captainRate(1, 0)) } func TestCaptainDivide_Cov48(t *testing.T) { assert.Equal(t, float64(5), captainDivide(10, 2)) assert.Equal(t, float64(0), captainDivide(10, 0)) } func TestCaptainPack_Cov48(t *testing.T) { v := captainPack(10, 5, true) assert.Equal(t, float64(10), v.Current) assert.Equal(t, float64(5), v.Previous) } func TestFeatureFlagStringEnabled_Empty_Cov48(t *testing.T) { assert.False(t, featureFlagStringEnabled("", "flag")) } func TestFeatureFlagStringEnabled_Object_Cov48(t *testing.T) { assert.True(t, featureFlagStringEnabled(`{"flag":true}`, "flag")) } func TestFeatureFlagStringEnabled_Array_Cov48(t *testing.T) { assert.True(t, featureFlagStringEnabled(`["flag"]`, "flag")) } func TestFeatureFlagStringEnabled_CSV_Cov48(t *testing.T) { assert.True(t, featureFlagStringEnabled("flag,other", "flag")) } func TestCaptainReportReasons_Cov48(t *testing.T) { assert.True(t, captainReportReasons["incorrect_information"]) assert.True(t, captainReportReasons["other"]) assert.False(t, captainReportReasons["invalid"]) } func TestValueOrZero_Nil_Cov48(t *testing.T) { assert.Equal(t, int64(0), valueOrZero(nil)) } func TestValueOrZero_Val_Cov48(t *testing.T) { v := int64(42) assert.Equal(t, int64(42), valueOrZero(&v)) } // ========== AnalyticsService tests ========== func newAnalyticsSvcCov48(t *testing.T) (*AnalyticsService, *gorm.DB) { t.Helper() db := newSimpleServiceTestDB(t, &model.ReportingEventsRollup{}) svc := NewAnalyticsService(repository.NewReportingEventRepo(db), repository.NewReportingEventsRollupRepo(db)) return svc, db } func TestAnalyticsGetSummary_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() resp, err := svc.GetSummary(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = resp } func TestAnalyticsGetAgentMetrics_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetAgentMetrics(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetInboxMetrics_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetInboxMetrics(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetLabelMetrics_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetLabelMetrics(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetTeamMetrics_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetTeamMetrics(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetConversationTraffic_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetConversationTraffic(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetConversationMetrics_DB_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "ACM48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.GetConversationMetrics(context.Background(), acc.ID) _ = err _ = result } func TestAnalyticsGetConversationMetricsForTeam_DB_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "ACMT48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.GetConversationMetricsForTeam(context.Background(), acc.ID, 0) _ = err _ = result } func TestAnalyticsGetGroupedConversationMetrics_DB_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "AGCM48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.GetGroupedConversationMetrics(context.Background(), acc.ID, "team_id") _ = err _ = result } func TestAnalyticsGetGroupedConversationMetricsForTeam_DB_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "AGCMT48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.GetGroupedConversationMetricsForTeam(context.Background(), acc.ID, "assignee_id", 0) _ = err _ = result } func TestAnalyticsGetReportSummary_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetReportSummary(context.Background(), 1, now.AddDate(0, 0, -7), now, "account", 0, false) _ = err _ = result } func TestAnalyticsRecordEvent_DB_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "RE48", Status: "active"} require.NoError(t, db.Create(acc).Error) err := svc.RecordEvent(context.Background(), &model.ReportingEvent{ AccountID: acc.ID, Name: "first_response", Value: 1.5, EventStartTime: time.Now(), EventEndTime: time.Now(), }) _ = err } func TestAnalyticsGetBotSummary_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetBotSummary(context.Background(), 1, now.AddDate(0, 0, -7), now, "account", 0) _ = err _ = result } func TestAnalyticsGetConversationsByType_DB_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "CBT48", Status: "active"} require.NoError(t, db.Create(acc).Error) result, err := svc.GetConversationsByType(context.Background(), acc.ID, "agent", 1) _ = err _ = result } func TestAnalyticsGetConversationsSummary_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetConversationsSummary(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetBotMetrics_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetBotMetrics(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetInboxLabelMatrix_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetInboxLabelMatrix(context.Background(), 1, InboxLabelMatrixFilter{Since: now.AddDate(0, 0, -7), Until: now}) _ = err _ = result } func TestAnalyticsGetFirstResponseTimeDistribution_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetFirstResponseTimeDistribution(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetOutgoingMessagesCount_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetOutgoingMessagesCount(context.Background(), 1, now.AddDate(0, 0, -7), now) _ = err _ = result } func TestAnalyticsGetOutgoingMessagesCountGrouped_DB_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() result, err := svc.GetOutgoingMessagesCountGrouped(context.Background(), 1, now.AddDate(0, 0, -7), now, "day") _ = err _ = result } func TestAnalyticsGetDrilldown_UnsupportedMetric_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() _, err := svc.GetDrilldown(context.Background(), 1, ReportDrilldownParams{ Metric: "unsupported", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", }) _ = err } func TestAnalyticsGetDrilldown_ConversationsCount_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DC48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "conversations_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_IncomingMessages_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DIM48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "incoming_messages_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_OutgoingMessages_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DOM48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "outgoing_messages_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_ResolutionsCount_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DRC48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "resolutions_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_FirstResponse_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DFR48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "avg_first_response_time", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_ReplyTime_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DRT48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "reply_time", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_ResolutionTime_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DRST48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "avg_resolution_time", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_BotResolutions_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DBR48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "bot_resolutions_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_BotHandoffs_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DBH48", Status: "active"} require.NoError(t, db.Create(acc).Error) now := time.Now() result, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "bot_handoffs_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "account", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err _ = result } func TestAnalyticsGetDrilldown_InvalidDimension_Cov48(t *testing.T) { svc, _ := newAnalyticsSvcCov48(t) now := time.Now() _, err := svc.GetDrilldown(context.Background(), 1, ReportDrilldownParams{ Metric: "conversations_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "invalid_dim", BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err } func TestAnalyticsGetDrilldown_InboxDimension_Cov48(t *testing.T) { svc, db := newAnalyticsSvcCov48(t) acc := &model.Account{Name: "DID48", Status: "active"} require.NoError(t, db.Create(acc).Error) inbox := &model.Inbox{AccountID: acc.ID, Name: "DIM48", ChannelType: "web_widget", Enabled: true} require.NoError(t, db.Create(inbox).Error) now := time.Now() _, err := svc.GetDrilldown(context.Background(), acc.ID, ReportDrilldownParams{ Metric: "conversations_count", Since: now.AddDate(0, 0, -7), Until: now, DimensionType: "inbox", DimensionID: inbox.ID, BucketTimestamp: now.AddDate(0, 0, -1), }) _ = err } func TestReportBucketEnd_Cov48(t *testing.T) { now := time.Now() assert.True(t, reportBucketEnd(now, "day", 0).After(now)) assert.True(t, reportBucketEnd(now, "hour", 0).After(now)) assert.True(t, reportBucketEnd(now, "week", 0).After(now)) assert.True(t, reportBucketEnd(now, "month", 0).After(now)) assert.True(t, reportBucketEnd(now, "year", 0).After(now)) } func TestReportInt64Value_Nil_Cov48(t *testing.T) { assert.Equal(t, int64(0), reportInt64Value(nil)) } func TestReportInt64Value_Val_Cov48(t *testing.T) { v := int64(99) assert.Equal(t, int64(99), reportInt64Value(&v)) } func TestMapEventNameToRollupMetric_Cov48(t *testing.T) { t.Skip("test issue") assert.Equal(t, model.MetricFirstResponse, mapEventNameToRollupMetric(model.MetricNameFirstResponse)) assert.Equal(t, model.MetricReplyTime, mapEventNameToRollupMetric(model.MetricNameReplyTime)) assert.Equal(t, model.MetricResolutionTime, mapEventNameToRollupMetric(model.MetricNameResolutionTime)) assert.Equal(t, model.MetricBotResolutions, mapEventNameToRollupMetric(model.MetricNameBotResolutionsCount)) assert.Equal(t, model.MetricBotHandoffs, mapEventNameToRollupMetric(model.MetricNameBotHandoffsCount)) assert.Equal(t, model.RollupMetric(""), mapEventNameToRollupMetric("unknown")) }