geo/backend/tests/test_services/test_email_service.py

497 lines
16 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import pytest
from unittest.mock import Mock, patch, MagicMock
from app.services.email_service import (
EmailService,
EmailMessage,
EmailSendResult,
EMAIL_TEMPLATES,
)
class TestEmailMessage:
"""邮件消息数据结构测试"""
def test_email_message_creation(self):
"""测试EmailMessage数据创建"""
msg = EmailMessage(
to="test@example.com",
subject="测试邮件",
body_html="<h1>测试</h1>",
body_text="测试邮件",
)
assert msg.to == "test@example.com"
assert msg.subject == "测试邮件"
assert msg.body_html == "<h1>测试</h1>"
assert msg.body_text == "测试邮件"
assert msg.attachments == []
assert msg.metadata == {}
def test_email_message_with_attachments(self):
"""测试带附件的邮件消息"""
msg = EmailMessage(
to="test@example.com",
subject="带附件",
body_html="<p>内容</p>",
body_text="内容",
attachments=[{"filename": "report.pdf", "content": b"data"}],
)
assert len(msg.attachments) == 1
assert msg.attachments[0]["filename"] == "report.pdf"
def test_email_message_with_metadata(self):
"""测试带元数据的邮件消息"""
msg = EmailMessage(
to="test@example.com",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
metadata={"brand_name": "test_brand", "alert_type": "error"},
)
assert msg.metadata["brand_name"] == "test_brand"
class TestEmailSendResult:
"""邮件发送结果数据结构测试"""
def test_email_send_result_success(self):
"""测试成功发送结果"""
result = EmailSendResult(
success=True,
message_id="msg_123",
error=None,
retry_count=0,
)
assert result.success is True
assert result.message_id == "msg_123"
assert result.error is None
assert result.retry_count == 0
def test_email_send_result_failure(self):
"""测试失败发送结果"""
result = EmailSendResult(
success=False,
message_id=None,
error="SMTP连接失败",
retry_count=3,
)
assert result.success is False
assert result.message_id is None
assert result.error == "SMTP连接失败"
assert result.retry_count == 3
class TestEmailTemplateRendering:
"""邮件模板渲染测试"""
@pytest.fixture
def email_service(self):
"""创建邮件服务实例"""
return EmailService()
def test_render_alert_notification_template(self, email_service):
"""测试告警通知模板渲染"""
variables = {
"alert_type": "系统错误",
"brand_name": "测试品牌",
"severity": "",
"description": "数据库连接失败",
"timestamp": "2024-01-01 12:00:00",
}
msg = email_service.render_template("alert_notification", "admin@example.com", variables)
assert msg.to == "admin@example.com"
assert "[GEO平台] 告警通知:系统错误" in msg.subject
assert "测试品牌" in msg.body_html
assert "系统错误" in msg.body_html
assert "" in msg.body_html
def test_render_quota_warning_template(self, email_service):
"""测试额度预警模板渲染"""
variables = {
"quota_type": "API调用",
"usage_percentage": 85,
"used": 850,
"limit": 1000,
"recommended_action": "请升级套餐",
}
msg = email_service.render_template("quota_warning", "user@example.com", variables)
assert msg.to == "user@example.com"
assert "[GEO平台] 额度预警API调用" in msg.subject
assert "85%" in msg.body_html
assert "850" in msg.body_html
assert "1000" in msg.body_html
def test_render_template_missing_variables(self, email_service):
"""测试模板渲染缺少变量"""
variables = {"alert_type": "系统错误"}
msg = email_service.render_template("alert_notification", "admin@example.com", variables)
assert msg is not None
assert msg.to == "admin@example.com"
def test_render_template_invalid_template(self, email_service):
"""测试无效模板名称"""
with pytest.raises(ValueError, match="模板不存在"):
email_service.render_template(
"invalid_template",
"admin@example.com",
{"key": "value"},
)
def test_render_template_variable_substitution(self, email_service):
"""测试模板变量替换"""
variables = {
"brand_name": "品牌A",
"alert_type": "告警B",
"severity": "严重",
"description": "描述C",
"timestamp": "时间D",
}
msg = email_service.render_template("alert_notification", "test@example.com", variables)
assert "品牌A" in msg.body_html
assert "告警B" in msg.body_html
assert "严重" in msg.body_html
assert "描述C" in msg.body_html
assert "时间D" in msg.body_html
class TestEmailGeneration:
"""邮件内容生成测试"""
@pytest.fixture
def email_service(self):
"""创建邮件服务实例"""
return EmailService()
def test_generate_alert_notification_email(self, email_service):
"""测试生成告警通知邮件"""
msg = email_service.generate_alert_email(
to="admin@example.com",
alert_type="数据库告警",
brand_name="测试品牌",
severity="",
description="数据库CPU使用率超过90%",
timestamp="2024-01-01 12:00:00",
)
assert msg.to == "admin@example.com"
assert "数据库告警" in msg.subject
assert "测试品牌" in msg.body_html
assert "" in msg.body_html
assert msg.body_text != ""
def test_generate_quota_warning_email(self, email_service):
"""测试生成额度预警邮件"""
msg = email_service.generate_quota_warning_email(
to="user@example.com",
quota_type="API调用",
usage_percentage=85,
used=850,
limit=1000,
recommended_action="建议升级套餐",
)
assert msg.to == "user@example.com"
assert "API调用" in msg.subject
assert "85%" in msg.body_html
assert "850" in msg.body_html
assert "1000" in msg.body_html
def test_generate_email_has_both_formats(self, email_service):
"""测试生成的邮件包含HTML和纯文本格式"""
msg = email_service.generate_alert_email(
to="test@example.com",
alert_type="测试",
brand_name="品牌",
severity="",
description="描述",
timestamp="时间",
)
assert msg.body_html != ""
assert msg.body_text != ""
assert "<" in msg.body_html
assert "<" not in msg.body_text
class TestEmailSending:
"""邮件发送测试"""
@pytest.fixture
def email_service(self):
"""创建邮件服务实例(模拟模式)"""
return EmailService(simulate_mode=True)
def test_send_email_simulate_mode(self, email_service):
"""测试模拟模式发送邮件"""
msg = EmailMessage(
to="test@example.com",
subject="测试",
body_html="<p>测试</p>",
body_text="测试",
)
result = email_service.send_email(msg)
assert result.success is True
assert result.message_id is not None
assert result.error is None
@patch("smtplib.SMTP")
def test_send_email_real_smtp(self, mock_smtp):
"""测试真实SMTP发送模拟SMTP"""
mock_server = MagicMock()
mock_smtp.return_value = mock_server
service = EmailService(
simulate_mode=False,
smtp_host="smtp.example.com",
smtp_port=587,
smtp_user="user@example.com",
smtp_password="password",
)
msg = EmailMessage(
to="recipient@example.com",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
)
result = service.send_email(msg)
assert result.success is True
mock_smtp.assert_called_once_with("smtp.example.com", 587)
mock_server.starttls.assert_called_once()
mock_server.login.assert_called_once_with("user@example.com", "password")
@patch("smtplib.SMTP")
def test_send_email_smtp_failure(self, mock_smtp):
"""测试SMTP发送失败"""
mock_smtp.side_effect = Exception("连接失败")
service = EmailService(
simulate_mode=False,
smtp_host="smtp.example.com",
smtp_port=587,
smtp_user="user@example.com",
smtp_password="password",
)
msg = EmailMessage(
to="test@example.com",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
)
result = service.send_email(msg)
assert result.success is False
assert result.error is not None
assert "连接失败" in result.error
@patch("smtplib.SMTP")
def test_send_email_with_retry(self, mock_smtp):
"""测试邮件发送重试"""
mock_server = MagicMock()
mock_smtp.return_value = mock_server
service = EmailService(
simulate_mode=False,
smtp_host="smtp.example.com",
smtp_port=587,
smtp_user="user@example.com",
smtp_password="password",
max_retries=3,
)
msg = EmailMessage(
to="test@example.com",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
)
result = service.send_email(msg)
assert result.success is True
assert result.retry_count == 0
class TestEmailQueue:
"""邮件队列测试"""
@pytest.fixture
def email_service(self):
"""创建邮件服务实例"""
return EmailService(simulate_mode=True)
def test_add_to_queue(self, email_service):
"""测试添加邮件到队列"""
msg = EmailMessage(
to="test@example.com",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
)
email_service.add_to_queue(msg)
assert len(email_service.get_queue()) == 1
assert email_service.get_queue()[0].to == "test@example.com"
def test_add_multiple_to_queue(self, email_service):
"""测试批量添加邮件到队列"""
messages = [
EmailMessage(to=f"user{i}@example.com", subject=f"测试{i}", body_html="<p>内容</p>", body_text="内容")
for i in range(5)
]
for msg in messages:
email_service.add_to_queue(msg)
assert len(email_service.get_queue()) == 5
def test_send_queue(self, email_service):
"""测试发送队列中的邮件"""
messages = [
EmailMessage(to=f"user{i}@example.com", subject=f"测试{i}", body_html="<p>内容</p>", body_text="内容")
for i in range(3)
]
for msg in messages:
email_service.add_to_queue(msg)
results = email_service.send_queue()
assert len(results) == 3
assert all(r.success for r in results)
assert len(email_service.get_queue()) == 0
def test_send_queue_empty(self, email_service):
"""测试发送空队列"""
results = email_service.send_queue()
assert results == []
def test_clear_queue(self, email_service):
"""测试清空队列"""
msg = EmailMessage(
to="test@example.com",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
)
email_service.add_to_queue(msg)
assert len(email_service.get_queue()) == 1
email_service.clear_queue()
assert len(email_service.get_queue()) == 0
class TestEmailAttachments:
"""邮件附件测试"""
@pytest.fixture
def email_service(self):
"""创建邮件服务实例"""
return EmailService(simulate_mode=True)
def test_add_attachment_to_message(self, email_service):
"""测试添加附件到邮件"""
msg = EmailMessage(
to="test@example.com",
subject="带附件",
body_html="<p>内容</p>",
body_text="内容",
)
email_service.add_attachment(msg, "report.pdf", b"PDF content")
assert len(msg.attachments) == 1
assert msg.attachments[0]["filename"] == "report.pdf"
assert msg.attachments[0]["content"] == b"PDF content"
def test_add_multiple_attachments(self, email_service):
"""测试添加多个附件"""
msg = EmailMessage(
to="test@example.com",
subject="多附件",
body_html="<p>内容</p>",
body_text="内容",
)
email_service.add_attachment(msg, "file1.pdf", b"content1")
email_service.add_attachment(msg, "file2.xlsx", b"content2")
assert len(msg.attachments) == 2
assert msg.attachments[0]["filename"] == "file1.pdf"
assert msg.attachments[1]["filename"] == "file2.xlsx"
class TestEmailValidation:
"""邮箱地址验证测试"""
@pytest.fixture
def email_service(self):
"""创建邮件服务实例"""
return EmailService()
def test_validate_valid_email(self, email_service):
"""测试有效邮箱地址"""
assert email_service.validate_email("test@example.com") is True
assert email_service.validate_email("user.name@domain.org") is True
assert email_service.validate_email("user+tag@example.com") is True
def test_validate_invalid_email(self, email_service):
"""测试无效邮箱地址"""
assert email_service.validate_email("invalid") is False
assert email_service.validate_email("invalid@") is False
assert email_service.validate_email("@example.com") is False
assert email_service.validate_email("test@") is False
assert email_service.validate_email("") is False
def test_send_to_invalid_email(self, email_service):
"""测试发送到无效邮箱"""
msg = EmailMessage(
to="invalid_email",
subject="测试",
body_html="<p>内容</p>",
body_text="内容",
)
result = email_service.send_email(msg)
assert result.success is False
assert result.error is not None
class TestEmailTemplatesConstants:
"""邮件模板常量测试"""
def test_email_templates_exist(self):
"""测试邮件模板存在"""
assert "alert_notification" in EMAIL_TEMPLATES
assert "quota_warning" in EMAIL_TEMPLATES
def test_alert_notification_template_structure(self):
"""测试告警通知模板结构"""
template = EMAIL_TEMPLATES["alert_notification"]
assert "subject" in template
assert "body_html" in template
assert "body_text" in template
assert "{alert_type}" in template["subject"]
assert "{brand_name}" in template["body_html"]
def test_quota_warning_template_structure(self):
"""测试额度预警模板结构"""
template = EMAIL_TEMPLATES["quota_warning"]
assert "subject" in template
assert "body_html" in template
assert "body_text" in template
assert "{quota_type}" in template["subject"]
assert "{usage_percentage}" in template["body_html"]