| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117 |
- //go:build unit
- package service
- import (
- "bytes"
- "io"
- "mime"
- "mime/quotedprintable"
- "net/mail"
- "regexp"
- "strings"
- "testing"
- "github.com/stretchr/testify/require"
- )
- func TestBuildSMTPMessageProducesStandardsCompliantMIME(t *testing.T) {
- config := &SMTPConfig{
- Host: "smtp.example.com",
- From: "reply@example.com",
- FromName: "Sub2API 通知",
- }
- body := "<html>\n<body>验证码:123456 & ready</body>\n</html>"
- message, err := buildSMTPMessage(config, "User <user@example.net>", "邮箱验证码", body)
- require.NoError(t, err)
- require.Equal(t, "reply@example.com", message.envelopeFrom)
- require.Equal(t, "user@example.net", message.envelopeTo)
- parsed, err := mail.ReadMessage(bytes.NewReader(message.data))
- require.NoError(t, err)
- from, err := mail.ParseAddress(parsed.Header.Get("From"))
- require.NoError(t, err)
- require.Equal(t, "Sub2API 通知", from.Name)
- require.Equal(t, "reply@example.com", from.Address)
- recipient, err := mail.ParseAddress(parsed.Header.Get("To"))
- require.NoError(t, err)
- require.Equal(t, "User", recipient.Name)
- require.Equal(t, "user@example.net", recipient.Address)
- decodedSubject, err := new(mime.WordDecoder).DecodeHeader(parsed.Header.Get("Subject"))
- require.NoError(t, err)
- require.Equal(t, "邮箱验证码", decodedSubject)
- require.NotEmpty(t, parsed.Header.Get("Date"))
- _, err = mail.ParseDate(parsed.Header.Get("Date"))
- require.NoError(t, err)
- require.Regexp(t, regexp.MustCompile(`^<[0-9a-f]{32}@example\.com>$`), parsed.Header.Get("Message-ID"))
- require.Equal(t, "1.0", parsed.Header.Get("MIME-Version"))
- require.Equal(t, "quoted-printable", parsed.Header.Get("Content-Transfer-Encoding"))
- mediaType, params, err := mime.ParseMediaType(parsed.Header.Get("Content-Type"))
- require.NoError(t, err)
- require.Equal(t, "text/html", mediaType)
- require.Equal(t, "UTF-8", params["charset"])
- decodedBody, err := io.ReadAll(quotedprintable.NewReader(parsed.Body))
- require.NoError(t, err)
- require.Equal(t, strings.ReplaceAll(body, "\n", "\r\n"), string(decodedBody))
- }
- func TestBuildSMTPMessagePreventsHeaderInjection(t *testing.T) {
- config := &SMTPConfig{
- Host: "smtp.example.com",
- From: "reply@example.com",
- FromName: "Sender\r\nBcc: hidden@example.com",
- }
- message, err := buildSMTPMessage(config, "user@example.net", "Subject\r\nCc: hidden@example.com", "body")
- require.NoError(t, err)
- parsed, err := mail.ReadMessage(bytes.NewReader(message.data))
- require.NoError(t, err)
- require.Empty(t, parsed.Header.Get("Bcc"))
- require.Empty(t, parsed.Header.Get("Cc"))
- decodedSubject, err := new(mime.WordDecoder).DecodeHeader(parsed.Header.Get("Subject"))
- require.NoError(t, err)
- require.Equal(t, "SubjectCc: hidden@example.com", decodedSubject)
- }
- func TestBuildSMTPMessageRejectsInvalidConfiguration(t *testing.T) {
- _, err := buildSMTPMessage(nil, "user@example.net", "subject", "body")
- require.ErrorContains(t, err, "missing SMTP configuration")
- _, err = buildSMTPMessage(&SMTPConfig{Host: "smtp.example.com"}, "user@example.net", "subject", "body")
- require.ErrorContains(t, err, "invalid SMTP from address")
- _, err = buildSMTPMessage(&SMTPConfig{
- Host: "smtp.example.com",
- From: "reply@example.com",
- }, "invalid recipient <>", "subject", "body")
- require.ErrorContains(t, err, "invalid SMTP recipient address")
- _, err = buildSMTPMessage(&SMTPConfig{
- Host: "smtp.example.com",
- From: "reply@example.com",
- }, "user@example.net\r\nBcc: hidden@example.net", "subject", "body")
- require.ErrorContains(t, err, "invalid SMTP recipient address")
- }
- func TestBuildSMTPMessageUsesUniqueMessageIDs(t *testing.T) {
- config := &SMTPConfig{Host: "smtp.example.com", From: "reply@example.com"}
- first, err := buildSMTPMessage(config, "user@example.net", "subject", "body")
- require.NoError(t, err)
- second, err := buildSMTPMessage(config, "user@example.net", "subject", "body")
- require.NoError(t, err)
- firstParsed, err := mail.ReadMessage(bytes.NewReader(first.data))
- require.NoError(t, err)
- secondParsed, err := mail.ReadMessage(bytes.NewReader(second.data))
- require.NoError(t, err)
- require.NotEqual(t, firstParsed.Header.Get("Message-ID"), secondParsed.Header.Get("Message-ID"))
- }
|