Coverage for tests/utils/test_email.py: 100%
60 statements
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
« prev ^ index » next coverage.py v7.16.1, created at 2026-09-18 22:11 +0000
1import unittest
2from unittest.mock import patch, MagicMock
3from webapp.utils.emailer import Emailer, SMTPConfig, EmailerError
6class TestEmailer(unittest.TestCase):
7 def setUp(self):
8 self.smtp_config = SMTPConfig(
9 host="smtp.test.com",
10 port=587,
11 username="testuser@canonical.com",
12 password="testpass",
13 domain="example.com",
14 )
15 self.emailer = Emailer(self.smtp_config)
17 @patch("webapp.utils.emailer.Thread")
18 @patch("smtplib.SMTP")
19 def test_send_email_success(self, mock_smtp, mock_thread):
20 subject = "Test subject"
21 body = "Test body"
22 to_email = "recipient@test.com"
24 mock_server = MagicMock()
25 mock_smtp.return_value.__enter__.return_value = mock_server
27 self.emailer.send_email(subject, body, to_email)
29 target = mock_thread.call_args.kwargs["target"]
30 args = mock_thread.call_args.kwargs["args"]
31 target(*args)
33 mock_smtp.assert_called_once_with(
34 self.smtp_config.host, self.smtp_config.port
35 )
36 mock_server.starttls.assert_called_once()
37 mock_server.login.assert_called_with(
38 self.smtp_config.username, self.smtp_config.password
39 )
41 @patch("smtplib.SMTP")
42 def test_send_email_template_success(self, mock_smtp):
43 with patch("webapp.utils.emailer.render_template") as mock_render:
44 with patch("webapp.utils.emailer.Thread") as mock_thread:
45 mock_render.return_value = "<h1>Hello</h1>"
46 mock_server = MagicMock()
47 mock_smtp.return_value.__enter__.return_value = mock_server
49 self.emailer.send_email_template(
50 to_email="recipient@test.com",
51 subject="Test Template",
52 template_path="template.html",
53 context={"name": "Test"},
54 )
56 target = mock_thread.call_args.kwargs["target"]
57 args = mock_thread.call_args.kwargs["args"]
58 target(*args)
60 mock_render.assert_called_once_with(
61 "template.html", **{"name": "Test"}
62 )
63 mock_server.send_message.assert_called()
65 def test_validate_config_missing_fields(self):
66 incomplete_config = SMTPConfig(
67 host=None,
68 port=587,
69 username=None,
70 password=None,
71 domain="example.com",
72 )
73 emailer = Emailer(incomplete_config)
74 self.assertFalse(emailer.is_configured)
76 @patch("smtplib.SMTP")
77 def test_send_raises_smtp_exception(self, mock_smtp):
78 mock_smtp.side_effect = Exception("SMTP failure")
79 subject = "Test"
80 body = "Body"
81 to_email = "to@example.com"
83 with self.assertRaises(EmailerError):
84 self.emailer._send(subject, body, to_email)
86 def test_create_message_addresses(self):
87 message = self.emailer._create_message(
88 subject="Subject",
89 body="Body",
90 to_email=["a@test.com", "b@test.com"],
91 )
92 self.assertIn("From", message)
93 self.assertIn("To", message)
94 self.assertEqual(message["From"], "testuser@canonical.com")
96 emailer2 = Emailer(
97 SMTPConfig(
98 host="host",
99 port=587,
100 username="simpleuser",
101 password="pass",
102 domain="example.com",
103 )
104 )
105 message2 = emailer2._create_message(
106 "Sub", "Body", "someone@example.com"
107 )
108 self.assertTrue(message2["From"].startswith("noreply+"))
110 def test_send_email_no_configuration(self):
111 no_config = SMTPConfig(
112 host=None,
113 port=587,
114 username=None,
115 password=None,
116 domain="example.com",
117 )
118 emailer = Emailer(no_config)
119 emailer.send_email("subject", "body", "to@example.com")
120 self.assertFalse(emailer.is_configured)