veza/veza-backend-api/internal/services/email_service_test.go
senke a6cf20e614 fix(tests): fix 2 skipped tests, add clear skip reasons to 11 others
INT-04: Fixed nil UserID panic in AuditService (re-enabled 2 tests).
Added INT-04 comments explaining skip reasons for tests requiring
PostgreSQL, real file headers, or external services.
2026-02-22 17:53:00 +01:00

317 lines
9.5 KiB
Go

package services
import (
"os"
"strings"
"testing"
"veza-backend-api/internal/database"
"github.com/stretchr/testify/assert"
"go.uber.org/zap"
)
func setupTestEmailService(t *testing.T) *EmailService {
testDB := &database.Database{}
logger := zap.NewNop()
// Set environment variables for testing
os.Setenv("FRONTEND_URL", "http://localhost:5173")
os.Setenv("SMTP_HOST", "")
os.Setenv("SMTP_PORT", "587")
os.Setenv("FROM_EMAIL", "test@veza.com")
os.Setenv("FROM_NAME", "Veza Test")
return NewEmailService(testDB, logger)
}
func TestEmailService_NewEmailService(t *testing.T) {
service := setupTestEmailService(t)
assert.NotNil(t, service)
assert.NotNil(t, service.logger)
assert.NotNil(t, service.db)
}
func TestEmailService_SendVerificationEmail_NoSMTP(t *testing.T) {
service := setupTestEmailService(t)
// Without SMTP configured, should not error (just logs)
err := service.SendVerificationEmail("test@example.com", "test-token-123")
assert.NoError(t, err)
}
func TestEmailService_SendVerificationEmail_WithFRONTEND_URL(t *testing.T) {
service := setupTestEmailService(t)
os.Setenv("FRONTEND_URL", "https://app.veza.com")
err := service.SendVerificationEmail("test@example.com", "test-token-123")
assert.NoError(t, err)
}
func TestEmailService_SendVerificationEmail_DefaultURL(t *testing.T) {
service := setupTestEmailService(t)
os.Unsetenv("FRONTEND_URL")
err := service.SendVerificationEmail("test@example.com", "test-token-123")
assert.NoError(t, err)
}
func TestEmailService_buildVerificationEmailHTML_Success(t *testing.T) {
service := setupTestEmailService(t)
verifyURL := "https://app.veza.com/verify-email?token=test-token-123"
html := service.buildVerificationEmailHTML(verifyURL)
assert.Contains(t, html, "<!DOCTYPE html>")
assert.Contains(t, html, "<html>")
assert.Contains(t, html, "Verify your Veza account")
assert.Contains(t, html, "Verify Email Address")
assert.Contains(t, html, verifyURL)
assert.Contains(t, html, "This link will expire in 24 hours")
}
func TestEmailService_buildVerificationEmailHTML_TemplateFallback(t *testing.T) {
service := setupTestEmailService(t)
verifyURL := "https://app.veza.com/verify-email?token=test-token-123"
html := service.buildVerificationEmailHTML(verifyURL)
// Should contain the URL even if template parsing fails
assert.Contains(t, html, verifyURL)
}
func TestEmailService_SendWelcomeEmail_NoSMTP(t *testing.T) {
service := setupTestEmailService(t)
err := service.SendWelcomeEmail("test@example.com", "testuser")
assert.NoError(t, err)
}
func TestEmailService_buildWelcomeEmailHTML_Success(t *testing.T) {
service := setupTestEmailService(t)
username := "testuser"
html := service.buildWelcomeEmailHTML(username)
assert.Contains(t, html, "<!DOCTYPE html>")
assert.Contains(t, html, "<html>")
assert.Contains(t, html, "Welcome to Veza")
assert.Contains(t, html, username)
assert.Contains(t, html, "Get Started")
assert.Contains(t, html, "Uploading your first track")
assert.Contains(t, html, "Creating playlists")
assert.Contains(t, html, "Discovering new music")
}
func TestEmailService_buildWelcomeEmailHTML_WithFRONTEND_URL(t *testing.T) {
service := setupTestEmailService(t)
os.Setenv("FRONTEND_URL", "https://app.veza.com")
username := "testuser"
html := service.buildWelcomeEmailHTML(username)
assert.Contains(t, html, "https://app.veza.com")
}
func TestEmailService_buildWelcomeEmailHTML_DefaultURL(t *testing.T) {
service := setupTestEmailService(t)
os.Unsetenv("FRONTEND_URL")
username := "testuser"
html := service.buildWelcomeEmailHTML(username)
assert.Contains(t, html, "http://localhost:5173")
}
func TestEmailService_SendNotificationEmail_NoSMTP(t *testing.T) {
service := setupTestEmailService(t)
err := service.SendNotificationEmail("test@example.com", "Test Subject", "Test message", "track_like")
assert.NoError(t, err)
}
func TestEmailService_buildNotificationEmailHTML_TrackLike(t *testing.T) {
service := setupTestEmailService(t)
message := "Someone liked your track"
html := service.buildNotificationEmailHTML(message, "track_like")
assert.Contains(t, html, "<!DOCTYPE html>")
assert.Contains(t, html, "<html>")
assert.Contains(t, html, message)
assert.Contains(t, html, "❤️")
assert.Contains(t, html, "View on Veza")
}
func TestEmailService_buildNotificationEmailHTML_NewFollower(t *testing.T) {
service := setupTestEmailService(t)
message := "You have a new follower"
html := service.buildNotificationEmailHTML(message, "new_follower")
assert.Contains(t, html, message)
assert.Contains(t, html, "👤")
}
func TestEmailService_buildNotificationEmailHTML_PlaylistUpdate(t *testing.T) {
service := setupTestEmailService(t)
message := "A playlist was updated"
html := service.buildNotificationEmailHTML(message, "playlist_update")
assert.Contains(t, html, message)
assert.Contains(t, html, "🎵")
}
func TestEmailService_buildNotificationEmailHTML_CommentReply(t *testing.T) {
service := setupTestEmailService(t)
message := "Someone replied to your comment"
html := service.buildNotificationEmailHTML(message, "comment_reply")
assert.Contains(t, html, message)
assert.Contains(t, html, "💬")
}
func TestEmailService_buildNotificationEmailHTML_Default(t *testing.T) {
service := setupTestEmailService(t)
message := "You have a notification"
html := service.buildNotificationEmailHTML(message, "unknown_type")
assert.Contains(t, html, message)
assert.Contains(t, html, "🔔")
}
func TestEmailService_buildNotificationEmailHTML_WithFRONTEND_URL(t *testing.T) {
service := setupTestEmailService(t)
os.Setenv("FRONTEND_URL", "https://app.veza.com")
message := "Test notification"
html := service.buildNotificationEmailHTML(message, "track_like")
assert.Contains(t, html, "https://app.veza.com")
}
func TestEmailService_buildNotificationEmailHTML_DefaultURL(t *testing.T) {
service := setupTestEmailService(t)
os.Unsetenv("FRONTEND_URL")
message := "Test notification"
html := service.buildNotificationEmailHTML(message, "track_like")
assert.Contains(t, html, "http://localhost:5173")
}
func TestEmailService_sendEmail_NoSMTP(t *testing.T) {
service := setupTestEmailService(t)
// Without SMTP configured, should not error (just logs)
err := service.sendEmail("test@example.com", "Test Subject", "Test Body")
assert.NoError(t, err)
}
func TestEmailService_generateVerificationToken(t *testing.T) {
service := setupTestEmailService(t)
token1, err1 := service.generateVerificationToken()
assert.NoError(t, err1)
assert.NotEmpty(t, token1)
token2, err2 := service.generateVerificationToken()
assert.NoError(t, err2)
assert.NotEmpty(t, token2)
// Tokens should be different
assert.NotEqual(t, token1, token2)
}
func TestEmailService_generateVerificationToken_Length(t *testing.T) {
service := setupTestEmailService(t)
token, err := service.generateVerificationToken()
assert.NoError(t, err)
assert.Greater(t, len(token), 20) // Base64 encoded 32 bytes should be ~44 chars
}
func TestEmailService_SendVerificationEmailWithUserID_NoSMTP(t *testing.T) {
// INT-04: Skip - requires real DB to store verification tokens; covered by integration tests.
t.Skip("requires real database connection for token storage")
}
func TestEmailService_buildVerificationEmailHTML_ContainsToken(t *testing.T) {
service := setupTestEmailService(t)
testToken := "test-verification-token-12345"
verifyURL := "https://app.veza.com/verify-email?token=" + testToken
html := service.buildVerificationEmailHTML(verifyURL)
assert.Contains(t, html, testToken)
assert.Contains(t, html, "verify-email?token="+testToken)
}
func TestEmailService_buildVerificationEmailHTML_URLAppearsTwice(t *testing.T) {
service := setupTestEmailService(t)
verifyURL := "https://app.veza.com/verify-email?token=test-token"
html := service.buildVerificationEmailHTML(verifyURL)
// URL should appear at least twice (button and text)
urlCount := strings.Count(html, verifyURL)
assert.GreaterOrEqual(t, urlCount, 2, "Verify URL should appear at least twice (button and text)")
}
func TestEmailService_buildWelcomeEmailHTML_ContainsUsername(t *testing.T) {
service := setupTestEmailService(t)
username := "testuser123"
html := service.buildWelcomeEmailHTML(username)
assert.Contains(t, html, username)
}
func TestEmailService_buildNotificationEmailHTML_ContainsMessage(t *testing.T) {
service := setupTestEmailService(t)
message := "Custom notification message"
html := service.buildNotificationEmailHTML(message, "track_like")
assert.Contains(t, html, message)
}
func TestEmailService_buildNotificationEmailHTML_NotificationPreferences(t *testing.T) {
service := setupTestEmailService(t)
message := "Test notification"
html := service.buildNotificationEmailHTML(message, "track_like")
assert.Contains(t, html, "notification preferences")
assert.Contains(t, html, "account settings")
}
func TestEmailService_EmailService_Fields(t *testing.T) {
service := setupTestEmailService(t)
assert.NotNil(t, service.db)
assert.NotNil(t, service.logger)
// SMTP fields may be empty if not configured
}
func TestEmailService_buildVerificationEmailHTML_ExpirationMessage(t *testing.T) {
service := setupTestEmailService(t)
verifyURL := "https://app.veza.com/verify-email?token=test"
html := service.buildVerificationEmailHTML(verifyURL)
assert.Contains(t, html, "24 hours")
}
func TestEmailService_buildPasswordResetEmail_ExpirationMessage(t *testing.T) {
service := setupTestEmailService(t)
resetURL := "https://app.veza.com/reset-password?token=test"
html := service.buildPasswordResetEmail(resetURL)
assert.Contains(t, html, "1 hour")
}