Files
cv-site/tests/integration/email_test.go
T

275 lines
6.8 KiB
Go
Raw Normal View History

package integration_test
import (
"crypto/tls"
"fmt"
"net/smtp"
"os"
"testing"
"time"
"github.com/juanatsap/cv-site/internal/services"
)
// TestSMTPConnection tests that SMTP credentials are valid and connection works
// This test requires valid SMTP credentials in environment variables
// Run with: go test -v ./tests/integration/... -run TestSMTPConnection
func TestSMTPConnection(t *testing.T) {
// Skip in CI or when credentials aren't available
host := os.Getenv("SMTP_HOST")
port := os.Getenv("SMTP_PORT")
user := os.Getenv("SMTP_USER")
pass := os.Getenv("SMTP_PASSWORD")
if host == "" || user == "" || pass == "" {
t.Skip("Skipping SMTP test: SMTP credentials not configured")
}
addr := fmt.Sprintf("%s:%s", host, port)
t.Run("TLS_Connection", func(t *testing.T) {
tlsConfig := &tls.Config{
ServerName: host,
MinVersion: tls.VersionTLS12,
}
if port == "465" {
// Implicit SSL
conn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
t.Fatalf("TLS dial failed: %v", err)
}
defer func() { _ = conn.Close() }()
t.Log("TLS connection established (port 465 - implicit SSL)")
} else {
// STARTTLS
client, err := smtp.Dial(addr)
if err != nil {
t.Fatalf("SMTP dial failed: %v", err)
}
defer func() { _ = client.Close() }()
if err := client.StartTLS(tlsConfig); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
t.Log("TLS connection established (STARTTLS)")
}
})
t.Run("SMTP_Authentication", func(t *testing.T) {
tlsConfig := &tls.Config{
ServerName: host,
MinVersion: tls.VersionTLS12,
}
var client *smtp.Client
var err error
if port == "465" {
// Implicit SSL - connect with TLS from the start
conn, err := tls.Dial("tcp", addr, tlsConfig)
if err != nil {
t.Fatalf("TLS dial failed: %v", err)
}
defer func() { _ = conn.Close() }()
client, err = smtp.NewClient(conn, host)
if err != nil {
t.Fatalf("SMTP client creation failed: %v", err)
}
} else {
// STARTTLS
client, err = smtp.Dial(addr)
if err != nil {
t.Fatalf("SMTP dial failed: %v", err)
}
if err := client.StartTLS(tlsConfig); err != nil {
t.Fatalf("STARTTLS failed: %v", err)
}
}
defer func() { _ = client.Close() }()
auth := smtp.PlainAuth("", user, pass, host)
if err := client.Auth(auth); err != nil {
t.Fatalf("SMTP authentication failed: %v", err)
}
t.Log("SMTP authentication successful")
_ = client.Quit()
})
}
// TestEmailServiceSend tests actual email sending
// This will send a real test email - use sparingly
// Run with: go test -v ./tests/integration/... -run TestEmailServiceSend
func TestEmailServiceSend(t *testing.T) {
// Skip in CI or when credentials aren't available
host := os.Getenv("SMTP_HOST")
port := os.Getenv("SMTP_PORT")
user := os.Getenv("SMTP_USER")
pass := os.Getenv("SMTP_PASSWORD")
from := os.Getenv("SMTP_FROM_EMAIL")
to := os.Getenv("CONTACT_EMAIL")
if host == "" || user == "" || pass == "" {
t.Skip("Skipping email send test: SMTP credentials not configured")
}
if from == "" {
from = user
}
if to == "" {
t.Skip("Skipping email send test: CONTACT_EMAIL not configured")
}
config := &services.EmailConfig{
SMTPHost: host,
SMTPPort: port,
SMTPUser: user,
SMTPPassword: pass,
FromEmail: from,
ToEmail: to,
}
emailService := services.NewEmailService(config)
testData := &services.ContactFormData{
Email: "test-sender@example.com",
Name: "Integration Test",
Company: "Test Suite",
Subject: "Email Integration Test",
Message: "This is an automated test email sent by the integration test suite. If you receive this, the email configuration is working correctly.",
IP: "127.0.0.1",
Time: time.Now(),
}
t.Run("SendContactForm", func(t *testing.T) {
err := emailService.SendContactForm(testData)
if err != nil {
t.Fatalf("Failed to send email: %v", err)
}
t.Logf("Test email sent successfully to %s", to)
})
}
// TestEmailServiceValidation tests that the email service properly validates input
func TestEmailServiceValidation(t *testing.T) {
config := &services.EmailConfig{
SMTPHost: "smtp.test.com",
SMTPPort: "465",
SMTPUser: "test@test.com",
SMTPPassword: "password",
FromEmail: "from@test.com",
ToEmail: "to@test.com",
}
emailService := services.NewEmailService(config)
tests := []struct {
name string
data *services.ContactFormData
wantErr bool
errMsg string
}{
{
name: "valid data",
data: &services.ContactFormData{
Email: "valid@example.com",
Name: "Valid User",
Message: "This is a valid message with more than 10 characters.",
Time: time.Now(),
},
wantErr: false,
},
{
name: "missing email",
data: &services.ContactFormData{
Name: "No Email",
Message: "This is a valid message.",
Time: time.Now(),
},
wantErr: true,
errMsg: "email is required",
},
{
name: "invalid email format",
data: &services.ContactFormData{
Email: "notanemail",
Name: "Bad Email",
Message: "This is a valid message.",
Time: time.Now(),
},
wantErr: true,
errMsg: "invalid email format",
},
{
name: "missing message",
data: &services.ContactFormData{
Email: "valid@example.com",
Name: "No Message",
Time: time.Now(),
},
wantErr: true,
errMsg: "message is required",
},
{
name: "message too short",
data: &services.ContactFormData{
Email: "valid@example.com",
Name: "Short Msg",
Message: "Hi",
Time: time.Now(),
},
wantErr: true,
errMsg: "message too short",
},
{
name: "email with newlines (header injection)",
data: &services.ContactFormData{
Email: "test@example.com\nBcc: attacker@evil.com",
Name: "Attacker",
Message: "Trying to inject headers.",
Time: time.Now(),
},
wantErr: true,
errMsg: "prohibited characters",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// We can't actually send since we don't have real SMTP
// but we can test validation
err := tt.data.Validate()
if tt.wantErr {
if err == nil {
t.Errorf("expected error containing %q, got nil", tt.errMsg)
} else if tt.errMsg != "" && !containsString(err.Error(), tt.errMsg) {
t.Errorf("expected error containing %q, got %q", tt.errMsg, err.Error())
}
} else {
if err != nil {
t.Errorf("unexpected error: %v", err)
}
}
})
}
_ = emailService // Avoid unused variable warning
}
func containsString(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsStringHelper(s, substr))
}
func containsStringHelper(s, substr string) bool {
for i := 0; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}