smtp2shoutrrr/backend_test.go
Felipe M. ad212d6ed7
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
deps: update
2026-02-04 12:49:10 +01:00

359 lines
8.8 KiB
Go

package smtp2shoutrrr
import (
"context"
"fmt"
"io"
"net/http"
"net/http/httptest"
"net/smtp"
"testing"
"time"
"github.com/stretchr/testify/require"
)
func TestEmailForwarding(t *testing.T) {
// Start mock ntfy server
notifications := make([]string, 0)
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
// Read body and log
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
notifications = append(notifications, string(body))
w.WriteHeader(http.StatusOK)
}))
defer mockServer.Close()
// Configure the SMTP server
config := &Config{
Port: 2525,
Username: "testuser",
Password: "testpass",
Recipients: []ConfigRecipient{
{
Addresses: []string{"test@example.com"},
Target: "generic+" + mockServer.URL + "/?template=json",
},
},
}
ctx := context.Background()
smtpServer := NewSMTPServer(config)
// Start the server
go func() {
if err := smtpServer.Start(ctx); err != nil {
t.Errorf("failed to start server: %v", err)
}
}()
// Give the server time to start
time.Sleep(100 * time.Millisecond)
defer func() {
if err := smtpServer.Stop(ctx); err != nil {
t.Errorf("failed to stop server: %v", err)
}
}()
// Send test email
auth := smtp.PlainAuth("", config.Username, config.Password, "localhost")
err := smtp.SendMail(
fmt.Sprintf("localhost:%d", config.Port),
auth,
"sender@example.com",
[]string{"test@example.com"},
[]byte("Subject: Test Email\r\n\r\nThis is a test email body"),
)
require.NoError(t, err)
// Give some time for the notification to be processed
time.Sleep(100 * time.Millisecond)
// Verify the notification was received
require.Len(t, notifications, 1)
require.Contains(t, notifications[0], "This is a test email body")
}
func TestMultipleTargets(t *testing.T) {
// Track notifications received
notifications := make([]string, 0)
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
body, err := io.ReadAll(r.Body)
if err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
notifications = append(notifications, string(body))
w.WriteHeader(http.StatusOK)
}))
defer mockServer.Close()
// Configure with multiple targets
config := &Config{
Port: 2526,
Username: "testuser",
Password: "testpass",
Recipients: []ConfigRecipient{
{
Addresses: []string{"test@example.com"},
Targets: []string{
"generic+" + mockServer.URL + "/?template=json&id=1",
"generic+" + mockServer.URL + "/?template=json&id=2",
},
},
},
}
ctx := context.Background()
smtpServer := NewSMTPServer(config)
go func() {
if err := smtpServer.Start(ctx); err != nil {
t.Errorf("failed to start server: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
defer func() {
if err := smtpServer.Stop(ctx); err != nil {
t.Errorf("failed to stop server: %v", err)
}
}()
// Send test email
auth := smtp.PlainAuth("", config.Username, config.Password, "localhost")
err := smtp.SendMail(
fmt.Sprintf("localhost:%d", config.Port),
auth,
"sender@example.com",
[]string{"test@example.com"},
[]byte("Subject: Multiple Targets Test\r\n\r\nTest body for multiple targets"),
)
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
// Verify both targets received notification
require.Len(t, notifications, 2)
require.Contains(t, notifications[0], "Test body for multiple targets")
require.Contains(t, notifications[1], "Test body for multiple targets")
}
func TestTargetAndTargetsMerging(t *testing.T) {
// Track notifications and targets hit
notificationCount := 0
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
_, _ = io.ReadAll(r.Body)
notificationCount++
w.WriteHeader(http.StatusOK)
}))
defer mockServer.Close()
// Configure with both Target (deprecated) and Targets
config := &Config{
Port: 2527,
Username: "testuser",
Password: "testpass",
Recipients: []ConfigRecipient{
{
Addresses: []string{"test@example.com"},
Target: "generic+" + mockServer.URL + "/?template=json&id=legacy",
Targets: []string{
"generic+" + mockServer.URL + "/?template=json&id=new1",
"generic+" + mockServer.URL + "/?template=json&id=new2",
},
},
},
}
ctx := context.Background()
smtpServer := NewSMTPServer(config)
go func() {
if err := smtpServer.Start(ctx); err != nil {
t.Errorf("failed to start server: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
defer func() {
if err := smtpServer.Stop(ctx); err != nil {
t.Errorf("failed to stop server: %v", err)
}
}()
// Send test email
auth := smtp.PlainAuth("", config.Username, config.Password, "localhost")
err := smtp.SendMail(
fmt.Sprintf("localhost:%d", config.Port),
auth,
"sender@example.com",
[]string{"test@example.com"},
[]byte("Subject: Merging Test\r\n\r\nTest body"),
)
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
// Verify all 3 targets received notification (1 from Target + 2 from Targets)
require.Equal(t, 3, notificationCount)
}
func TestPartialTargetFailure(t *testing.T) {
// Track successful notifications
successCount := 0
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
_, _ = io.ReadAll(r.Body)
// Fail if URL path contains "fail"
if r.URL.Path == "/fail" {
w.WriteHeader(http.StatusInternalServerError)
return
}
successCount++
w.WriteHeader(http.StatusOK)
}))
defer mockServer.Close()
// Configure with mix of good and bad targets
config := &Config{
Port: 2528,
Username: "testuser",
Password: "testpass",
Recipients: []ConfigRecipient{
{
Addresses: []string{"test@example.com"},
Targets: []string{
"generic+" + mockServer.URL + "/success1?template=json",
"generic+" + mockServer.URL + "/fail?template=json",
"generic+" + mockServer.URL + "/success2?template=json",
},
},
},
}
ctx := context.Background()
smtpServer := NewSMTPServer(config)
go func() {
if err := smtpServer.Start(ctx); err != nil {
t.Errorf("failed to start server: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
defer func() {
if err := smtpServer.Stop(ctx); err != nil {
t.Errorf("failed to stop server: %v", err)
}
}()
// Send test email
auth := smtp.PlainAuth("", config.Username, config.Password, "localhost")
err := smtp.SendMail(
fmt.Sprintf("localhost:%d", config.Port),
auth,
"sender@example.com",
[]string{"test@example.com"},
[]byte("Subject: Partial Failure Test\r\n\r\nTest body"),
)
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
// Verify that 2 out of 3 targets succeeded
require.Equal(t, 2, successCount)
}
func TestInvalidTargetURLs(t *testing.T) {
// Track notifications
notifications := make([]string, 0)
mockServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
return
}
body, _ := io.ReadAll(r.Body)
notifications = append(notifications, string(body))
w.WriteHeader(http.StatusOK)
}))
defer mockServer.Close()
// Configure with invalid and valid URLs
config := &Config{
Port: 2529,
Username: "testuser",
Password: "testpass",
Recipients: []ConfigRecipient{
{
Addresses: []string{"test@example.com"},
Targets: []string{
"://invalid-url", // Invalid URL
"generic+" + mockServer.URL + "/?template=json&id=1", // Valid URL
"ht!tp://bad url with spaces", // Invalid URL
"generic+" + mockServer.URL + "/?template=json&id=2", // Valid URL
},
},
},
}
ctx := context.Background()
smtpServer := NewSMTPServer(config)
go func() {
if err := smtpServer.Start(ctx); err != nil {
t.Errorf("failed to start server: %v", err)
}
}()
time.Sleep(100 * time.Millisecond)
defer func() {
if err := smtpServer.Stop(ctx); err != nil {
t.Errorf("failed to stop server: %v", err)
}
}()
// Send test email
auth := smtp.PlainAuth("", config.Username, config.Password, "localhost")
err := smtp.SendMail(
fmt.Sprintf("localhost:%d", config.Port),
auth,
"sender@example.com",
[]string{"test@example.com"},
[]byte("Subject: Invalid URLs Test\r\n\r\nTest body"),
)
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
// Verify only valid URLs received notifications (2 out of 4)
require.Len(t, notifications, 2)
}