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="
内容
", 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="内容
", 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="测试
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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="内容
", 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"]