You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

test_smtp_client.py 3.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. from unittest.mock import MagicMock, patch
  2. import pytest
  3. from libs.smtp import SMTPClient
  4. def _mail() -> dict:
  5. return {"to": "user@example.com", "subject": "Hi", "html": "<b>Hi</b>"}
  6. @patch("libs.smtp.smtplib.SMTP")
  7. def test_smtp_plain_success(mock_smtp_cls: MagicMock):
  8. mock_smtp = MagicMock()
  9. mock_smtp_cls.return_value = mock_smtp
  10. client = SMTPClient(server="smtp.example.com", port=25, username="", password="", _from="noreply@example.com")
  11. client.send(_mail())
  12. mock_smtp_cls.assert_called_once_with("smtp.example.com", 25, timeout=10)
  13. mock_smtp.sendmail.assert_called_once()
  14. mock_smtp.quit.assert_called_once()
  15. @patch("libs.smtp.smtplib.SMTP")
  16. def test_smtp_tls_opportunistic_success(mock_smtp_cls: MagicMock):
  17. mock_smtp = MagicMock()
  18. mock_smtp_cls.return_value = mock_smtp
  19. client = SMTPClient(
  20. server="smtp.example.com",
  21. port=587,
  22. username="user",
  23. password="pass",
  24. _from="noreply@example.com",
  25. use_tls=True,
  26. opportunistic_tls=True,
  27. )
  28. client.send(_mail())
  29. mock_smtp_cls.assert_called_once_with("smtp.example.com", 587, timeout=10)
  30. assert mock_smtp.ehlo.call_count == 2
  31. mock_smtp.starttls.assert_called_once()
  32. mock_smtp.login.assert_called_once_with("user", "pass")
  33. mock_smtp.sendmail.assert_called_once()
  34. mock_smtp.quit.assert_called_once()
  35. @patch("libs.smtp.smtplib.SMTP_SSL")
  36. def test_smtp_tls_ssl_branch_and_timeout(mock_smtp_ssl_cls: MagicMock):
  37. # Cover SMTP_SSL branch and TimeoutError handling
  38. mock_smtp = MagicMock()
  39. mock_smtp.sendmail.side_effect = TimeoutError("timeout")
  40. mock_smtp_ssl_cls.return_value = mock_smtp
  41. client = SMTPClient(
  42. server="smtp.example.com",
  43. port=465,
  44. username="",
  45. password="",
  46. _from="noreply@example.com",
  47. use_tls=True,
  48. opportunistic_tls=False,
  49. )
  50. with pytest.raises(TimeoutError):
  51. client.send(_mail())
  52. mock_smtp.quit.assert_called_once()
  53. @patch("libs.smtp.smtplib.SMTP")
  54. def test_smtp_generic_exception_propagates(mock_smtp_cls: MagicMock):
  55. mock_smtp = MagicMock()
  56. mock_smtp.sendmail.side_effect = RuntimeError("oops")
  57. mock_smtp_cls.return_value = mock_smtp
  58. client = SMTPClient(server="smtp.example.com", port=25, username="", password="", _from="noreply@example.com")
  59. with pytest.raises(RuntimeError):
  60. client.send(_mail())
  61. mock_smtp.quit.assert_called_once()
  62. @patch("libs.smtp.smtplib.SMTP")
  63. def test_smtp_smtplib_exception_in_login(mock_smtp_cls: MagicMock):
  64. # Ensure we hit the specific SMTPException except branch
  65. import smtplib
  66. mock_smtp = MagicMock()
  67. mock_smtp.login.side_effect = smtplib.SMTPException("login-fail")
  68. mock_smtp_cls.return_value = mock_smtp
  69. client = SMTPClient(
  70. server="smtp.example.com",
  71. port=25,
  72. username="user", # non-empty to trigger login
  73. password="pass",
  74. _from="noreply@example.com",
  75. )
  76. with pytest.raises(smtplib.SMTPException):
  77. client.send(_mail())
  78. mock_smtp.quit.assert_called_once()