mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-01-23 13:08:59 -06:00
- Normalize line endings from CRLF to LF across all files to match .editorconfig - Standardize quote style from single quotes to double quotes - Normalize whitespace and formatting throughout codebase - Apply consistent code style across 372 files including: * Application code (models, routes, services, utils) * Test files * Configuration files * CI/CD workflows This ensures consistency with the project's .editorconfig settings and improves code maintainability.
312 lines
12 KiB
Python
312 lines
12 KiB
Python
"""
|
|
Tests for email functionality
|
|
"""
|
|
|
|
import pytest
|
|
from unittest.mock import patch, MagicMock
|
|
from flask import current_app
|
|
from app.utils.email import send_email, check_email_configuration, send_test_email, init_mail
|
|
|
|
|
|
class TestEmailConfiguration:
|
|
"""Tests for email configuration"""
|
|
|
|
def test_init_mail(self, app):
|
|
"""Test email initialization"""
|
|
with app.app_context():
|
|
mail = init_mail(app)
|
|
assert mail is not None
|
|
assert "MAIL_SERVER" in app.config
|
|
assert "MAIL_PORT" in app.config
|
|
assert "MAIL_DEFAULT_SENDER" in app.config
|
|
|
|
def test_email_config_status_not_configured(self, app):
|
|
"""Test email configuration status when not configured"""
|
|
with app.app_context():
|
|
# Reset mail server to simulate unconfigured state
|
|
app.config["MAIL_SERVER"] = "localhost"
|
|
|
|
status = check_email_configuration()
|
|
|
|
assert status is not None
|
|
assert "configured" in status
|
|
assert "settings" in status
|
|
assert "errors" in status
|
|
assert "warnings" in status
|
|
assert status["configured"] is False
|
|
assert len(status["errors"]) > 0
|
|
|
|
def test_email_config_status_configured(self, app):
|
|
"""Test email configuration status when properly configured"""
|
|
with app.app_context():
|
|
# Set up proper configuration
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
app.config["MAIL_PORT"] = 587
|
|
app.config["MAIL_USE_TLS"] = True
|
|
app.config["MAIL_USE_SSL"] = False
|
|
app.config["MAIL_USERNAME"] = "test@example.com"
|
|
app.config["MAIL_PASSWORD"] = "test_password"
|
|
app.config["MAIL_DEFAULT_SENDER"] = "noreply@example.com"
|
|
|
|
status = check_email_configuration()
|
|
|
|
assert status is not None
|
|
assert status["configured"] is True
|
|
assert len(status["errors"]) == 0
|
|
assert status["settings"]["server"] == "smtp.gmail.com"
|
|
assert status["settings"]["port"] == 587
|
|
assert status["settings"]["password_set"] is True
|
|
|
|
def test_email_config_warns_about_default_sender(self, app):
|
|
"""Test that configuration warns about default sender"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
app.config["MAIL_DEFAULT_SENDER"] = "noreply@timetracker.local"
|
|
|
|
status = check_email_configuration()
|
|
|
|
assert len(status["warnings"]) > 0
|
|
assert any("Default sender" in w for w in status["warnings"])
|
|
|
|
def test_email_config_errors_on_both_tls_and_ssl(self, app):
|
|
"""Test that configuration errors when both TLS and SSL are enabled"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
app.config["MAIL_USE_TLS"] = True
|
|
app.config["MAIL_USE_SSL"] = True
|
|
|
|
status = check_email_configuration()
|
|
|
|
assert len(status["errors"]) > 0
|
|
assert any("TLS and SSL" in e for e in status["errors"])
|
|
|
|
|
|
class TestSendEmail:
|
|
"""Tests for sending emails"""
|
|
|
|
@patch("app.utils.email.mail.send")
|
|
@patch("app.utils.email.Thread")
|
|
def test_send_email_success(self, mock_thread, mock_send, app):
|
|
"""Test sending email successfully"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
|
|
send_email(
|
|
subject="Test Subject",
|
|
recipients=["test@example.com"],
|
|
text_body="Test body",
|
|
html_body="<p>Test body</p>",
|
|
)
|
|
|
|
# Verify thread was started for async sending
|
|
assert mock_thread.called
|
|
|
|
def test_send_email_no_server(self, app):
|
|
"""Test sending email with no mail server configured"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = None
|
|
|
|
# This should not raise an exception, but log a warning and return early
|
|
send_email(subject="Test Subject", recipients=["test@example.com"], text_body="Test body")
|
|
|
|
# The function should return without error
|
|
# (The warning is logged but we can't easily capture it in this context)
|
|
|
|
def test_send_email_no_recipients(self, app):
|
|
"""Test sending email with no recipients"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
|
|
# This should not raise an exception, but log a warning and return early
|
|
send_email(subject="Test Subject", recipients=[], text_body="Test body")
|
|
|
|
# The function should return without error
|
|
# (The warning is logged but we can't easily capture it in this context)
|
|
|
|
@patch("app.utils.email.mail.send")
|
|
def test_send_test_email_success(self, mock_send, app):
|
|
"""Test sending test email successfully"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
app.config["MAIL_DEFAULT_SENDER"] = "test@example.com"
|
|
|
|
success, message = send_test_email("recipient@example.com", "Test Sender")
|
|
|
|
assert success is True
|
|
assert "successfully" in message.lower()
|
|
assert mock_send.called
|
|
|
|
def test_send_test_email_invalid_recipient(self, app):
|
|
"""Test sending test email with invalid recipient"""
|
|
with app.app_context():
|
|
success, message = send_test_email("invalid-email", "Test Sender")
|
|
|
|
assert success is False
|
|
assert "Invalid" in message
|
|
|
|
def test_send_test_email_no_server(self, app):
|
|
"""Test sending test email with no mail server"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = None
|
|
|
|
success, message = send_test_email("test@example.com", "Test Sender")
|
|
|
|
assert success is False
|
|
assert "not configured" in message
|
|
|
|
@patch("app.utils.email.mail.send")
|
|
def test_send_test_email_exception(self, mock_send, app):
|
|
"""Test sending test email with exception"""
|
|
with app.app_context():
|
|
app.config["MAIL_SERVER"] = "smtp.gmail.com"
|
|
app.config["MAIL_DEFAULT_SENDER"] = "test@example.com"
|
|
|
|
# Simulate exception
|
|
mock_send.side_effect = Exception("SMTP error")
|
|
|
|
success, message = send_test_email("test@example.com", "Test Sender")
|
|
|
|
assert success is False
|
|
assert "Failed" in message
|
|
|
|
|
|
class TestEmailIntegration:
|
|
"""Integration tests for email functionality"""
|
|
|
|
def check_email_configuration_in_app_context(self, app):
|
|
"""Test that email configuration is available in app context"""
|
|
with app.app_context():
|
|
assert hasattr(current_app, "config")
|
|
assert "MAIL_SERVER" in current_app.config
|
|
assert "MAIL_PORT" in current_app.config
|
|
assert "MAIL_USE_TLS" in current_app.config
|
|
assert "MAIL_DEFAULT_SENDER" in current_app.config
|
|
|
|
def test_email_settings_from_environment(self, app, monkeypatch):
|
|
"""Test that email settings are loaded from environment"""
|
|
# Set environment variables
|
|
monkeypatch.setenv("MAIL_SERVER", "smtp.test.com")
|
|
monkeypatch.setenv("MAIL_PORT", "465")
|
|
monkeypatch.setenv("MAIL_USE_SSL", "true")
|
|
|
|
# Reinitialize mail with new environment
|
|
with app.app_context():
|
|
mail = init_mail(app)
|
|
|
|
assert app.config["MAIL_SERVER"] == "smtp.test.com"
|
|
assert app.config["MAIL_PORT"] == 465
|
|
assert app.config["MAIL_USE_SSL"] is True
|
|
|
|
|
|
class TestDatabaseEmailConfiguration:
|
|
"""Tests for database-backed email configuration"""
|
|
|
|
def test_get_mail_config_when_disabled(self, app):
|
|
"""Test get_mail_config returns None when database config is disabled"""
|
|
with app.app_context():
|
|
from app.models import Settings
|
|
|
|
settings = Settings.get_settings()
|
|
settings.mail_enabled = False
|
|
settings.mail_server = "smtp.test.com"
|
|
|
|
config = settings.get_mail_config()
|
|
assert config is None
|
|
|
|
def test_get_mail_config_when_enabled(self, app):
|
|
"""Test get_mail_config returns config when enabled"""
|
|
with app.app_context():
|
|
from app.models import Settings
|
|
|
|
settings = Settings.get_settings()
|
|
settings.mail_enabled = True
|
|
settings.mail_server = "smtp.test.com"
|
|
settings.mail_port = 587
|
|
settings.mail_use_tls = True
|
|
settings.mail_use_ssl = False
|
|
settings.mail_username = "test@example.com"
|
|
settings.mail_password = "test_password"
|
|
settings.mail_default_sender = "noreply@example.com"
|
|
|
|
config = settings.get_mail_config()
|
|
|
|
assert config is not None
|
|
assert config["MAIL_SERVER"] == "smtp.test.com"
|
|
assert config["MAIL_PORT"] == 587
|
|
assert config["MAIL_USE_TLS"] is True
|
|
assert config["MAIL_USE_SSL"] is False
|
|
assert config["MAIL_USERNAME"] == "test@example.com"
|
|
assert config["MAIL_PASSWORD"] == "test_password"
|
|
assert config["MAIL_DEFAULT_SENDER"] == "noreply@example.com"
|
|
|
|
def test_init_mail_uses_database_config(self, app):
|
|
"""Test that init_mail uses database settings when available"""
|
|
with app.app_context():
|
|
from app.models import Settings
|
|
from app.utils.email import init_mail
|
|
from app import db
|
|
|
|
settings = Settings.get_settings()
|
|
settings.mail_enabled = True
|
|
settings.mail_server = "smtp.database.com"
|
|
settings.mail_port = 465
|
|
db.session.commit()
|
|
|
|
init_mail(app)
|
|
|
|
# Should use database settings
|
|
assert app.config["MAIL_SERVER"] == "smtp.database.com"
|
|
assert app.config["MAIL_PORT"] == 465
|
|
|
|
def test_reload_mail_config(self, app):
|
|
"""Test reloading email configuration"""
|
|
with app.app_context():
|
|
from app.models import Settings
|
|
from app.utils.email import reload_mail_config
|
|
from app import db
|
|
|
|
# Set up database config
|
|
settings = Settings.get_settings()
|
|
settings.mail_enabled = True
|
|
settings.mail_server = "smtp.reloaded.com"
|
|
db.session.commit()
|
|
|
|
# Reload configuration
|
|
success = reload_mail_config(app)
|
|
|
|
assert success is True
|
|
assert app.config["MAIL_SERVER"] == "smtp.reloaded.com"
|
|
|
|
def test_check_email_configuration_shows_source(self, app):
|
|
"""Test that configuration status shows source (database or environment)"""
|
|
with app.app_context():
|
|
from app.models import Settings
|
|
from app.utils.email import check_email_configuration
|
|
from app import db
|
|
|
|
# Test with database config
|
|
settings = Settings.get_settings()
|
|
settings.mail_enabled = True
|
|
settings.mail_server = "smtp.database.com"
|
|
db.session.commit()
|
|
|
|
status = check_email_configuration()
|
|
|
|
assert "source" in status
|
|
assert status["source"] == "database"
|
|
|
|
# Test with environment config
|
|
settings.mail_enabled = False
|
|
db.session.commit()
|
|
|
|
status = check_email_configuration()
|
|
assert status["source"] == "environment"
|
|
|
|
|
|
# Fixtures
|
|
@pytest.fixture
|
|
def mock_mail_send():
|
|
"""Mock the mail.send method"""
|
|
with patch("app.utils.email.mail.send") as mock:
|
|
yield mock
|