feat: harden SMTP transport

Add explicit TLS modes, context-aware delivery, and typed transport errors.
Preserve raw MIME messages and cover the new delivery paths with local SMTP tests.
This commit is contained in:
2026-08-18 18:16:22 -06:00
parent 537fbeebd9
commit b3c22c261e
12 changed files with 1560 additions and 652 deletions
+14 -49
View File
@@ -1,61 +1,26 @@
package email
import (
"crypto/tls"
"io"
"net/smtp"
"context"
)
type MockCallbackFn func(params ...interface{})
// NewMockMailService is retained for compatibility. It never creates a network
// connection and contains no insecure TLS configuration.
func NewMockMailService(callbackFn MockCallbackFn, params ...interface{}) *EmailService {
return &EmailService{
auth: smtp.PlainAuth("", "", "", ""),
host: "",
port: "",
from: "",
tlsconfig: &tls.Config{
InsecureSkipVerify: true,
ServerName: "",
},
dial: func(hostPort string) (SMTPClientIface, error) {
callbackFn(params)
return &mockSMTP{}, nil
from: "mock@example.invalid",
connectTimeout: DefaultConnectTimeout,
operationTimeout: DefaultOperationTimeout,
sendRawHook: func(ctx context.Context, _ RawMessage) error {
if err := ctx.Err(); err != nil {
return err
}
if callbackFn != nil {
callbackFn(params...)
}
return nil
},
}
}
type mockWriter struct{}
func (w *mockWriter) Close() error {
return nil
}
func (w *mockWriter) Write(p []byte) (n int, err error) {
return 10, nil
}
// Mock SMTP Client
type mockSMTP struct{}
func (m *mockSMTP) StartTLS(*tls.Config) error {
return nil
}
func (m *mockSMTP) Auth(a smtp.Auth) error {
return nil
}
func (m *mockSMTP) Close() error {
return nil
}
func (m *mockSMTP) Data() (io.WriteCloser, error) {
return &mockWriter{}, nil
}
func (m *mockSMTP) Mail(from string) error {
return nil
}
func (m *mockSMTP) Quit() error {
return nil
}
func (m *mockSMTP) Rcpt(to string) error {
return nil
}