package provider import ( "context" "encoding/json" "testing" "github.com/gochat/gochat/internal/channel" "github.com/gochat/gochat/internal/model" channelmodel "github.com/gochat/gochat/internal/model/channel" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) // =========================== // EmailProvider tests // =========================== func TestEmailProvider_Type_Cov5(t *testing.T) { p := NewEmailProvider() assert.Equal(t, channel.ChannelEmail, p.Type()) } func TestEmailProvider_ConfigSchema_Cov5(t *testing.T) { p := NewEmailProvider() schema := p.ConfigSchema() require.NotNil(t, schema) assert.Contains(t, schema.Required, "email") assert.NotEmpty(t, schema.Properties) } func TestEmailProvider_ValidateConfig_MissingEmail_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{}) require.Error(t, err) assert.Contains(t, err.Error(), "email is required") } func TestEmailProvider_ValidateConfig_BadEmail_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "not-an-email", }) require.Error(t, err) assert.Contains(t, err.Error(), "email format is invalid") } func TestEmailProvider_ValidateConfig_IMAPMissingAddress_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "imap_enabled": true, }) require.Error(t, err) assert.Contains(t, err.Error(), "imap_address is required") } func TestEmailProvider_ValidateConfig_IMAPMissingLogin_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "imap_enabled": true, "imap_address": "imap.example.com", }) require.Error(t, err) assert.Contains(t, err.Error(), "imap_login is required") } func TestEmailProvider_ValidateConfig_IMAPMissingPassword_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "imap_enabled": true, "imap_address": "imap.example.com", "imap_login": "user", "imap_password": "", }) require.Error(t, err) assert.Contains(t, err.Error(), "imap_password is required") } func TestEmailProvider_ValidateConfig_IMAPBadPort_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "imap_enabled": true, "imap_address": "imap.example.com", "imap_login": "user", "imap_password": "pass", "imap_port": float64(99999), }) require.Error(t, err) assert.Contains(t, err.Error(), "imap_port") } func TestEmailProvider_ValidateConfig_SMTPMissingAddress_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "smtp_enabled": true, "imap_enabled": false, }) require.Error(t, err) assert.Contains(t, err.Error(), "smtp_address is required") } func TestEmailProvider_ValidateConfig_SMTPMissingLogin_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "smtp_enabled": true, "imap_enabled": false, "smtp_address": "smtp.example.com", }) require.Error(t, err) assert.Contains(t, err.Error(), "smtp_login is required") } func TestEmailProvider_ValidateConfig_SMTPMissingPassword_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "smtp_enabled": true, "imap_enabled": false, "smtp_address": "smtp.example.com", "smtp_login": "user", "smtp_password": "", }) require.Error(t, err) assert.Contains(t, err.Error(), "smtp_password is required") } func TestEmailProvider_ValidateConfig_SMTPBadPort_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "smtp_enabled": true, "imap_enabled": false, "smtp_address": "smtp.example.com", "smtp_login": "user", "smtp_password": "pass", "smtp_port": float64(99999), }) require.Error(t, err) assert.Contains(t, err.Error(), "smtp_port") } func TestEmailProvider_ValidateConfig_Success_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateConfig(context.Background(), channel.ChannelConfig{ "email": "test@example.com", "imap_enabled": false, "smtp_enabled": false, }) assert.NoError(t, err) } func TestEmailProvider_DefaultConfig_Cov5(t *testing.T) { p := NewEmailProvider() cfg := p.DefaultConfig() assert.Equal(t, "Support", cfg["mailbox_name"]) assert.Equal(t, true, cfg["imap_enabled"]) assert.Equal(t, 993, cfg["imap_port"]) assert.Equal(t, 587, cfg["smtp_port"]) assert.Equal(t, "ssl", cfg["imap_ssl_mode"]) assert.Equal(t, "starttls", cfg["smtp_ssl_mode"]) } func TestEmailProvider_Capabilities_Cov5(t *testing.T) { p := NewEmailProvider() caps := p.Capabilities() assert.True(t, caps.SupportsAttachments) assert.True(t, caps.SupportsReplies) assert.True(t, caps.SupportsEmailHeaders) assert.False(t, caps.SupportsLocation) } func TestEmailProvider_PollInterval_Cov5(t *testing.T) { p := NewEmailProvider() assert.Equal(t, 5*60*1000000000, int(p.PollInterval())) } func TestEmailProvider_SetService_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) assert.NotNil(t, p.emailService) } func TestEmailProvider_OnCreate_Success_Cov5(t *testing.T) { p := NewEmailProvider() cfg, err := p.OnCreate(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{ "email": "test@example.com", }) require.NoError(t, err) assert.Equal(t, "test@example.com", cfg["email"]) assert.NotEmpty(t, cfg["forward_to_address"]) assert.Equal(t, "example.com", cfg["domain"]) } func TestEmailProvider_OnCreate_MissingEmail_Cov5(t *testing.T) { p := NewEmailProvider() _, err := p.OnCreate(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{}) require.Error(t, err) } func TestEmailProvider_OnCreate_WithForwardTo_Cov5(t *testing.T) { p := NewEmailProvider() cfg, err := p.OnCreate(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{ "email": "test@example.com", "forward_to_address": "custom@fwd.com", }) require.NoError(t, err) assert.Equal(t, "custom@fwd.com", cfg["forward_to_address"]) } func TestEmailProvider_OnDestroy_Cov5(t *testing.T) { p := NewEmailProvider() err := p.OnDestroy(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, channel.ChannelConfig{ "email": "test@example.com", }) assert.NoError(t, err) } func TestEmailProvider_ValidateWebhookRequest_Cov5(t *testing.T) { p := NewEmailProvider() err := p.ValidateWebhookRequest(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, &channel.WebhookRequest{}) assert.NoError(t, err) } func TestEmailProvider_GetContactProfile_Cov5(t *testing.T) { p := NewEmailProvider() profile, err := p.GetContactProfile(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, "user@example.com") require.NoError(t, err) assert.Equal(t, "user@example.com", profile.Name) assert.Equal(t, "user@example.com", profile.Extra["email"]) } func TestEmailProvider_ProcessIncoming_BadJSON_Cov5(t *testing.T) { p := NewEmailProvider() _, err := p.ProcessIncoming(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, []byte("bad json")) require.Error(t, err) } func TestEmailProvider_ProcessIncoming_TextEmail_Cov5(t *testing.T) { p := NewEmailProvider() emailJSON, _ := json.Marshal(EmailMessage{ MessageID: "msg123", From: "sender@example.com", FromName: "Sender", To: "rcpt@example.com", TextContent: "Hello world", Subject: "Test", }) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"use_reply_to":"false"}`} msg, err := p.ProcessIncoming(context.Background(), inbox, emailJSON) require.NoError(t, err) assert.Equal(t, "Hello world", msg.Content) assert.Equal(t, "msg123", msg.SourceID) } func TestEmailProvider_ProcessIncoming_HTMLEmail_Cov5(t *testing.T) { p := NewEmailProvider() emailJSON, _ := json.Marshal(EmailMessage{ MessageID: "msg456", From: "sender@example.com", FromName: "Sender", HTMLContent: "
Hello world
", Subject: "HTML Test", }) inbox := &model.Inbox{Base: model.Base{ID: 1}} msg, err := p.ProcessIncoming(context.Background(), inbox, emailJSON) require.NoError(t, err) assert.Contains(t, msg.Content, "Hello") } func TestEmailProvider_ProcessIncoming_WithAttachments_Cov5(t *testing.T) { p := NewEmailProvider() emailJSON, _ := json.Marshal(EmailMessage{ MessageID: "msg789", From: "sender@example.com", TextContent: "See attachment", Attachments: []EmailAttachment{ {Filename: "doc.pdf", ContentType: "application/pdf", FileSize: 1024}, }, }) inbox := &model.Inbox{Base: model.Base{ID: 1}} msg, err := p.ProcessIncoming(context.Background(), inbox, emailJSON) require.NoError(t, err) assert.Len(t, msg.Attachments, 1) assert.Equal(t, "doc.pdf", msg.Attachments[0].Filename) } func TestEmailProvider_ProcessIncoming_WithReplyTo_Cov5(t *testing.T) { p := NewEmailProvider() emailJSON, _ := json.Marshal(EmailMessage{ MessageID: "msg_re", From: "from@example.com", ReplyTo: "replyto@example.com", TextContent: "Reply test", InReplyTo: "orig_msg_id", }) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"use_reply_to":"true"}`} msg, err := p.ProcessIncoming(context.Background(), inbox, emailJSON) require.NoError(t, err) assert.Equal(t, "replyto@example.com", msg.SenderID) assert.Equal(t, "orig_msg_id", msg.ReplyToID) } func TestEmailProvider_SendMessage_NoService_Cov5(t *testing.T) { p := NewEmailProvider() _, err := p.SendMessage(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}, &model.Message{}, &model.Contact{}) require.Error(t, err) assert.Contains(t, err.Error(), "not initialized") } func TestEmailProvider_SendMessage_SMTPDisabled_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"smtp_enabled":false}`} _, err := p.SendMessage(context.Background(), inbox, &model.Message{}, &model.Contact{}) require.Error(t, err) assert.Contains(t, err.Error(), "SMTP is not enabled") } func TestEmailProvider_SendMessage_NoSMTPAddress_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"smtp_enabled":true}`} _, err := p.SendMessage(context.Background(), inbox, &model.Message{}, &model.Contact{}) require.Error(t, err) assert.Contains(t, err.Error(), "smtp_address") } func TestEmailProvider_SendMessage_NoContactEmail_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"smtp_enabled":true,"smtp_address":"smtp.test.com","email":"from@test.com"}`} _, err := p.SendMessage(context.Background(), inbox, &model.Message{}, &model.Contact{Identifier: ""}) require.Error(t, err) assert.Contains(t, err.Error(), "no email address") } func TestEmailProvider_SendMessage_Success_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"smtp_enabled":true,"smtp_address":"smtp.test.com","email":"from@test.com"}`} result, err := p.SendMessage(context.Background(), inbox, &model.Message{Content: "test msg"}, &model.Contact{Identifier: "rcpt@test.com"}) require.NoError(t, err) assert.Equal(t, "mock_msg_id", result.ExternalID) } func TestEmailProvider_PollMessages_NoService_Cov5(t *testing.T) { p := NewEmailProvider() msgs, err := p.PollMessages(context.Background(), &model.Inbox{Base: model.Base{ID: 1}}) require.NoError(t, err) assert.Nil(t, msgs) } func TestEmailProvider_PollMessages_IMAPDisabled_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"imap_enabled":false}`} msgs, err := p.PollMessages(context.Background(), inbox) require.NoError(t, err) assert.Nil(t, msgs) } func TestEmailProvider_PollMessages_IncompleteConfig_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"imap_enabled":true}`} msgs, err := p.PollMessages(context.Background(), inbox) require.NoError(t, err) assert.Nil(t, msgs) } func TestEmailProvider_PollMessages_Success_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"imap_enabled":true,"imap_address":"imap.test.com","imap_login":"user","imap_password":"pass"}`} msgs, err := p.PollMessages(context.Background(), inbox) require.NoError(t, err) assert.Len(t, msgs, 1) } func TestEmailProvider_PollMessages_BadConfig_Cov5(t *testing.T) { p := NewEmailProvider() p.SetService(&mockEmailService_Cov5{}) inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: "bad json"} msgs, err := p.PollMessages(context.Background(), inbox) require.NoError(t, err) assert.Nil(t, msgs) } func TestStripHTMLTags_Cov5(t *testing.T) { result := stripHTMLTags("Hello world
") assert.Contains(t, result, "Hello") assert.Contains(t, result, "world") assert.NotContains(t, result, "<") } func TestStripHTMLTags_Entities_Cov5(t *testing.T) { result := stripHTMLTags(" & < > ' "") assert.Contains(t, result, " ") assert.Contains(t, result, "&") } func TestBuildEmailSubject_Cov5(t *testing.T) { result := buildEmailSubject(nil, &model.Message{Content: "short msg"}, channel.ChannelConfig{ "mailbox_name": "Support", }) assert.Contains(t, result, "[Support]") assert.Contains(t, result, "short msg") } func TestBuildEmailSubject_LongContent_Cov5(t *testing.T) { longContent := "This is a very long message that exceeds the fifty character limit for sure" result := buildEmailSubject(nil, &model.Message{Content: longContent}, channel.ChannelConfig{}) assert.Contains(t, result, "...") } func TestBuildEmailSubject_DefaultMailbox_Cov5(t *testing.T) { result := buildEmailSubject(nil, &model.Message{Content: "msg"}, channel.ChannelConfig{}) assert.Contains(t, result, "[Support]") } func TestInboxConfigMap_Empty_Cov5(t *testing.T) { cfg, err := inboxConfigMap(&model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: ""}) require.NoError(t, err) assert.Empty(t, cfg) } func TestInboxConfigMap_BadJSON_Cov5(t *testing.T) { _, err := inboxConfigMap(&model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: "bad json"}) require.Error(t, err) } func TestInboxConfigMap_Success_Cov5(t *testing.T) { cfg, err := inboxConfigMap(&model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"key":"value","num":42.0}`}) require.NoError(t, err) assert.Equal(t, "value", cfg["key"]) } func TestInboxConfigValue_Cov5(t *testing.T) { inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: `{"str":"hello","b":true,"n":42.0}`} assert.Equal(t, "hello", inboxConfigValue(inbox, "str")) assert.Equal(t, "true", inboxConfigValue(inbox, "b")) assert.Equal(t, "42", inboxConfigValue(inbox, "n")) assert.Equal(t, "", inboxConfigValue(inbox, "missing")) } func TestInboxConfigValue_BadJSON_Cov5(t *testing.T) { inbox := &model.Inbox{Base: model.Base{ID: 1}, ChannelConfig: "bad"} assert.Equal(t, "", inboxConfigValue(inbox, "key")) } // =========================== // ChannelRegistry tests // =========================== func TestChannelRegistry_Cov5(t *testing.T) { r := NewChannelRegistry() assert.NotNil(t, r) assert.Empty(t, r.List()) } func TestChannelRegistry_RegisterGet_Cov5(t *testing.T) { r := NewChannelRegistry() r.Register(&mockChannelProvider_Cov5{name: "test"}) p := r.Get("test") require.NotNil(t, p) assert.Equal(t, "test", p.Name()) } func TestChannelRegistry_GetNotFound_Cov5(t *testing.T) { r := NewChannelRegistry() assert.Nil(t, r.Get("nonexistent")) } func TestChannelRegistry_List_Cov5(t *testing.T) { r := NewChannelRegistry() r.Register(&mockChannelProvider_Cov5{name: "a"}) r.Register(&mockChannelProvider_Cov5{name: "b"}) names := r.List() assert.Len(t, names, 2) } // =========================== // Mocks // =========================== type mockEmailService_Cov5 struct{} func (m *mockEmailService_Cov5) ValidateIMAPConnection(config channel.ChannelConfig) error { return nil } func (m *mockEmailService_Cov5) ValidateSMTPConnection(config channel.ChannelConfig) error { return nil } func (m *mockEmailService_Cov5) FetchIMAPMessages(ctx context.Context, inbox *model.Inbox, config channel.ChannelConfig) ([]*channel.IncomingMessage, error) { return []*channel.IncomingMessage{{SourceID: "test"}}, nil } func (m *mockEmailService_Cov5) SendViaSMTP(config channel.ChannelConfig, from, to, subject, body string) (string, error) { return "mock_msg_id", nil } type mockChannelProvider_Cov5 struct { name string } func (m *mockChannelProvider_Cov5) Name() string { return m.name } func (m *mockChannelProvider_Cov5) CreateChannel(ctx context.Context, accountID uint, params map[string]interface{}) (channelmodel.Channelable, error) { return nil, nil } func (m *mockChannelProvider_Cov5) UpdateChannel(ctx context.Context, channelID uint, params map[string]interface{}) (channelmodel.Channelable, error) { return nil, nil } func (m *mockChannelProvider_Cov5) DeleteChannel(ctx context.Context, channelID uint) error { return nil } func (m *mockChannelProvider_Cov5) HandleWebhook(ctx context.Context, payload map[string]interface{}) error { return nil } func (m *mockChannelProvider_Cov5) ProcessIncomingMessage(ctx context.Context, inboxID uint, contactID uint, payload map[string]interface{}) (*IncomingMessageResult, error) { return &IncomingMessageResult{}, nil } func (m *mockChannelProvider_Cov5) ProcessOutgoingMessage(ctx context.Context, conversationID uint, messageID uint, content string) error { return nil } func (m *mockChannelProvider_Cov5) ValidateConfig(params map[string]interface{}) error { return nil }