367 lines
8.9 KiB
Go
367 lines
8.9 KiB
Go
package smtp
|
||
|
||
import (
|
||
"context"
|
||
"strings"
|
||
"testing"
|
||
|
||
"code.yun.ink/pkg/mailx/interfaces"
|
||
)
|
||
|
||
// 测试内容类型检测
|
||
func TestContentTypeDetection(t *testing.T) {
|
||
smtpClient := NewSmtp()
|
||
|
||
tests := []struct {
|
||
name string
|
||
body string
|
||
expected interfaces.ContentType
|
||
}{
|
||
{
|
||
name: "HTML with html tag",
|
||
body: "<html><body>Test</body></html>",
|
||
expected: interfaces.ContentTypeHTML,
|
||
},
|
||
{
|
||
name: "HTML with div tag",
|
||
body: "<div>Test content</div>",
|
||
expected: interfaces.ContentTypeHTML,
|
||
},
|
||
{
|
||
name: "HTML with img tag",
|
||
body: "Check this image: <img src='test.jpg'>",
|
||
expected: interfaces.ContentTypeHTML,
|
||
},
|
||
{
|
||
name: "Plain text",
|
||
body: "This is plain text without any HTML tags.",
|
||
expected: interfaces.ContentTypeText,
|
||
},
|
||
{
|
||
name: "Text with angle brackets",
|
||
body: "Price: 100 < 200, Quality: A > B",
|
||
expected: interfaces.ContentTypeText,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
result := smtpClient.detectContentType(tt.body)
|
||
if result != tt.expected {
|
||
t.Errorf("Expected %s, got %s for body: %s", tt.expected, result, tt.body)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 测试邮件头构建
|
||
func TestBuildHeaders(t *testing.T) {
|
||
smtpClient := NewSmtp()
|
||
smtpClient.SetOption(context.Background(), interfaces.SetSmtp(&interfaces.EmailConfigDataSmtp{}))
|
||
boundary := "test-boundary"
|
||
|
||
tests := []struct {
|
||
name string
|
||
message interfaces.Message
|
||
expected string
|
||
}{
|
||
{
|
||
name: "HTML with inline images",
|
||
message: interfaces.Message{
|
||
Subject: "Test Subject",
|
||
InlineImage: []interfaces.Attachment{{Name: "test.jpg"}},
|
||
},
|
||
expected: "multipart/related",
|
||
},
|
||
{
|
||
name: "HTML with attachments",
|
||
message: interfaces.Message{
|
||
Subject: "Test Subject",
|
||
Attachment: []interfaces.Attachment{{Name: "test.pdf"}},
|
||
},
|
||
expected: "multipart/mixed",
|
||
},
|
||
{
|
||
name: "Simple message",
|
||
message: interfaces.Message{
|
||
Subject: "Test Subject",
|
||
},
|
||
expected: "multipart/alternative",
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
headers := smtpClient.buildHeaders(tt.message, boundary)
|
||
contentType := headers["Content-Type"]
|
||
if !strings.Contains(contentType, tt.expected) {
|
||
t.Errorf("Expected Content-Type to contain %s, got %s", tt.expected, contentType)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 测试附件类型处理
|
||
func TestAttachmentTypes(t *testing.T) {
|
||
tests := []struct {
|
||
name string
|
||
attachment interfaces.Attachment
|
||
expectCID bool
|
||
}{
|
||
{
|
||
name: "File attachment",
|
||
attachment: interfaces.Attachment{
|
||
Name: "document.pdf",
|
||
Type: interfaces.AttachmentTypeFile,
|
||
},
|
||
expectCID: false,
|
||
},
|
||
{
|
||
name: "Inline image",
|
||
attachment: interfaces.Attachment{
|
||
Name: "image.jpg",
|
||
Type: interfaces.AttachmentTypeInline,
|
||
CID: "test-image",
|
||
},
|
||
expectCID: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
if tt.expectCID && tt.attachment.CID == "" {
|
||
t.Error("Expected CID for inline attachment")
|
||
}
|
||
if !tt.expectCID && tt.attachment.Type == interfaces.AttachmentTypeFile {
|
||
// File attachments should not have CID
|
||
if tt.attachment.CID != "" {
|
||
t.Error("File attachment should not have CID")
|
||
}
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 测试消息验证
|
||
func TestMessageValidation2(t *testing.T) {
|
||
smtpClient := NewSmtp()
|
||
ctx := context.Background()
|
||
|
||
// 配置SMTP客户端
|
||
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
|
||
opt.Smtp = &interfaces.EmailConfigDataSmtp{
|
||
Host: "smtp.example.com",
|
||
Port: "587",
|
||
Username: "test@example.com",
|
||
Password: "password",
|
||
}
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("Failed to configure SMTP: %v", err)
|
||
}
|
||
|
||
tests := []struct {
|
||
name string
|
||
message interfaces.Message
|
||
wantErr bool
|
||
}{
|
||
{
|
||
name: "Valid HTML message",
|
||
message: interfaces.Message{
|
||
To: []string{"test@example.com"},
|
||
Subject: "Test",
|
||
Body: "<h1>Test</h1>",
|
||
BodyType: interfaces.ContentTypeHTML,
|
||
},
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "Valid text message",
|
||
message: interfaces.Message{
|
||
To: []string{"test@example.com"},
|
||
Subject: "Test",
|
||
Body: "Plain text",
|
||
BodyType: interfaces.ContentTypeText,
|
||
},
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "Message with inline images",
|
||
message: interfaces.Message{
|
||
To: []string{"test@example.com"},
|
||
Subject: "Test",
|
||
Body: "<img src='cid:test'>",
|
||
InlineImage: []interfaces.Attachment{
|
||
{
|
||
Name: "test.jpg",
|
||
CID: "test",
|
||
Type: interfaces.AttachmentTypeInline,
|
||
},
|
||
},
|
||
},
|
||
wantErr: false,
|
||
},
|
||
{
|
||
name: "Empty recipients",
|
||
message: interfaces.Message{
|
||
Subject: "Test",
|
||
Body: "Test body",
|
||
},
|
||
wantErr: true,
|
||
},
|
||
}
|
||
|
||
for _, tt := range tests {
|
||
t.Run(tt.name, func(t *testing.T) {
|
||
err := smtpClient.validateMessage(tt.message)
|
||
if (err != nil) != tt.wantErr {
|
||
t.Errorf("validateMessage() error = %v, wantErr %v", err, tt.wantErr)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
|
||
// 测试多部分邮件构建
|
||
func TestMultipartEmailConstruction2(t *testing.T) {
|
||
smtpClient := NewSmtp()
|
||
ctx := context.Background()
|
||
|
||
_, err := smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
|
||
opt.Smtp = &interfaces.EmailConfigDataSmtp{
|
||
Host: "smtp.example.com",
|
||
Port: "587",
|
||
Username: "test@example.com",
|
||
Password: "password",
|
||
}
|
||
})
|
||
if err != nil {
|
||
t.Fatalf("Failed to configure SMTP: %v", err)
|
||
}
|
||
|
||
message := interfaces.Message{
|
||
To: []string{"recipient@example.com"},
|
||
Subject: "Multipart Test",
|
||
BodyType: interfaces.ContentTypeHTML,
|
||
Body: "<h1>HTML Content</h1><img src='cid:test-img'>",
|
||
TextBody: "Plain text version",
|
||
InlineImage: []interfaces.Attachment{
|
||
{
|
||
Data: []byte("fake-image-data"),
|
||
ContentType: "image/jpeg",
|
||
Name: "test.jpg",
|
||
CID: "test-img",
|
||
Type: interfaces.AttachmentTypeInline,
|
||
},
|
||
},
|
||
Attachment: []interfaces.Attachment{
|
||
{
|
||
Data: []byte("fake-pdf-data"),
|
||
ContentType: "application/pdf",
|
||
Name: "document.pdf",
|
||
Type: interfaces.AttachmentTypeFile,
|
||
},
|
||
},
|
||
}
|
||
|
||
// 测试邮件构建(不实际发送)
|
||
boundary := "test-boundary"
|
||
headers := smtpClient.buildHeaders(message, boundary)
|
||
|
||
// 验证Content-Type
|
||
contentType := headers["Content-Type"]
|
||
if !strings.Contains(contentType, "multipart/related") {
|
||
t.Errorf("Expected multipart/related for message with inline images, got %s", contentType)
|
||
}
|
||
|
||
// 验证必要的头部字段
|
||
requiredHeaders := []string{"From", "To", "Subject", "Date", "MIME-Version", "Content-Type"}
|
||
for _, header := range requiredHeaders {
|
||
if _, exists := headers[header]; !exists {
|
||
t.Errorf("Missing required header: %s", header)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 测试字节数据附件
|
||
func TestByteDataAttachment(t *testing.T) {
|
||
attachment := interfaces.Attachment{
|
||
Data: []byte("test file content"),
|
||
ContentType: "text/plain",
|
||
Name: "test.txt",
|
||
Type: interfaces.AttachmentTypeFile,
|
||
}
|
||
|
||
if len(attachment.Data) == 0 {
|
||
t.Error("Attachment data should not be empty")
|
||
}
|
||
|
||
if attachment.ContentType != "text/plain" {
|
||
t.Errorf("Expected content type text/plain, got %s", attachment.ContentType)
|
||
}
|
||
}
|
||
|
||
// 基准测试:HTML邮件构建性能
|
||
func BenchmarkHTMLEmailConstruction(b *testing.B) {
|
||
smtpClient := NewSmtp()
|
||
ctx := context.Background()
|
||
|
||
smtpClient.SetOption(ctx, func(opt *interfaces.Options) {
|
||
opt.Smtp = &interfaces.EmailConfigDataSmtp{
|
||
Host: "smtp.example.com",
|
||
Port: "587",
|
||
Username: "test@example.com",
|
||
Password: "password",
|
||
}
|
||
})
|
||
|
||
message := interfaces.Message{
|
||
To: []string{"test@example.com"},
|
||
Subject: "Benchmark Test",
|
||
BodyType: interfaces.ContentTypeHTML,
|
||
Body: "<h1>Benchmark</h1><p>Performance test content</p>",
|
||
TextBody: "Benchmark - Performance test content",
|
||
InlineImage: []interfaces.Attachment{
|
||
{
|
||
Data: make([]byte, 1024), // 1KB fake image
|
||
ContentType: "image/jpeg",
|
||
Name: "test.jpg",
|
||
CID: "test-img",
|
||
Type: interfaces.AttachmentTypeInline,
|
||
},
|
||
},
|
||
}
|
||
|
||
b.ResetTimer()
|
||
for i := 0; i < b.N; i++ {
|
||
boundary := "benchmark-boundary"
|
||
headers := smtpClient.buildHeaders(message, boundary)
|
||
_ = headers
|
||
}
|
||
}
|
||
|
||
// 测试大附件处理
|
||
func TestLargeAttachmentHandling(t *testing.T) {
|
||
// 创建大附件数据(模拟)
|
||
largeData := make([]byte, 1024*1024) // 1MB
|
||
for i := range largeData {
|
||
largeData[i] = byte(i % 256)
|
||
}
|
||
|
||
attachment := interfaces.Attachment{
|
||
Data: largeData,
|
||
ContentType: "application/octet-stream",
|
||
Name: "large_file.bin",
|
||
Type: interfaces.AttachmentTypeFile,
|
||
}
|
||
|
||
if len(attachment.Data) != 1024*1024 {
|
||
t.Errorf("Expected 1MB data, got %d bytes", len(attachment.Data))
|
||
}
|
||
|
||
// 测试是否超过限制(25MB)
|
||
maxSize := 25 * 1024 * 1024
|
||
if len(attachment.Data) > maxSize {
|
||
t.Errorf("Attachment size %d exceeds limit %d", len(attachment.Data), maxSize)
|
||
}
|
||
}
|