301 lines
10 KiB
Go
301 lines
10 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"
|
|
)
|
|
|
|
func TestNewWebhookHandler_Cov4(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
assert.NotNil(t, h)
|
|
}
|
|
|
|
func TestWebhookHandler_HandleWebhookRequest_EmptyBody_Cov4(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 TestWebhookHandler_HandleWebhookRequest_ReadError_Cov4(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest("POST", "/webhook", &errReaderCov4{})
|
|
h.HandleWebhookRequest(w, r)
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
}
|
|
|
|
func TestWebhookHandler_HandleWebhookRequest_ValidMailgun_Cov4(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 TestWebhookHandler_HandleWebhookRequest_ValidSendGrid_Cov4(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := `{"envelope":{"from":"sender@example.com","to":["recipient@example.com"]},"subject":"Test","text":"Hello","html":"<p>Hello</p>","messageId":"msg-2"}`
|
|
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 TestWebhookHandler_HandleWebhookRequest_GenericJSON_Cov4(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
w := httptest.NewRecorder()
|
|
body := `{"from":"sender@example.com","subject":"Test","text":"Hello","to":"recipient@example.com","message_id":"msg-3"}`
|
|
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 TestWebhookHandler_HandleWebhookRequest_RawEmail_Cov4(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 TestWebhookHandler_HandleWebhookRequest_InvalidMailgun_Cov4(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 TestWebhookHandler_ParseWebhookBody_Cov4(t *testing.T) {
|
|
h := NewWebhookHandler()
|
|
|
|
t.Run("mailgun", func(t *testing.T) {
|
|
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)
|
|
})
|
|
|
|
t.Run("sendgrid", func(t *testing.T) {
|
|
body := `{"envelope":{"from":"sender@example.com","to":["recipient@example.com"]},"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, "sender@example.com", msg.FromAddress)
|
|
assert.Equal(t, "Test", msg.Subject)
|
|
})
|
|
|
|
t.Run("generic json", func(t *testing.T) {
|
|
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)
|
|
})
|
|
|
|
t.Run("raw email", func(t *testing.T) {
|
|
body := "From: sender@example.com\r\nSubject: Test\r\n\r\nHello"
|
|
msg, err := h.ParseWebhookBody([]byte(body), http.Header{})
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, msg.TextContent)
|
|
})
|
|
}
|
|
|
|
func TestIsMailgunPayload_Cov4(t *testing.T) {
|
|
headers := http.Header{}
|
|
assert.False(t, isMailgunPayload(headers))
|
|
headers.Set("X-Mailgun-Sid", "12345")
|
|
assert.True(t, isMailgunPayload(headers))
|
|
}
|
|
|
|
func TestIsSendGridPayload_Cov4(t *testing.T) {
|
|
headers := http.Header{}
|
|
assert.False(t, isSendGridPayload(headers))
|
|
headers.Set("X-Sg-Envelope-Id", "env-123")
|
|
assert.True(t, isSendGridPayload(headers))
|
|
}
|
|
|
|
func TestParseMailgunPayload_Cov4(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
body := `{"from":"sender@example.com","subject":"Test","body-plain":"Hello","to":"recipient@example.com","Message-Id":"msg-1","In-Reply-To":"parent","References":"<parent>","Date":"2024-01-01","body-html":"<p>Hello</p>"}`
|
|
msg, err := parseMailgunPayload([]byte(body))
|
|
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, "msg-1", msg.MessageID)
|
|
})
|
|
|
|
t.Run("invalid json", func(t *testing.T) {
|
|
_, err := parseMailgunPayload([]byte("invalid"))
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestParseSendGridPayload_Cov4(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
body := `{"envelope":{"from":"sender@example.com","to":["recipient@example.com"]},"subject":"Test","text":"Hello","html":"<p>Hello</p>","messageId":"msg-2","in_reply_to":"parent","date":"2024-01-01"}`
|
|
msg, err := parseSendGridPayload([]byte(body))
|
|
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, "msg-2", msg.MessageID)
|
|
assert.Equal(t, "parent", msg.InReplyTo)
|
|
})
|
|
|
|
t.Run("invalid json", func(t *testing.T) {
|
|
_, err := parseSendGridPayload([]byte("invalid"))
|
|
assert.Error(t, err)
|
|
})
|
|
}
|
|
|
|
func TestParseGenericJSONPayload_Cov4(t *testing.T) {
|
|
t.Run("valid", func(t *testing.T) {
|
|
data := map[string]interface{}{
|
|
"from": "sender@example.com",
|
|
"subject": "Test",
|
|
"text": "Hello",
|
|
"html": "<p>Hello</p>",
|
|
"message_id": "msg-3",
|
|
"in_reply_to": "parent",
|
|
"date": "2024-01-01",
|
|
"to": "recipient@example.com",
|
|
}
|
|
msg, err := parseGenericJSONPayload(data)
|
|
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, "msg-3", msg.MessageID)
|
|
assert.Equal(t, "parent", msg.InReplyTo)
|
|
assert.Equal(t, []string{"recipient@example.com"}, msg.ToAddresses)
|
|
})
|
|
}
|
|
|
|
func TestParseRawEmail_Cov4(t *testing.T) {
|
|
body := "From: sender@example.com\r\nSubject: Test\r\n\r\nHello world"
|
|
msg, err := parseRawEmail([]byte(body))
|
|
require.NoError(t, err)
|
|
assert.NotEmpty(t, msg.TextContent)
|
|
assert.Contains(t, msg.TextContent, "Hello world")
|
|
}
|
|
|
|
func TestEmailProvider_SendMessage_WithSMTPConfig_Cov4(t *testing.T) {
|
|
provider := NewEmailProvider(NewService(), &Repository{}, NewIncomingProcessor(nil))
|
|
ctx := context.Background()
|
|
inbox := &model.Inbox{
|
|
Base: model.Base{ID: 1},
|
|
AccountID: 1,
|
|
ChannelConfig: `{"smtp_email":"support@example.com","smtp_address":"smtp.example.com","smtp_port":587,"smtp_password":"pass","imap_address":"imap.example.com","imap_port":993,"imap_email":"support@example.com","imap_password":"pass"}`,
|
|
}
|
|
contact := &model.Contact{SourceID: "recipient@example.com"}
|
|
msg := &model.Message{Content: "test"}
|
|
|
|
// This will try to connect to smtp.example.com — should fail with network error
|
|
_, err := provider.SendMessage(ctx, inbox, msg, contact)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestEmailProvider_PollInterval_Cov4(t *testing.T) {
|
|
provider := NewEmailProvider(NewService(), &Repository{}, NewIncomingProcessor(nil))
|
|
interval := provider.PollInterval()
|
|
assert.NotZero(t, interval)
|
|
}
|
|
|
|
func TestEmailProvider_PollMessages_Cov4(t *testing.T) {
|
|
provider := NewEmailProvider(NewService(), &Repository{}, NewIncomingProcessor(nil))
|
|
inbox := &model.Inbox{Base: model.Base{ID: 1}, AccountID: 1}
|
|
// With empty config, should fail
|
|
_, err := provider.PollMessages(context.Background(), inbox)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestEmailMessage_JSON_Cov4(t *testing.T) {
|
|
msg := EmailMessage{
|
|
MessageID: "msg-1",
|
|
FromAddress: "sender@example.com",
|
|
Subject: "Test",
|
|
TextContent: "Hello",
|
|
}
|
|
data, err := json.Marshal(msg)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(data), "msg-1")
|
|
|
|
var decoded EmailMessage
|
|
err = json.Unmarshal(data, &decoded)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, msg.MessageID, decoded.MessageID)
|
|
}
|
|
|
|
func TestEmailAttachment_JSON_Cov4(t *testing.T) {
|
|
att := EmailAttachment{
|
|
Filename: "doc.pdf",
|
|
ContentType: "application/pdf",
|
|
FileSize: 1024,
|
|
}
|
|
data, err := json.Marshal(att)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(data), "doc.pdf")
|
|
|
|
var decoded EmailAttachment
|
|
err = json.Unmarshal(data, &decoded)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, att.Filename, decoded.Filename)
|
|
}
|
|
|
|
// errReaderCov4 is a reader that always returns an error
|
|
type errReaderCov4 struct{}
|
|
|
|
func (e *errReaderCov4) Read(p []byte) (n int, err error) {
|
|
return 0, assertErrorCov4("read error")
|
|
}
|
|
|
|
type errStringCov4 string
|
|
|
|
func (e errStringCov4) Error() string { return string(e) }
|
|
|
|
func assertErrorCov4(msg string) error {
|
|
return errStringCov4(msg)
|
|
}
|
|
|
|
func jsonMarshal(v interface{}) ([]byte, error) {
|
|
return json.Marshal(v)
|
|
}
|
|
|
|
func TestChannelpkgConstant_Cov4(t *testing.T) {
|
|
// Ensure channelpkg import is used
|
|
assert.Equal(t, "email", string(channelpkg.ChannelEmail))
|
|
}
|