1678 lines
55 KiB
Go
1678 lines
55 KiB
Go
package email
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
channelpkg "github.com/gochat/gochat/internal/channel"
|
|
"github.com/gochat/gochat/internal/model"
|
|
)
|
|
|
|
// safeCallCov10 runs fn and recovers from panics (nil-dep panics etc).
|
|
func safeCallCov10(fn func()) {
|
|
defer func() { _ = recover() }()
|
|
fn()
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — ValidateConfig edge cases
|
|
// ============================================================
|
|
|
|
func TestValidateConfig_FloatPort_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
err := p.ValidateConfig(context.Background(), channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": float64(993),
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": float64(587),
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateConfig_Int64Port_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
err := p.ValidateConfig(context.Background(), channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": int64(993),
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": int64(587),
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateConfig_StringPort_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
err := p.ValidateConfig(context.Background(), channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": "993",
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": "587",
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateConfig_InvalidStringPort_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
err := p.ValidateConfig(context.Background(), channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": "abc",
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": "587",
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "imap_port")
|
|
}
|
|
|
|
func TestValidateConfig_BoolPort_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
err := p.ValidateConfig(context.Background(), channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": true,
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": 587,
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "imap_port")
|
|
}
|
|
|
|
func TestValidateConfig_DefaultPort_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
// Missing imap_port entirely → default 0 → error
|
|
err := p.ValidateConfig(context.Background(), channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": 587,
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "imap_port")
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — OnCreate forward_to_address path
|
|
// ============================================================
|
|
|
|
func TestOnCreate_ForwardToAddressSet_Cov10(t *testing.T) {
|
|
// OnCreate with a Service that has nil imap/smtp → will panic on nil dep.
|
|
// Use safeCall to handle that.
|
|
p := NewEmailProvider(&Service{}, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
safeCallCov10(func() {
|
|
cfg, err := p.OnCreate(context.Background(), inbox, channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": 993,
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": 587,
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
// Will error on IMAP validation, but the smtp_email is set in config
|
|
_ = cfg
|
|
_ = err
|
|
})
|
|
}
|
|
|
|
func TestOnCreate_NilService_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
safeCallCov10(func() {
|
|
_, _ = p.OnCreate(context.Background(), inbox, channelpkg.ChannelConfig{})
|
|
})
|
|
}
|
|
|
|
func TestOnCreate_SMTPFail_Cov10(t *testing.T) {
|
|
// Use a Service with nil imapListener → IMAP validation panics
|
|
p := &EmailProvider{
|
|
service: &Service{},
|
|
}
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
safeCallCov10(func() {
|
|
_, _ = p.OnCreate(context.Background(), inbox, channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_email": "user@test.com",
|
|
"imap_password": "pass",
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_port": 587,
|
|
"smtp_email": "user@test.com",
|
|
"smtp_password": "pass",
|
|
})
|
|
})
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — ProcessIncoming with various EmailMessage fields
|
|
// ============================================================
|
|
|
|
func TestProcessIncoming_WithHTMLContent_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
|
|
emailMsg := EmailMessage{
|
|
MessageID: "msg-html",
|
|
FromAddress: "sender@test.com",
|
|
FromName: "Sender",
|
|
Subject: "HTML Test",
|
|
HTMLContent: "<p>Hello</p>",
|
|
TextContent: "Hello",
|
|
ToAddresses: []string{"to@test.com"},
|
|
CcAddresses: []string{"cc@test.com"},
|
|
ReplyToAddress: "reply@test.com",
|
|
References: "<ref-1>",
|
|
}
|
|
payload, _ := json.Marshal(emailMsg)
|
|
msg, err := p.ProcessIncoming(context.Background(), inbox, payload)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, msg)
|
|
assert.Equal(t, "msg-html", msg.SourceID)
|
|
assert.Equal(t, "Hello", msg.Content)
|
|
assert.Equal(t, "HTML Test", msg.Extra["subject"])
|
|
assert.Equal(t, "<p>Hello</p>", msg.Extra["html_content"])
|
|
assert.Equal(t, "reply@test.com", msg.Extra["reply_to_address"])
|
|
}
|
|
|
|
func TestProcessIncoming_WithUID_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
|
|
emailMsg := EmailMessage{
|
|
MessageID: "msg-uid",
|
|
UID: 42,
|
|
}
|
|
payload, _ := json.Marshal(emailMsg)
|
|
msg, err := p.ProcessIncoming(context.Background(), inbox, payload)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, msg)
|
|
// UID should be in Extra
|
|
uidVal, ok := msg.Extra["imap_uid"]
|
|
assert.True(t, ok)
|
|
_ = uidVal
|
|
}
|
|
|
|
func TestProcessIncoming_NoInReplyTo_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
|
|
emailMsg := EmailMessage{
|
|
MessageID: "msg-no-reply",
|
|
FromAddress: "sender@test.com",
|
|
TextContent: "hello",
|
|
}
|
|
payload, _ := json.Marshal(emailMsg)
|
|
msg, err := p.ProcessIncoming(context.Background(), inbox, payload)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, msg)
|
|
assert.Empty(t, msg.ReplyToID)
|
|
}
|
|
|
|
func TestProcessIncoming_WithInReplyTo_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
|
|
emailMsg := EmailMessage{
|
|
MessageID: "msg-reply",
|
|
InReplyTo: "parent-msg-id",
|
|
}
|
|
payload, _ := json.Marshal(emailMsg)
|
|
msg, err := p.ProcessIncoming(context.Background(), inbox, payload)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, msg)
|
|
assert.Equal(t, "parent-msg-id", msg.ReplyToID)
|
|
assert.Equal(t, "parent-msg-id", msg.ConversationID)
|
|
}
|
|
|
|
func TestProcessIncoming_EmptyPayload_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := p.ProcessIncoming(context.Background(), inbox, []byte{})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestProcessIncoming_NilPayload_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := p.ProcessIncoming(context.Background(), inbox, nil)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestProcessIncoming_MultipleAttachments_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
|
|
emailMsg := EmailMessage{
|
|
MessageID: "msg-multi-att",
|
|
FromAddress: "sender@test.com",
|
|
TextContent: "see attachments",
|
|
Attachments: []EmailAttachment{
|
|
{Filename: "file1.pdf", ContentType: "application/pdf", URL: "https://example.com/f1", FileSize: 1024},
|
|
{Filename: "file2.png", ContentType: "image/png", URL: "https://example.com/f2", FileSize: 2048},
|
|
{Filename: "file3.txt", ContentType: "text/plain", URL: "https://example.com/f3"},
|
|
},
|
|
}
|
|
payload, _ := json.Marshal(emailMsg)
|
|
msg, err := p.ProcessIncoming(context.Background(), inbox, payload)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, msg)
|
|
assert.Len(t, msg.Attachments, 3)
|
|
assert.Equal(t, "file1.pdf", msg.Attachments[0].Filename)
|
|
assert.Equal(t, int64(1024), msg.Attachments[0].FileSize)
|
|
assert.Equal(t, "file3.txt", msg.Attachments[2].Filename)
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — PollMessages with messages
|
|
// ============================================================
|
|
|
|
func TestPollMessages_WithMessages_Cov10(t *testing.T) {
|
|
// PollMessages with a nil service → will panic, use safeCall
|
|
p := &EmailProvider{}
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
safeCallCov10(func() {
|
|
_, _ = p.PollMessages(context.Background(), inbox)
|
|
})
|
|
}
|
|
|
|
func TestPollMessages_WithValidConfig_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(NewService(), nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
inbox.ChannelConfig = `{"imap_address":"127.0.0.1","imap_port":1,"imap_login":"user","imap_password":"pass","imap_ssl_mode":"none"}`
|
|
// Will fail on connection but exercise the config parsing path
|
|
_, _ = p.PollMessages(context.Background(), inbox)
|
|
}
|
|
|
|
func TestPollMessages_NilInbox_Cov10(t *testing.T) {
|
|
p := &EmailProvider{}
|
|
safeCallCov10(func() {
|
|
_, _ = p.PollMessages(context.Background(), nil)
|
|
})
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — SendMessage edge cases
|
|
// ============================================================
|
|
|
|
func TestSendMessage_EmptyContact_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(NewService(), nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
inbox.ChannelConfig = `{"smtp_email":"from@test.com"}`
|
|
contact := &model.Contact{}
|
|
_, err := p.SendMessage(context.Background(), inbox, &model.Message{Content: "test"}, contact)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "source_id")
|
|
}
|
|
|
|
func TestSendMessage_NilPipeline_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(NewService(), nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
inbox.ChannelConfig = `{"smtp_email":"from@test.com"}`
|
|
contact := &model.Contact{SourceID: "to@test.com"}
|
|
// This will attempt SMTP send and fail with network error
|
|
_, err := p.SendMessage(context.Background(), inbox, &model.Message{Content: "test"}, contact)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSendMessage_SuccessResult_Cov10(t *testing.T) {
|
|
// Test the result structure when SMTP succeeds (unlikely without a server)
|
|
// But we can verify the error path
|
|
p := NewEmailProvider(NewService(), nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 5}}
|
|
inbox.ChannelConfig = `{"smtp_email":"from@test.com","smtp_address":"127.0.0.1","smtp_port":1,"smtp_login":"user","smtp_password":"pass","smtp_ssl_mode":"none"}`
|
|
contact := &model.Contact{SourceID: "to@test.com"}
|
|
msg := &model.Message{Content: "hello world"}
|
|
_, err := p.SendMessage(context.Background(), inbox, msg, contact)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "SMTP")
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — GetContactProfile
|
|
// ============================================================
|
|
|
|
func TestGetContactProfile_EmptySource_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{}
|
|
profile, err := p.GetContactProfile(context.Background(), inbox, "")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, profile)
|
|
assert.Equal(t, "", profile.Name)
|
|
assert.Equal(t, "", profile.Extra["email"])
|
|
}
|
|
|
|
func TestGetContactProfile_WithDomain_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{}
|
|
profile, err := p.GetContactProfile(context.Background(), inbox, "user@example.com")
|
|
require.NoError(t, err)
|
|
require.NotNil(t, profile)
|
|
assert.Equal(t, "user@example.com", profile.Name)
|
|
assert.Equal(t, "user@example.com", profile.Extra["email"])
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — ConfigSchema properties
|
|
// ============================================================
|
|
|
|
func TestConfigSchema_AllProperties_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
schema := p.ConfigSchema()
|
|
require.NotNil(t, schema)
|
|
|
|
expectedProps := []string{
|
|
"imap_address", "imap_port", "imap_email", "imap_password", "imap_ssl",
|
|
"smtp_address", "smtp_port", "smtp_email", "smtp_password", "smtp_ssl",
|
|
}
|
|
for _, prop := range expectedProps {
|
|
assert.Contains(t, schema.Properties, prop, "missing property: %s", prop)
|
|
}
|
|
|
|
// Check imap_port default
|
|
assert.Equal(t, 993, schema.Properties["imap_port"].Default)
|
|
assert.Equal(t, 587, schema.Properties["smtp_port"].Default)
|
|
|
|
// Check required fields
|
|
for _, req := range expectedProps {
|
|
if req == "imap_ssl" || req == "smtp_ssl" {
|
|
continue // These are not required
|
|
}
|
|
assert.Contains(t, schema.Required, req, "missing required: %s", req)
|
|
}
|
|
|
|
// Check secret fields
|
|
assert.True(t, schema.Properties["imap_password"].Secret)
|
|
assert.True(t, schema.Properties["smtp_password"].Secret)
|
|
|
|
// Check email format
|
|
assert.Equal(t, "email", schema.Properties["imap_email"].Format)
|
|
assert.Equal(t, "email", schema.Properties["smtp_email"].Format)
|
|
}
|
|
|
|
func TestDefaultConfig_Values_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
cfg := p.DefaultConfig()
|
|
assert.Equal(t, 993, cfg["imap_port"])
|
|
assert.Equal(t, 587, cfg["smtp_port"])
|
|
assert.True(t, cfg["imap_ssl"].(bool))
|
|
assert.True(t, cfg["smtp_ssl"].(bool))
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — Capabilities
|
|
// ============================================================
|
|
|
|
func TestCapabilities_FullCheck_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
caps := p.Capabilities()
|
|
assert.True(t, caps.SupportsAttachments)
|
|
assert.False(t, caps.SupportsLocation)
|
|
assert.False(t, caps.SupportsTypingIndicator)
|
|
assert.True(t, caps.SupportsDeliveryStatus)
|
|
assert.True(t, caps.SupportsReplies)
|
|
assert.False(t, caps.SupportsEmojiReactions)
|
|
assert.False(t, caps.SupportsVoiceMessages)
|
|
assert.False(t, caps.SupportsVideoCalls)
|
|
assert.False(t, caps.SupportsCustomCards)
|
|
assert.False(t, caps.SupportsTemplates)
|
|
assert.True(t, caps.SupportsEmailHeaders)
|
|
assert.Equal(t, int64(25*1024*1024), caps.MaxAttachmentSize)
|
|
assert.Equal(t, 0, caps.MaxTextLength)
|
|
}
|
|
|
|
// ============================================================
|
|
// smtp_sender.go — Send with various configs
|
|
// ============================================================
|
|
|
|
func TestSMTPSend_DefaultSSLMode_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
_, err := s.Send(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
}, "from@test.com", "to@test.com", "subj", "body")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPSend_EmptyFrom_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
_, err := s.Send(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
"smtp_ssl_mode": "none",
|
|
"email": "default@test.com",
|
|
}, "", "to@test.com", "subj", "body")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPSend_WithMailboxName_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
_, err := s.Send(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
"smtp_ssl_mode": "none",
|
|
"mailbox_name": "Support Team",
|
|
}, "from@test.com", "to@test.com", "subj", "body")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPSend_SSLMode_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
_, err := s.Send(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
"smtp_ssl_mode": "ssl",
|
|
}, "from@test.com", "to@test.com", "subj", "body")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "TLS")
|
|
}
|
|
|
|
func TestSMTPSend_STARTTLSMode_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
_, err := s.Send(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
"smtp_ssl_mode": "starttls",
|
|
}, "from@test.com", "to@test.com", "subj", "body")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPSend_NoPassword_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
_, err := s.Send(channelpkg.ChannelConfig{
|
|
"smtp_address": "smtp.test.com",
|
|
"smtp_login": "user",
|
|
}, "from@test.com", "to@test.com", "subj", "body")
|
|
// address and login are set but password is missing → "address and login required" only checks those two
|
|
// So it proceeds to connection attempt
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPValidateConnection_DefaultMode_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
err := s.ValidateConnection(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPValidateConnection_SSLMode_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
err := s.ValidateConnection(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
"smtp_ssl_mode": "ssl",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestSMTPValidateConnection_STARTTLSMode_Cov10(t *testing.T) {
|
|
s := NewSMTPSender()
|
|
err := s.ValidateConnection(channelpkg.ChannelConfig{
|
|
"smtp_address": "127.0.0.1",
|
|
"smtp_port": 1,
|
|
"smtp_login": "user",
|
|
"smtp_password": "pass",
|
|
"smtp_ssl_mode": "starttls",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
// ============================================================
|
|
// smtp_sender.go — composeSMTPMessage edge cases
|
|
// ============================================================
|
|
|
|
func TestComposeSMTPMessage_EmptyBody_Cov10(t *testing.T) {
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "Test", "", "<msg-id>")
|
|
assert.Contains(t, msg, "From: from@test.com")
|
|
assert.Contains(t, msg, "To: to@test.com")
|
|
assert.Contains(t, msg, "Subject: Test")
|
|
assert.Contains(t, msg, "Message-Id: <msg-id>")
|
|
assert.Contains(t, msg, "MIME-Version: 1.0")
|
|
assert.Contains(t, msg, "Content-Type: text/plain; charset=UTF-8")
|
|
assert.Contains(t, msg, "Content-Transfer-Encoding: 8bit")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_HTMLBody_Cov10(t *testing.T) {
|
|
body := "<html><body><p>Hello</p></body></html>"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "HTML Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/html; charset=UTF-8")
|
|
assert.Contains(t, msg, body)
|
|
}
|
|
|
|
func TestComposeSMTPMessage_DivBody_Cov10(t *testing.T) {
|
|
body := "<div>Content</div>"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "Div Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/html; charset=UTF-8")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_PTagBody_Cov10(t *testing.T) {
|
|
body := "<p>Paragraph</p>"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "P Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/html; charset=UTF-8")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_BrTagBody_Cov10(t *testing.T) {
|
|
body := "Line 1<br>Line 2"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "BR Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/html; charset=UTF-8")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_BoldTagBody_Cov10(t *testing.T) {
|
|
body := "<b>Bold text</b>"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "Bold Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/html; charset=UTF-8")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_ItalicTagBody_Cov10(t *testing.T) {
|
|
body := "<i>Italic text</i>"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "Italic Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/html; charset=UTF-8")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_PlainTextBody_Cov10(t *testing.T) {
|
|
body := "This is plain text"
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "Plain Test", body, "<msg-id>")
|
|
assert.Contains(t, msg, "Content-Type: text/plain; charset=UTF-8")
|
|
}
|
|
|
|
func TestComposeSMTPMessage_SpecialChars_Cov10(t *testing.T) {
|
|
msg := composeSMTPMessage("from@test.com", "to@test.com", "Subjéct wîth spéciål", "Bödý", "<msg-id>")
|
|
assert.Contains(t, msg, "Subjéct wîth spéciål")
|
|
}
|
|
|
|
// ============================================================
|
|
// smtp_sender.go — extractMessageIDFromRaw edge cases
|
|
// ============================================================
|
|
|
|
func TestExtractMessageIDFromRaw_CapitalID_Cov10(t *testing.T) {
|
|
msg := []byte("From: test@test.com\r\nMessage-ID: <capital-id@test.com>\r\n\r\nbody")
|
|
id := extractMessageIDFromRaw(msg)
|
|
assert.Equal(t, "<capital-id@test.com>", id)
|
|
}
|
|
|
|
func TestExtractMessageIDFromRaw_MixedCase_Cov10(t *testing.T) {
|
|
msg := []byte("From: test@test.com\r\nMessage-Id: <mixed-id@test.com>\r\n\r\nbody")
|
|
id := extractMessageIDFromRaw(msg)
|
|
assert.Equal(t, "<mixed-id@test.com>", id)
|
|
}
|
|
|
|
func TestExtractMessageIDFromRaw_NoMessageID_Cov10(t *testing.T) {
|
|
msg := []byte("From: test@test.com\r\nSubject: Test\r\n\r\nbody")
|
|
id := extractMessageIDFromRaw(msg)
|
|
assert.Equal(t, "", id)
|
|
}
|
|
|
|
func TestExtractMessageIDFromRaw_Empty_Cov10(t *testing.T) {
|
|
id := extractMessageIDFromRaw([]byte{})
|
|
assert.Equal(t, "", id)
|
|
}
|
|
|
|
func TestExtractMessageIDFromRaw_OnlyNewlines_Cov10(t *testing.T) {
|
|
id := extractMessageIDFromRaw([]byte("\r\n\r\n"))
|
|
assert.Equal(t, "", id)
|
|
}
|
|
|
|
// ============================================================
|
|
// imap_listener.go — connectIMAP with various SSL modes
|
|
// ============================================================
|
|
|
|
func TestConnectIMAP_SSLMode_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
_, err := l.connectIMAP("127.0.0.1:1", "ssl")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "TLS")
|
|
}
|
|
|
|
func TestConnectIMAP_StartTLSMode_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
_, err := l.connectIMAP("127.0.0.1:1", "starttls")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestConnectIMAP_NoneMode_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
_, err := l.connectIMAP("127.0.0.1:1", "none")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestConnectIMAP_DefaultMode_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
_, err := l.connectIMAP("127.0.0.1:1", "")
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "TLS")
|
|
}
|
|
|
|
func TestConnectIMAP_UnknownMode_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
_, err := l.connectIMAP("127.0.0.1:1", "unknown")
|
|
require.Error(t, err)
|
|
// Default mode is SSL
|
|
assert.Contains(t, err.Error(), "TLS")
|
|
}
|
|
|
|
// ============================================================
|
|
// imap_listener.go — Fetch with various configs
|
|
// ============================================================
|
|
|
|
func TestFetch_MissingLogin_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := l.Fetch(context.Background(), inbox, channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_port": 993,
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "configuration incomplete")
|
|
}
|
|
|
|
func TestFetch_MissingPassword_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := l.Fetch(context.Background(), inbox, channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_login": "user",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "configuration incomplete")
|
|
}
|
|
|
|
func TestFetch_WithMailboxName_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := l.Fetch(context.Background(), inbox, channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "none",
|
|
"mailbox_name": "CustomBox",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestFetch_DefaultMailbox_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := l.Fetch(context.Background(), inbox, channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "none",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestFetch_NilInbox_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
safeCallCov10(func() {
|
|
_, _ = l.Fetch(context.Background(), nil, channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "none",
|
|
})
|
|
})
|
|
}
|
|
|
|
// ============================================================
|
|
// imap_listener.go — ValidateConnection with various configs
|
|
// ============================================================
|
|
|
|
func TestValidateConnection_WithMailboxName_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "none",
|
|
"mailbox_name": "Sent",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateConnection_DefaultMailbox_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "none",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateConnection_SSLMode_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "ssl",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateConnection_StartTLSMode_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
"imap_ssl_mode": "starttls",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateConnection_DefaultMode_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "127.0.0.1",
|
|
"imap_port": 1,
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestValidateConnection_MissingAddress_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_login": "user",
|
|
"imap_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "required")
|
|
}
|
|
|
|
func TestValidateConnection_MissingLogin_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_password": "pass",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "required")
|
|
}
|
|
|
|
func TestValidateConnection_MissingPassword_Cov10(t *testing.T) {
|
|
l := NewIMAPListener()
|
|
err := l.ValidateConnection(channelpkg.ChannelConfig{
|
|
"imap_address": "imap.test.com",
|
|
"imap_login": "user",
|
|
})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "required")
|
|
}
|
|
|
|
// ============================================================
|
|
// imap_listener.go — parseMessage with nil
|
|
// ============================================================
|
|
|
|
func TestParseMessage_NilClient_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
safeCallCov10(func() {
|
|
_, _ = l.parseMessage(context.Background(), nil, nil, "", &model.Inbox{})
|
|
})
|
|
}
|
|
|
|
func TestParseMessage_NilInbox_Cov10(t *testing.T) {
|
|
l := &IMAPListener{}
|
|
safeCallCov10(func() {
|
|
_, _ = l.parseMessage(context.Background(), nil, nil, "", nil)
|
|
})
|
|
}
|
|
|
|
// ============================================================
|
|
// webhook_handler.go — HandleWebhookRequest edge cases
|
|
// ============================================================
|
|
|
|
func TestHandleWebhookRequest_EmptyBody_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest("POST", "/webhook", nil)
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_ValidMailgun_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := `{"from":"sender@example.com","subject":"Test","body-plain":"Hello","to":"recipient@example.com","Message-Id":"msg-1"}`
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
r.Header.Set("X-Mailgun-Sid", "12345")
|
|
r.Header.Set("Content-Type", "application/json")
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
assert.Contains(t, w.Body.String(), "accepted")
|
|
}
|
|
|
|
func TestHandleWebhookRequest_ValidSendGrid_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := `{"envelope":{"from":"sender@example.com","to":["recipient@example.com"]},"subject":"Test","text":"Hello"}`
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
r.Header.Set("X-Sg-Envelope-Id", "env-123")
|
|
r.Header.Set("Content-Type", "application/json")
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_GenericJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := `{"from":"sender@example.com","subject":"Test","text":"Hello","to":"recipient@example.com"}`
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
r.Header.Set("Content-Type", "application/json")
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_RawEmail_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := "From: sender@example.com\r\nSubject: Test\r\n\r\nHello world"
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_InvalidMailgunJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := "invalid json"
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
r.Header.Set("X-Mailgun-Sid", "12345")
|
|
h.HandleWebhookRequest(w, r)
|
|
// Should still return 200 to prevent retries
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_InvalidSendGridJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := "invalid json"
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
r.Header.Set("X-Sg-Envelope-Id", "env-123")
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_InvalidGenericJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := "not json at all"
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
r.Header.Set("Content-Type", "application/json")
|
|
h.HandleWebhookRequest(w, r)
|
|
// Falls through to raw email parsing
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
func TestHandleWebhookRequest_NoContentType_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := "From: sender@example.com\r\nSubject: Test\r\n\r\nHello"
|
|
r := httptest.NewRequest("POST", "/webhook", strings.NewReader(body))
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
}
|
|
|
|
// ============================================================
|
|
// webhook_handler.go — ParseWebhookBody
|
|
// ============================================================
|
|
|
|
func TestParseWebhookBody_Mailgun_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"from":"sender@example.com","subject":"Test","body-plain":"Hello","to":"recipient@example.com","Message-Id":"msg-1"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Equal(t, "Test", msg.Subject)
|
|
assert.Equal(t, "Hello", msg.TextContent)
|
|
assert.Equal(t, "msg-1", msg.MessageID)
|
|
assert.Equal(t, []string{"recipient@example.com"}, msg.ToAddresses)
|
|
}
|
|
|
|
func TestParseWebhookBody_SendGrid_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"envelope":{"from":"sender@example.com","to":["recipient@example.com"]},"subject":"Test","text":"Hello","html":"<p>Hello</p>"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Equal(t, "Test", msg.Subject)
|
|
assert.Equal(t, "Hello", msg.TextContent)
|
|
assert.Equal(t, "<p>Hello</p>", msg.HTMLContent)
|
|
assert.Equal(t, []string{"recipient@example.com"}, msg.ToAddresses)
|
|
}
|
|
|
|
func TestParseWebhookBody_GenericJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"from":"sender@example.com","subject":"Test","text":"Hello","to":"recipient@example.com"}`
|
|
headers := http.Header{}
|
|
headers.Set("Content-Type", "application/json")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Equal(t, "Test", msg.Subject)
|
|
assert.Equal(t, []string{"recipient@example.com"}, msg.ToAddresses)
|
|
}
|
|
|
|
func TestParseWebhookBody_RawEmail_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := "From: sender@example.com\r\nSubject: Test\r\n\r\nHello world"
|
|
msg, err := h.ParseWebhookBody([]byte(body), http.Header{})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, msg.TextContent)
|
|
assert.Contains(t, msg.TextContent, "Hello world")
|
|
}
|
|
|
|
func TestParseWebhookBody_MailgunWithAllFields_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"from":"sender@example.com","subject":"Re: Test","body-plain":"Hello","body-html":"<p>Hello</p>","to":"recipient@example.com","Message-Id":"msg-full","In-Reply-To":"parent","References":"<parent>","Date":"2024-01-01"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Equal(t, "Re: Test", msg.Subject)
|
|
assert.Equal(t, "Hello", msg.TextContent)
|
|
assert.Equal(t, "<p>Hello</p>", msg.HTMLContent)
|
|
assert.Equal(t, "msg-full", msg.MessageID)
|
|
assert.Equal(t, "parent", msg.InReplyTo)
|
|
assert.Equal(t, "<parent>", msg.References)
|
|
assert.Equal(t, "2024-01-01", msg.Date)
|
|
}
|
|
|
|
func TestParseWebhookBody_SendGridWithAllFields_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"envelope":{"from":"sender@example.com","to":["r1@example.com","r2@example.com"]},"subject":"Test","text":"Hello","html":"<p>Hello</p>","messageId":"msg-sg","in_reply_to":"parent","date":"2024-01-01"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Len(t, msg.ToAddresses, 2)
|
|
assert.Equal(t, "r1@example.com", msg.ToAddresses[0])
|
|
assert.Equal(t, "r2@example.com", msg.ToAddresses[1])
|
|
assert.Equal(t, "msg-sg", msg.MessageID)
|
|
assert.Equal(t, "parent", msg.InReplyTo)
|
|
assert.Equal(t, "2024-01-01", msg.Date)
|
|
}
|
|
|
|
func TestParseWebhookBody_SendGridNoEnvelope_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"subject":"Test","text":"Hello"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Test", msg.Subject)
|
|
assert.Equal(t, "Hello", msg.TextContent)
|
|
assert.Empty(t, msg.FromAddress)
|
|
}
|
|
|
|
func TestParseWebhookBody_SendGridEnvelopeNoTo_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"envelope":{"from":"sender@example.com"},"subject":"Test"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Nil(t, msg.ToAddresses)
|
|
}
|
|
|
|
func TestParseWebhookBody_SendGridBadJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := "invalid json"
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
_, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestParseWebhookBody_MailgunBadJSON_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := "invalid json"
|
|
headers := http.Header{}
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
_, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestParseWebhookBody_MailgunNoTo_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"from":"sender@example.com","subject":"Test"}`
|
|
headers := http.Header{}
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Nil(t, msg.ToAddresses)
|
|
}
|
|
|
|
func TestParseWebhookBody_GenericJSONNoTo_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"from":"sender@example.com","subject":"Test"}`
|
|
headers := http.Header{}
|
|
headers.Set("Content-Type", "application/json")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
assert.Nil(t, msg.ToAddresses)
|
|
}
|
|
|
|
func TestParseWebhookBody_GenericJSONWithHTML_Cov10(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
body := `{"from":"sender@example.com","subject":"Test","html":"<p>HTML</p>","message_id":"msg-1","in_reply_to":"parent","date":"2024-01-01"}`
|
|
headers := http.Header{}
|
|
headers.Set("Content-Type", "application/json")
|
|
msg, err := h.ParseWebhookBody([]byte(body), headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "<p>HTML</p>", msg.HTMLContent)
|
|
assert.Equal(t, "msg-1", msg.MessageID)
|
|
assert.Equal(t, "parent", msg.InReplyTo)
|
|
assert.Equal(t, "2024-01-01", msg.Date)
|
|
}
|
|
|
|
// ============================================================
|
|
// webhook_handler.go — isMailgunPayload / isSendGridPayload
|
|
// ============================================================
|
|
|
|
func TestIsMailgunPayload_True_Cov10(t *testing.T) {
|
|
headers := http.Header{}
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
assert.True(t, isMailgunPayload(headers))
|
|
}
|
|
|
|
func TestIsMailgunPayload_False_Cov10(t *testing.T) {
|
|
headers := http.Header{}
|
|
assert.False(t, isMailgunPayload(headers))
|
|
}
|
|
|
|
func TestIsSendGridPayload_True_Cov10(t *testing.T) {
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
assert.True(t, isSendGridPayload(headers))
|
|
}
|
|
|
|
func TestIsSendGridPayload_False_Cov10(t *testing.T) {
|
|
headers := http.Header{}
|
|
assert.False(t, isSendGridPayload(headers))
|
|
}
|
|
|
|
// ============================================================
|
|
// webhook_handler.go — parseRelayPayload direct calls
|
|
// ============================================================
|
|
|
|
func TestParseRelayPayload_Mailgun_Cov10(t *testing.T) {
|
|
body := []byte(`{"from":"sender@example.com","subject":"Test","body-plain":"Hello"}`)
|
|
headers := http.Header{}
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
msg, err := parseRelayPayload(body, headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
}
|
|
|
|
func TestParseRelayPayload_SendGrid_Cov10(t *testing.T) {
|
|
body := []byte(`{"envelope":{"from":"sender@example.com","to":["r@example.com"]},"subject":"Test"}`)
|
|
headers := http.Header{}
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
msg, err := parseRelayPayload(body, headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
}
|
|
|
|
func TestParseRelayPayload_GenericJSON_Cov10(t *testing.T) {
|
|
body := []byte(`{"from":"sender@example.com","subject":"Test","text":"Hello"}`)
|
|
headers := http.Header{}
|
|
headers.Set("Content-Type", "application/json")
|
|
msg, err := parseRelayPayload(body, headers)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "sender@example.com", msg.FromAddress)
|
|
}
|
|
|
|
func TestParseRelayPayload_RawEmail_Cov10(t *testing.T) {
|
|
body := []byte("From: sender@example.com\r\nSubject: Test\r\n\r\nHello")
|
|
msg, err := parseRelayPayload(body, http.Header{})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, msg.TextContent)
|
|
}
|
|
|
|
func TestParseRelayPayload_InvalidJSONFallsToRaw_Cov10(t *testing.T) {
|
|
body := []byte("not json at all")
|
|
headers := http.Header{}
|
|
headers.Set("Content-Type", "application/json")
|
|
msg, err := parseRelayPayload(body, headers)
|
|
// Falls through to raw email parsing
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, msg.TextContent)
|
|
}
|
|
|
|
// ============================================================
|
|
// service.go — extractEmailAddress / extractDisplayName
|
|
// ============================================================
|
|
|
|
func TestExtractEmailAddress_Simple_Cov10(t *testing.T) {
|
|
assert.Equal(t, "john@example.com", extractEmailAddress("john@example.com"))
|
|
}
|
|
|
|
func TestExtractEmailAddress_WithName_Cov10(t *testing.T) {
|
|
assert.Equal(t, "john@example.com", extractEmailAddress("John Doe <john@example.com>"))
|
|
}
|
|
|
|
func TestExtractEmailAddress_Empty_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", extractEmailAddress(""))
|
|
}
|
|
|
|
func TestExtractEmailAddress_BracketsOnly_Cov10(t *testing.T) {
|
|
assert.Equal(t, "john@example.com", extractEmailAddress("<john@example.com>"))
|
|
}
|
|
|
|
func TestExtractEmailAddress_NoCloseBracket_Cov10(t *testing.T) {
|
|
result := extractEmailAddress("<john@example.com")
|
|
assert.NotEmpty(t, result)
|
|
}
|
|
|
|
func TestExtractDisplayName_WithName_Cov10(t *testing.T) {
|
|
assert.Equal(t, "John Doe", extractDisplayName("John Doe <john@example.com>"))
|
|
}
|
|
|
|
func TestExtractDisplayName_NoName_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", extractDisplayName("john@example.com"))
|
|
}
|
|
|
|
func TestExtractDisplayName_Empty_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", extractDisplayName(""))
|
|
}
|
|
|
|
func TestExtractDisplayName_WithQuotes_Cov10(t *testing.T) {
|
|
result := extractDisplayName(`"John Doe" <john@example.com>`)
|
|
assert.Contains(t, result, "John Doe")
|
|
}
|
|
|
|
// ============================================================
|
|
// service.go — Service methods
|
|
// ============================================================
|
|
|
|
func TestService_ValidateIMAPConnection_Empty_Cov10(t *testing.T) {
|
|
s := NewService()
|
|
err := s.ValidateIMAPConnection(channelpkg.ChannelConfig{})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestService_ValidateSMTPConnection_Empty_Cov10(t *testing.T) {
|
|
s := NewService()
|
|
err := s.ValidateSMTPConnection(channelpkg.ChannelConfig{})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestService_FetchIMAPMessages_Empty_Cov10(t *testing.T) {
|
|
s := NewService()
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := s.FetchIMAPMessages(context.Background(), inbox, channelpkg.ChannelConfig{})
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestService_SendViaSMTP_Empty_Cov10(t *testing.T) {
|
|
s := NewService()
|
|
_, err := s.SendViaSMTP(channelpkg.ChannelConfig{}, "from@test.com", "to@test.com", "subject", "body")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestNewService_NotNil_Cov10(t *testing.T) {
|
|
s := NewService()
|
|
require.NotNil(t, s)
|
|
assert.NotNil(t, s.imapListener)
|
|
assert.NotNil(t, s.smtpSender)
|
|
}
|
|
|
|
// ============================================================
|
|
// pipeline.go — IncomingProcessor / OutgoingProcessor
|
|
// ============================================================
|
|
|
|
func TestNewIncomingProcessor_NotNil_Cov10(t *testing.T) {
|
|
ip := NewIncomingProcessor(nil)
|
|
require.NotNil(t, ip)
|
|
}
|
|
|
|
func TestIncomingProcessor_Process_NilHandler_Cov10(t *testing.T) {
|
|
ip := NewIncomingProcessor(nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := ip.Process(context.Background(), inbox, &EmailMessage{})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not initialized")
|
|
}
|
|
|
|
func TestIncomingProcessor_Process_Success_Cov10(t *testing.T) {
|
|
ip := NewIncomingProcessor(&mockEmailHandlerCov10{})
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := ip.Process(context.Background(), inbox, &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
Subject: "Test",
|
|
TextContent: "hello",
|
|
})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestNewOutgoingProcessor_NotNil_Cov10(t *testing.T) {
|
|
op := NewOutgoingProcessor(nil)
|
|
require.NotNil(t, op)
|
|
}
|
|
|
|
func TestOutgoingProcessor_Process_NilHandler_Cov10(t *testing.T) {
|
|
op := NewOutgoingProcessor(nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
_, err := op.Process(context.Background(), inbox, &model.Message{}, &model.Contact{})
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "not initialized")
|
|
}
|
|
|
|
func TestOutgoingProcessor_Process_Success_Cov10(t *testing.T) {
|
|
op := NewOutgoingProcessor(&mockEmailOutgoingHandlerCov10{})
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
result, err := op.Process(context.Background(), inbox, &model.Message{}, &model.Contact{})
|
|
require.NoError(t, err)
|
|
require.NotNil(t, result)
|
|
assert.Equal(t, "test_msg_id", result.ExternalID)
|
|
}
|
|
|
|
// ============================================================
|
|
// pipeline.go — serializeEmailMessage
|
|
// ============================================================
|
|
|
|
func TestSerializeEmailMessage_Full_Cov10(t *testing.T) {
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
UID: 42,
|
|
FromAddress: "sender@test.com",
|
|
FromName: "Sender",
|
|
ReplyToAddress: "reply@test.com",
|
|
ToAddresses: []string{"to@test.com"},
|
|
CcAddresses: []string{"cc@test.com"},
|
|
InReplyTo: "parent",
|
|
References: "<parent>",
|
|
Subject: "Test",
|
|
TextContent: "hello",
|
|
HTMLContent: "<p>Hello</p>",
|
|
Date: "2024-01-01",
|
|
Attachments: []EmailAttachment{
|
|
{Filename: "file.pdf", ContentType: "application/pdf"},
|
|
},
|
|
}
|
|
data, err := serializeEmailMessage(emailMsg)
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, data)
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(data, &result)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "msg1", result["message_id"])
|
|
assert.Equal(t, "sender@test.com", result["from_address"])
|
|
assert.Equal(t, "Test", result["subject"])
|
|
}
|
|
|
|
func TestSerializeEmailMessage_Empty_Cov10(t *testing.T) {
|
|
data, err := serializeEmailMessage(&EmailMessage{})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, data)
|
|
}
|
|
|
|
// ============================================================
|
|
// types.go — utility functions
|
|
// ============================================================
|
|
|
|
func TestConfigInt_Int_Cov10(t *testing.T) {
|
|
assert.Equal(t, 993, configInt(channelpkg.ChannelConfig{"port": 993}, "port", 0))
|
|
}
|
|
|
|
func TestConfigInt_Int64_Cov10(t *testing.T) {
|
|
assert.Equal(t, 993, configInt(channelpkg.ChannelConfig{"port": int64(993)}, "port", 0))
|
|
}
|
|
|
|
func TestConfigInt_Float64_Cov10(t *testing.T) {
|
|
assert.Equal(t, 993, configInt(channelpkg.ChannelConfig{"port": float64(993)}, "port", 0))
|
|
}
|
|
|
|
func TestConfigInt_String_Cov10(t *testing.T) {
|
|
assert.Equal(t, 993, configInt(channelpkg.ChannelConfig{"port": "993"}, "port", 0))
|
|
}
|
|
|
|
func TestConfigInt_InvalidString_Cov10(t *testing.T) {
|
|
assert.Equal(t, 0, configInt(channelpkg.ChannelConfig{"port": "abc"}, "port", 0))
|
|
}
|
|
|
|
func TestConfigInt_Missing_Cov10(t *testing.T) {
|
|
assert.Equal(t, 993, configInt(channelpkg.ChannelConfig{}, "port", 993))
|
|
}
|
|
|
|
func TestConfigInt_Bool_Cov10(t *testing.T) {
|
|
assert.Equal(t, 0, configInt(channelpkg.ChannelConfig{"port": true}, "port", 0))
|
|
}
|
|
|
|
func TestHTMLToPlainText_Basic_Cov10(t *testing.T) {
|
|
result := htmlToPlainText("<p>Hello & goodbye</p>")
|
|
assert.Contains(t, result, "Hello & goodbye")
|
|
}
|
|
|
|
func TestHTMLToPlainText_Nbsp_Cov10(t *testing.T) {
|
|
result := htmlToPlainText(" hello ")
|
|
assert.Contains(t, result, "hello")
|
|
}
|
|
|
|
func TestHTMLToPlainText_Empty_Cov10(t *testing.T) {
|
|
result := htmlToPlainText("")
|
|
assert.Equal(t, "", result)
|
|
}
|
|
|
|
func TestStripHTMLTags_Basic_Cov10(t *testing.T) {
|
|
assert.Contains(t, stripHTMLTags("<p>Hello</p>"), "Hello")
|
|
}
|
|
|
|
func TestStripHTMLTags_Nested_Cov10(t *testing.T) {
|
|
result := stripHTMLTags("<div><p>Hello</p></div>")
|
|
assert.Contains(t, result, "Hello")
|
|
}
|
|
|
|
func TestStripHTMLTags_NoTags_Cov10(t *testing.T) {
|
|
assert.Equal(t, "Hello World", stripHTMLTags("Hello World"))
|
|
}
|
|
|
|
func TestCollapseWhitespace_MultipleSpaces_Cov10(t *testing.T) {
|
|
assert.Equal(t, "hello world", collapseWhitespace("hello world"))
|
|
}
|
|
|
|
func TestCollapseWhitespace_Newlines_Cov10(t *testing.T) {
|
|
assert.Equal(t, "hello world", collapseWhitespace("hello\n\nworld"))
|
|
}
|
|
|
|
func TestCollapseWhitespace_Empty_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", collapseWhitespace(""))
|
|
}
|
|
|
|
func TestLooksLikeHTML_HtmlTag_Cov10(t *testing.T) {
|
|
assert.True(t, looksLikeHTML("<html>test</html>"))
|
|
}
|
|
|
|
func TestLooksLikeHTML_PlainText_Cov10(t *testing.T) {
|
|
assert.False(t, looksLikeHTML("plain text"))
|
|
}
|
|
|
|
func TestLooksLikeHTML_Empty_Cov10(t *testing.T) {
|
|
assert.False(t, looksLikeHTML(""))
|
|
}
|
|
|
|
func TestStripContentIDBrackets_WithBrackets_Cov10(t *testing.T) {
|
|
assert.Equal(t, "id123", stripContentIDBrackets("<id123>"))
|
|
}
|
|
|
|
func TestStripContentIDBrackets_NoBrackets_Cov10(t *testing.T) {
|
|
assert.Equal(t, "id123", stripContentIDBrackets("id123"))
|
|
}
|
|
|
|
func TestStripContentIDBrackets_Empty_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", stripContentIDBrackets(""))
|
|
}
|
|
|
|
func TestExtractDomainFromAddress_Valid_Cov10(t *testing.T) {
|
|
assert.Equal(t, "example.com", extractDomainFromAddress("user@example.com"))
|
|
}
|
|
|
|
func TestExtractDomainFromAddress_NoAt_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", extractDomainFromAddress("notanemail"))
|
|
}
|
|
|
|
func TestBuildSubjectForReply_Empty_Cov10(t *testing.T) {
|
|
assert.Equal(t, "Re: Your inquiry", buildSubjectForReply(""))
|
|
}
|
|
|
|
func TestBuildSubjectForReply_New_Cov10(t *testing.T) {
|
|
assert.Equal(t, "Re: Test", buildSubjectForReply("Test"))
|
|
}
|
|
|
|
func TestBuildSubjectForReply_AlreadyRe_Cov10(t *testing.T) {
|
|
assert.Equal(t, "Re: Test", buildSubjectForReply("Re: Test"))
|
|
}
|
|
|
|
func TestGenerateMessageID_WithDomain_Cov10(t *testing.T) {
|
|
id := generateMessageID("test.com")
|
|
assert.Contains(t, id, "test.com")
|
|
assert.Contains(t, id, "gochat-")
|
|
}
|
|
|
|
func TestGenerateMessageID_EmptyDomain_Cov10(t *testing.T) {
|
|
id := generateMessageID("")
|
|
assert.Contains(t, id, "gochat.local")
|
|
}
|
|
|
|
func TestFormatDateNow_NotEmpty_Cov10(t *testing.T) {
|
|
date := formatDateNow()
|
|
assert.NotEmpty(t, date)
|
|
}
|
|
|
|
func TestParseMessageIDList_Empty_Cov10(t *testing.T) {
|
|
assert.Nil(t, parseMessageIDList(""))
|
|
}
|
|
|
|
func TestParseMessageIDList_Single_Cov10(t *testing.T) {
|
|
ids := parseMessageIDList("<msg1>")
|
|
assert.Len(t, ids, 1)
|
|
assert.Equal(t, "msg1", ids[0])
|
|
}
|
|
|
|
func TestExtractThreadIDFromReferences_Empty_Cov10(t *testing.T) {
|
|
assert.Equal(t, "", extractThreadIDFromReferences(""))
|
|
}
|
|
|
|
func TestExtractThreadIDFromReferences_Single_Cov10(t *testing.T) {
|
|
assert.Equal(t, "msg1", extractThreadIDFromReferences("<msg1>"))
|
|
}
|
|
|
|
// ============================================================
|
|
// types.go — convertToIncomingMessage
|
|
// ============================================================
|
|
|
|
func TestConvertToIncomingMessage_Basic_Cov10(t *testing.T) {
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 10}
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
FromName: "Sender",
|
|
Subject: "Test",
|
|
TextContent: "hello",
|
|
}
|
|
msg, err := convertToIncomingMessage(emailMsg, inbox)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, msg)
|
|
assert.Equal(t, "hello", msg.Content)
|
|
assert.Equal(t, "msg1", msg.SourceID)
|
|
assert.Equal(t, "sender@test.com", msg.SenderID)
|
|
assert.Equal(t, "Sender", msg.SenderName)
|
|
}
|
|
|
|
func TestConvertToIncomingMessage_ReplyTo_Cov10(t *testing.T) {
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
ReplyToAddress: "reply@test.com",
|
|
TextContent: "hello",
|
|
}
|
|
msg, err := convertToIncomingMessage(emailMsg, inbox)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "reply@test.com", msg.SenderID)
|
|
}
|
|
|
|
func TestConvertToIncomingMessage_HTMLOnly_Cov10(t *testing.T) {
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
HTMLContent: "<p>HTML content</p>",
|
|
}
|
|
msg, err := convertToIncomingMessage(emailMsg, inbox)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, msg.Content, "HTML content")
|
|
}
|
|
|
|
func TestConvertToIncomingMessage_NoContent_Cov10(t *testing.T) {
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
}
|
|
msg, err := convertToIncomingMessage(emailMsg, inbox)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "(no content)", msg.Content)
|
|
}
|
|
|
|
func TestConvertToIncomingMessage_WithAttachments_Cov10(t *testing.T) {
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
TextContent: "see attachment",
|
|
Attachments: []EmailAttachment{
|
|
{Filename: "file.pdf", ContentType: "application/pdf"},
|
|
},
|
|
}
|
|
msg, err := convertToIncomingMessage(emailMsg, inbox)
|
|
require.NoError(t, err)
|
|
assert.Len(t, msg.Attachments, 1)
|
|
}
|
|
|
|
func TestConvertToIncomingMessage_WithInReplyTo_Cov10(t *testing.T) {
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
emailMsg := &EmailMessage{
|
|
MessageID: "msg1",
|
|
FromAddress: "sender@test.com",
|
|
TextContent: "hello",
|
|
InReplyTo: "parent-msg",
|
|
}
|
|
msg, err := convertToIncomingMessage(emailMsg, inbox)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "parent-msg", msg.ReplyToID)
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — SetPipeline
|
|
// ============================================================
|
|
|
|
func TestSetPipeline_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
ip := NewIncomingProcessor(nil)
|
|
p.SetPipeline(ip)
|
|
assert.NotNil(t, p.pipeline)
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — OnDestroy
|
|
// ============================================================
|
|
|
|
func TestOnDestroy_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}}
|
|
err := p.OnDestroy(context.Background(), inbox, channelpkg.ChannelConfig{})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestOnDestroy_NilInbox_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
safeCallCov10(func() {
|
|
_ = p.OnDestroy(context.Background(), nil, channelpkg.ChannelConfig{})
|
|
})
|
|
}
|
|
|
|
// ============================================================
|
|
// provider.go — ValidateWebhookRequest
|
|
// ============================================================
|
|
|
|
func TestValidateWebhookRequest_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{}
|
|
err := p.ValidateWebhookRequest(context.Background(), inbox, &channelpkg.WebhookRequest{})
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestValidateWebhookRequest_NilRequest_Cov10(t *testing.T) {
|
|
p := NewEmailProvider(nil, nil, nil)
|
|
inbox := &model.Inbox{}
|
|
safeCallCov10(func() {
|
|
_ = p.ValidateWebhookRequest(context.Background(), inbox, nil)
|
|
})
|
|
}
|
|
|
|
// ============================================================
|
|
// Repository — PollState table name
|
|
// ============================================================
|
|
|
|
func TestPollState_TableName_Cov10(t *testing.T) {
|
|
assert.Equal(t, "email_poll_states", PollState{}.TableName())
|
|
}
|
|
|
|
// ============================================================
|
|
// Mock types
|
|
// ============================================================
|
|
|
|
type mockEmailHandlerCov10 struct{}
|
|
|
|
func (m *mockEmailHandlerCov10) ProcessIncoming(ctx context.Context, inbox *model.Inbox, rawPayload []byte) (*channelpkg.IncomingMessage, error) {
|
|
return &channelpkg.IncomingMessage{Content: "test"}, nil
|
|
}
|
|
|
|
type mockEmailOutgoingHandlerCov10 struct{}
|
|
|
|
func (m *mockEmailOutgoingHandlerCov10) SendMessage(ctx context.Context, inbox *model.Inbox, message *model.Message, contact *model.Contact) (*channelpkg.SendResult, error) {
|
|
return &channelpkg.SendResult{ExternalID: "test_msg_id"}, nil
|
|
}
|