更新
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
package mailgun
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
mailx "code.yun.ink/pkg/mailx"
|
||||
"github.com/mailgun/mailgun-go/v4"
|
||||
)
|
||||
|
||||
// mockMG 实现 mgClient 接口,记录 NewMessage 的参数与 Send 调用
|
||||
type mockMG struct {
|
||||
sendErr error
|
||||
|
||||
// NewMessage 捕获
|
||||
from, subject, text string
|
||||
to []string
|
||||
|
||||
// Send 捕获
|
||||
msgSent bool
|
||||
sentMsg *mailgun.Message
|
||||
}
|
||||
|
||||
func (m *mockMG) NewMessage(from, subject, text string, to ...string) *mailgun.Message {
|
||||
m.from, m.subject, m.text = from, subject, text
|
||||
m.to = to
|
||||
return mailgun.NewMessage(from, subject, text, to...)
|
||||
}
|
||||
|
||||
func (m *mockMG) Send(_ context.Context, msg *mailgun.Message) (string, string, error) {
|
||||
m.msgSent = true
|
||||
m.sentMsg = msg
|
||||
if m.sendErr != nil {
|
||||
return "", "", m.sendErr
|
||||
}
|
||||
return "mock-id", "queued", nil
|
||||
}
|
||||
|
||||
// TestSendSuccess 验证完整发送流程与消息构造参数
|
||||
func TestSendSuccess(t *testing.T) {
|
||||
mock := &mockMG{}
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com", Sender: "noreply@example.com"}, client: mock}
|
||||
|
||||
msg := mailx.NewMessage().
|
||||
To("a@example.com", "b@example.com").
|
||||
Cc("cc@example.com").
|
||||
Bcc("bcc@example.com").
|
||||
Subject("hi").
|
||||
Text("plain").
|
||||
HTML("<b>html</b>").
|
||||
ReplyTo("reply@example.com").
|
||||
AttachBytes("a.txt", []byte("data")).
|
||||
InlineImageBytes("cid1", "a.png", []byte{1}).
|
||||
Build()
|
||||
|
||||
if err := g.Send(context.Background(), msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !mock.msgSent {
|
||||
t.Fatal("message not sent")
|
||||
}
|
||||
if mock.from != "noreply@example.com" {
|
||||
t.Errorf("From = %q", mock.from)
|
||||
}
|
||||
if mock.subject != "hi" {
|
||||
t.Errorf("Subject = %q", mock.subject)
|
||||
}
|
||||
if mock.text != "plain" {
|
||||
t.Errorf("Text = %q", mock.text)
|
||||
}
|
||||
if len(mock.to) != 2 || mock.to[0] != "a@example.com" || mock.to[1] != "b@example.com" {
|
||||
t.Errorf("To = %v", mock.to)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendFromFallback 验证 msg.From 为空时回退 cfg.Sender
|
||||
func TestSendFromFallback(t *testing.T) {
|
||||
mock := &mockMG{}
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com", Sender: "cfg@example.com"}, client: mock}
|
||||
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").Build()
|
||||
if err := g.Send(context.Background(), msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mock.from != "cfg@example.com" {
|
||||
t.Errorf("From = %q, want cfg fallback", mock.from)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendTextFallback 验证 Body 为纯文本时作为 text 传递
|
||||
func TestSendTextFallback(t *testing.T) {
|
||||
mock := &mockMG{}
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com", Sender: "s@example.com"}, client: mock}
|
||||
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").Body("plain body no html").Build()
|
||||
if err := g.Send(context.Background(), msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mock.text != "plain body no html" {
|
||||
t.Errorf("Text = %q, want fallback to Body", mock.text)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendHTMLOnly 验证 Body 为 HTML 时不作为 text 传递
|
||||
func TestSendHTMLOnly(t *testing.T) {
|
||||
mock := &mockMG{}
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com", Sender: "s@example.com"}, client: mock}
|
||||
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").HTML("<b>html</b>").Build()
|
||||
if err := g.Send(context.Background(), msg); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if mock.text != "" {
|
||||
t.Errorf("Text = %q, want empty for html body", mock.text)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendFail 验证发送失败包装为 ErrSendFailed
|
||||
func TestSendFail(t *testing.T) {
|
||||
mock := &mockMG{sendErr: errors.New("mailgun down")}
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com", Sender: "s@example.com"}, client: mock}
|
||||
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").Build()
|
||||
err := g.Send(context.Background(), msg)
|
||||
if err == nil || !errors.Is(err, mailx.ErrSendFailed) {
|
||||
t.Fatalf("err = %v, want ErrSendFailed", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendNoSender 验证缺少 Sender 报错
|
||||
func TestSendNoSender(t *testing.T) {
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com"}, client: &mockMG{}}
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").Build()
|
||||
err := g.Send(context.Background(), msg)
|
||||
if err == nil || !errors.Is(err, mailx.ErrInvalidConfig) {
|
||||
t.Fatalf("err = %v, want ErrInvalidConfig", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendNoDomain 验证缺少 Domain/APIKey 报错
|
||||
func TestSendNoDomain(t *testing.T) {
|
||||
g := &MailGun{cfg: Config{Sender: "s@example.com"}, client: &mockMG{}}
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").Build()
|
||||
err := g.Send(context.Background(), msg)
|
||||
if err == nil || !errors.Is(err, mailx.ErrInvalidConfig) {
|
||||
t.Fatalf("err = %v, want ErrInvalidConfig", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSendAttachmentErrors 验证附件边界情况
|
||||
func TestSendAttachmentErrors(t *testing.T) {
|
||||
g := &MailGun{cfg: Config{APIKey: "key", Domain: "mg.example.com", Sender: "s@example.com"}, client: &mockMG{}}
|
||||
|
||||
// 附件既无 path 也无 data
|
||||
msg := mailx.NewMessage().To("a@example.com").Subject("s").
|
||||
AttachBytes("x.txt", []byte{1}).
|
||||
Build()
|
||||
msg.Attachments[0].Data = nil // 清空 data
|
||||
if err := g.Send(context.Background(), msg); err == nil {
|
||||
t.Fatal("attachment without path/data should error")
|
||||
}
|
||||
|
||||
// 附件路径不存在
|
||||
msg = mailx.NewMessage().To("a@example.com").Subject("s").
|
||||
Attach("no-such-file.txt").
|
||||
Build()
|
||||
if err := g.Send(context.Background(), msg); err == nil {
|
||||
t.Fatal("missing attachment file should error")
|
||||
}
|
||||
|
||||
// 内嵌图片路径不存在
|
||||
msg = mailx.NewMessage().To("a@example.com").Subject("s").
|
||||
InlineImage("cid1", "no-such-img.png").
|
||||
Build()
|
||||
if err := g.Send(context.Background(), msg); err == nil {
|
||||
t.Fatal("missing inline file should error")
|
||||
}
|
||||
}
|
||||
|
||||
// TestGetClient 验证 getClient 惰性复用
|
||||
func TestGetClient(t *testing.T) {
|
||||
g := New(Config{APIKey: "key", Domain: "mg.example.com"})
|
||||
c1 := g.getClient()
|
||||
c2 := g.getClient()
|
||||
if c1 != c2 {
|
||||
t.Error("client should be reused")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user