Files
mailrelay/integration_test.go
2025-05-24 23:46:23 -04:00

371 lines
9.3 KiB
Go

package main
import (
"bytes"
"testing"
"time"
"github.com/jpillora/ipfilter"
"github.com/phires/go-guerrilla/log"
"github.com/phires/go-guerrilla/mail"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// setupTestLogger initializes the logger for testing.
func setupTestLogger(t *testing.T) {
var err error
Logger, err = log.GetLogger("stdout", "info")
require.NoError(t, err)
}
func TestSendMail_Success(t *testing.T) {
setupTestLogger(t)
// Start mock SMTP server
server := NewMockSMTPServer(t)
require.NoError(t, server.Start())
defer server.Stop()
// Configure for testing
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: false,
Username: "",
Password: "",
SkipVerify: true,
HeloHost: "",
}
// Create test envelope
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient1", Host: "example.com"},
{User: "recipient2", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: Test\r\n\r\nThis is a test email."),
RemoteIP: "127.0.0.1",
}
// Set up IP filter to allow this IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
AllowedIPs: []string{"127.0.0.1"},
BlockByDefault: false,
})
// Send email
err := sendMail(envelope, config)
assert.NoError(t, err)
// Verify the mock server received the email
conn := server.GetLastConnection()
require.NotNil(t, conn)
assert.Equal(t, "sender@test.com", conn.From)
assert.Equal(t, []string{"recipient1@example.com", "recipient2@example.com"}, conn.To)
assert.Contains(t, conn.Data, "Subject: Test")
assert.Contains(t, conn.Data, "This is a test email.")
}
func TestSendMail_WithAuthentication(t *testing.T) {
setupTestLogger(t)
// Start mock SMTP server with auth requirement
server := NewMockSMTPServer(t)
server.RequireAuth = true
require.NoError(t, server.Start())
defer server.Stop()
// Configure with authentication
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: false,
Username: "testuser",
Password: "testpass",
SkipVerify: true,
HeloHost: "relay.test.com",
}
// Create test envelope
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: Auth Test\r\n\r\nAuthenticated email."),
RemoteIP: "127.0.0.1",
}
// Allow IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
BlockByDefault: false,
})
// Send email
err := sendMail(envelope, config)
assert.NoError(t, err)
// Verify authentication was used
conn := server.GetLastConnection()
require.NotNil(t, conn)
assert.NotEmpty(t, conn.AuthUser)
assert.NotEmpty(t, conn.AuthPass)
}
func TestSendMail_WithLoginAuth(t *testing.T) {
setupTestLogger(t)
// Start mock SMTP server with LOGIN auth support
server := NewMockSMTPServer(t)
server.RequireAuth = true
server.SupportLoginAuth = true
require.NoError(t, server.Start())
defer server.Stop()
// Configure with LOGIN authentication
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: true,
Username: "testuser",
Password: "testpass",
SkipVerify: true,
HeloHost: "",
}
// Create test envelope
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: LOGIN Auth Test\r\n\r\nLOGIN authenticated email."),
RemoteIP: "127.0.0.1",
}
// Allow IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
BlockByDefault: false,
})
// Send email
err := sendMail(envelope, config)
assert.NoError(t, err)
// Verify LOGIN authentication was used
conn := server.GetLastConnection()
require.NotNil(t, conn)
assert.NotEmpty(t, conn.AuthUser)
assert.NotEmpty(t, conn.AuthPass)
}
func TestSendMail_IPFiltering_Blocked(t *testing.T) {
setupTestLogger(t)
// Start mock SMTP server
server := NewMockSMTPServer(t)
require.NoError(t, server.Start())
defer server.Stop()
// Configure server
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: false,
Username: "",
Password: "",
SkipVerify: true,
HeloHost: "",
}
// Create test envelope with blocked IP
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: Test\r\n\r\nThis should be blocked."),
RemoteIP: "192.168.1.100", // This IP will be blocked
}
// Set up IP filter to block this IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
AllowedIPs: []string{"127.0.0.1"},
BlockByDefault: true,
})
// Send email - should fail due to IP filtering
err := sendMail(envelope, config)
assert.Error(t, err)
assert.Contains(t, err.Error(), "192.168.1.100")
assert.Contains(t, err.Error(), "not allowed to send email")
// Verify no email was sent to the server
conn := server.GetLastConnection()
// The connection might be nil or have no data since the IP was blocked before SMTP
if conn != nil {
assert.Empty(t, conn.From)
}
}
func TestSendMail_IPFiltering_Allowed(t *testing.T) {
setupTestLogger(t)
// Start mock SMTP server
server := NewMockSMTPServer(t)
require.NoError(t, server.Start())
defer server.Stop()
// Configure server
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: false,
Username: "",
Password: "",
SkipVerify: true,
HeloHost: "",
}
// Create test envelope with allowed IP
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: Test\r\n\r\nThis should be allowed."),
RemoteIP: "192.168.1.100",
}
// Set up IP filter to allow this specific IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
AllowedIPs: []string{"192.168.1.0/24"},
BlockByDefault: true,
})
// Send email - should succeed
err := sendMail(envelope, config)
assert.NoError(t, err)
// Verify email was sent to the server
conn := server.GetLastConnection()
require.NotNil(t, conn)
assert.Equal(t, "sender@test.com", conn.From)
assert.Equal(t, []string{"recipient@example.com"}, conn.To)
}
func TestSendMail_ServerErrors(t *testing.T) {
setupTestLogger(t)
tests := []struct {
name string
failCommand string
expectError string
}{
{
name: "MAIL command fails",
failCommand: "MAIL",
expectError: "mail error",
},
{
name: "RCPT command fails",
failCommand: "RCPT",
expectError: "rcpt error",
},
{
name: "DATA command fails",
failCommand: "DATA",
expectError: "data error",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// Start mock SMTP server
server := NewMockSMTPServer(t)
server.FailCommands[tt.failCommand] = true
require.NoError(t, server.Start())
defer server.Stop()
// Configure server
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: false,
Username: "",
Password: "",
SkipVerify: true,
HeloHost: "",
}
// Create test envelope
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: Test\r\n\r\nThis should fail."),
RemoteIP: "127.0.0.1",
}
// Allow IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
BlockByDefault: false,
})
// Send email - should fail
err := sendMail(envelope, config)
assert.Error(t, err)
assert.Contains(t, err.Error(), tt.expectError)
})
}
}
func TestSendMail_ConnectionTimeout(t *testing.T) {
setupTestLogger(t)
// Start mock SMTP server with delay
server := NewMockSMTPServer(t)
server.ResponseDelay = 100 * time.Millisecond
require.NoError(t, server.Start())
defer server.Stop()
// Configure server
config := &relayConfig{
Server: server.Address(),
Port: server.Port(),
STARTTLS: true,
LoginAuthType: false,
Username: "",
Password: "",
SkipVerify: true,
HeloHost: "",
}
// Create test envelope
envelope := &mail.Envelope{
MailFrom: mail.Address{User: "sender", Host: "test.com"},
RcptTo: []mail.Address{
{User: "recipient", Host: "example.com"},
},
Data: *bytes.NewBufferString("Subject: Timeout Test\r\n\r\nThis tests server delays."),
RemoteIP: "127.0.0.1",
}
// Allow IP
AllowedSendersFilter = ipfilter.New(ipfilter.Options{
BlockByDefault: false,
})
// Send email - should still succeed despite delay
err := sendMail(envelope, config)
assert.NoError(t, err)
// Verify email was eventually sent
conn := server.GetLastConnection()
require.NotNil(t, conn)
assert.Equal(t, "sender@test.com", conn.From)
}