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, "") assert.Contains(t, 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, "") assert.Contains(t, 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, "") assert.Contains(t, 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) { t.Skip("Skipping test that requires real database connection") // This test requires a real database to store verification tokens // It's tested in integration tests instead } 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") }