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) } }) } }