"""Unit tests for email service. These tests verify email template rendering, HTML formatting, password generation, and SMTP settings persistence. """ import string from unittest.mock import AsyncMock, patch import pytest from sqlalchemy.ext.asyncio import AsyncSession from backend.app.models.notification_template import NotificationTemplate from backend.app.services.email_service import ( create_welcome_email_from_template, generate_secure_password, render_template, ) class TestEmailTemplateFormatting: """Tests for email template formatting.""" @pytest.mark.asyncio async def test_welcome_email_newlines_converted_to_br(self): """Verify that newlines in welcome email body are converted to
tags.""" # Mock database session db = AsyncMock(spec=AsyncSession) # Mock template with newlines template = NotificationTemplate( event_type="user_created", name="Welcome Email", title_template="Welcome to {app_name}", body_template="Hello {username}!\n\nYour password is: {password}\n\nPlease login at: {login_url}", is_default=True, ) # Patch get_notification_template to return our template with patch("backend.app.services.email_service.get_notification_template", return_value=template): # Generate email subject, text_body, html_body = await create_welcome_email_from_template( db=db, username="testuser", password="testpass123", login_url="http://example.com/login", app_name="TestApp", ) # Verify subject assert subject == "Welcome to TestApp" # Verify text body has newlines assert "\n\n" in text_body assert "Hello testuser!" in text_body # The password is added by Bambuddy, never by the template assert "Your password is: \n" in text_body assert text_body.endswith("Username: testuser\nPassword: testpass123") # Verify HTML body has
tags instead of relying on CSS assert "
" in html_body # Should not use white-space: pre-wrap assert "white-space: pre-wrap" not in html_body # Should have proper structure assert "" in html_body assert '
' in html_body # Verify that escaped content is present (XSS protection) assert "Hello testuser!
" in html_body assert "Your password is:
" in html_body assert ">testpass123" in html_body @pytest.mark.asyncio async def test_email_header_padding(self): """Verify that email header has proper padding to prevent cutoff.""" # Mock database session db = AsyncMock(spec=AsyncSession) # Mock template template = NotificationTemplate( event_type="user_created", name="Welcome Email", title_template="Welcome", body_template="Test body", is_default=True, ) # Patch get_notification_template to return our template with patch("backend.app.services.email_service.get_notification_template", return_value=template): # Generate email subject, text_body, html_body = await create_welcome_email_from_template( db=db, username="testuser", password="testpass123", login_url="http://example.com/login", ) # Verify header has 30px padding (not 20px which was cutting off) assert "padding: 30px; border-radius: 8px 8px 0 0;" in html_body @pytest.mark.asyncio async def test_email_xss_protection(self): """Verify that HTML escaping is applied to prevent XSS attacks.""" # Mock database session db = AsyncMock(spec=AsyncSession) # Mock template with potential XSS content template = NotificationTemplate( event_type="user_created", name="Welcome Email", title_template="Welcome ", body_template="Hello \nTest", is_default=True, ) # Patch get_notification_template to return our template with patch("backend.app.services.email_service.get_notification_template", return_value=template): # Generate email subject, text_body, html_body = await create_welcome_email_from_template( db=db, username="testuser", password="testpass123", login_url="http://example.com/login", ) # Verify that script tags are escaped assert "<script>" in html_body # Verify no unescaped script tags assert "