package mailx_test
import (
"fmt"
"strings"
"testing"
mailx "code.yun.ink/pkg/mailx"
)
// manyValidAddresses 生成 n 个合法地址
func manyValidAddresses(n int) []string {
out := make([]string, n)
for i := range out {
out[i] = fmt.Sprintf("user%d@example.com", i)
}
return out
}
// manyHeaders 生成 n 个自定义头
func manyHeaders(n int) map[string]string {
out := make(map[string]string, n)
for i := 0; i < n; i++ {
out[fmt.Sprintf("X-Test-%d", i)] = "v"
}
return out
}
func TestMessageBuilder(t *testing.T) {
msg := mailx.NewMessage().
From("sender@example.com").
To("a@example.com", "b@example.com").
Cc("cc@example.com").
Bcc("bcc@example.com").
Subject("subject").
Body("body").
ReplyTo("reply@example.com").
Attach("dir/file.txt").
AttachBytes("data.txt", []byte("hello")).
Build()
if msg.From != "sender@example.com" {
t.Errorf("From = %q", msg.From)
}
if len(msg.To) != 2 || msg.To[0] != "a@example.com" || msg.To[1] != "b@example.com" {
t.Errorf("To = %v", msg.To)
}
if len(msg.Cc) != 1 || msg.Cc[0] != "cc@example.com" {
t.Errorf("Cc = %v", msg.Cc)
}
if len(msg.Bcc) != 1 || msg.Bcc[0] != "bcc@example.com" {
t.Errorf("Bcc = %v", msg.Bcc)
}
if msg.Subject != "subject" || msg.Body != "body" || msg.ReplyTo != "reply@example.com" {
t.Errorf("fields mismatch: %+v", msg)
}
if len(msg.Attachments) != 2 {
t.Fatalf("Attachments len = %d, want 2", len(msg.Attachments))
}
if msg.Attachments[0].Name != "file.txt" || msg.Attachments[0].Path != "dir/file.txt" {
t.Errorf("attachment[0] = %+v", msg.Attachments[0])
}
if msg.Attachments[1].Name != "data.txt" || string(msg.Attachments[1].Data) != "hello" {
t.Errorf("attachment[1] = %+v", msg.Attachments[1])
}
}
func TestMessageBuilderHTML(t *testing.T) {
msg := mailx.NewMessage().HTML("
hi
").Build()
if msg.Body != "hi
" {
t.Errorf("HTML() did not set Body, got %q", msg.Body)
}
}
func TestMessageBuilderAppendTo(t *testing.T) {
b := mailx.NewMessage().To("a@example.com")
b.To("b@example.com") // 追加而非覆盖
msg := b.Build()
if len(msg.To) != 2 {
t.Fatalf("To = %v, want 2 recipients", msg.To)
}
}
func TestMessageValidate(t *testing.T) {
cases := []struct {
name string
msg *mailx.Message
wantErr string
}{
{"valid", &mailx.Message{To: []string{"a@b.com"}, Subject: "s"}, ""},
{"no recipients", &mailx.Message{Subject: "s"}, "recipient"},
{"no subject", &mailx.Message{To: []string{"a@b.com"}}, "subject"},
{"empty", &mailx.Message{}, "recipient"},
{"too many recipients", &mailx.Message{To: manyValidAddresses(mailx.MaxRecipients + 1), Subject: "s"}, "too many recipients"},
{"invalid recipient", &mailx.Message{To: []string{"not-an-email"}, Subject: "s"}, "invalid recipient"},
{"too many attachments", &mailx.Message{To: []string{"a@b.com"}, Subject: "s", Attachments: make([]mailx.Attachment, mailx.MaxAttachments+1)}, "too many attachments"},
{"too many headers", &mailx.Message{To: []string{"a@b.com"}, Subject: "s", Headers: manyHeaders(mailx.MaxHeaderCount + 1)}, "too many custom headers"},
{"message too large", &mailx.Message{To: []string{"a@b.com"}, Subject: "s", Body: strings.Repeat("x", mailx.MaxMessageSize+1)}, "message too large"},
{"inline without cid", &mailx.Message{To: []string{"a@b.com"}, Subject: "s", Inline: []mailx.InlineImage{{Data: []byte{1}}}}, "requires a CID"},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
err := tc.msg.Validate()
if tc.wantErr == "" {
if err != nil {
t.Fatalf("Validate() = %v, want nil", err)
}
return
}
if err == nil || !strings.Contains(err.Error(), tc.wantErr) {
t.Fatalf("Validate() = %v, want containing %q", err, tc.wantErr)
}
})
}
}