1
0

test_body.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. from textwrap import dedent
  2. import sys
  3. from redmail import EmailSender
  4. import pytest
  5. from convert import remove_extra_lines, payloads_to_dict
  6. from getpass import getpass, getuser
  7. from platform import node
  8. from convert import remove_email_extra, remove_email_content_id, prune_generated_headers
  9. IS_PY37 = sys.version_info < (3, 8)
  10. def test_text_message():
  11. sender = EmailSender(host=None, port=1234)
  12. if IS_PY37:
  13. # CI has FQDN that has UTF-8 chars and goes to new line
  14. # for Python <=3.7. We set a realistic looking domain
  15. # name for easier testing
  16. sender.domain = "REDMAIL-1234.mail.com"
  17. msg = sender.get_message(
  18. sender="me@example.com",
  19. receivers="you@example.com",
  20. subject="Some news",
  21. text="Hi, nice to meet you.",
  22. )
  23. msg = prune_generated_headers(str(msg))
  24. assert str(msg) == dedent("""
  25. From: me@example.com
  26. Subject: Some news
  27. To: you@example.com
  28. Message-ID: <<message_id>>
  29. Date: <date>
  30. Content-Type: text/plain; charset="utf-8"
  31. Content-Transfer-Encoding: 7bit
  32. MIME-Version: 1.0
  33. Hi, nice to meet you.
  34. """)[1:].replace('\n', '\r\n')
  35. def test_html_message():
  36. sender = EmailSender(host=None, port=1234)
  37. if IS_PY37:
  38. # CI has FQDN that has UTF-8 chars and goes to new line
  39. # for Python <=3.7. We set a realistic looking domain
  40. # name for easier testing
  41. sender.domain = "REDMAIL-1234.mail.com"
  42. msg = sender.get_message(
  43. sender="me@example.com",
  44. receivers="you@example.com",
  45. subject="Some news",
  46. html="<h3>Hi,</h3><p>Nice to meet you</p>",
  47. )
  48. msg = prune_generated_headers(str(msg))
  49. assert remove_email_content_id(str(msg)) == dedent("""
  50. From: me@example.com
  51. Subject: Some news
  52. To: you@example.com
  53. Message-ID: <<message_id>>
  54. Date: <date>
  55. Content-Type: multipart/mixed; boundary="===============<ID>=="
  56. --===============<ID>==
  57. Content-Type: multipart/alternative;
  58. boundary="===============<ID>=="
  59. --===============<ID>==
  60. Content-Type: text/html; charset="utf-8"
  61. Content-Transfer-Encoding: 7bit
  62. MIME-Version: 1.0
  63. <h3>Hi,</h3><p>Nice to meet you</p>
  64. --===============<ID>==--
  65. --===============<ID>==--
  66. """)[1:].replace('\n', '\r\n')
  67. def test_text_and_html_message():
  68. sender = EmailSender(host=None, port=1234)
  69. if IS_PY37:
  70. # CI has FQDN that has UTF-8 chars and goes to new line
  71. # for Python <=3.7. We set a realistic looking domain
  72. # name for easier testing
  73. sender.domain = "REDMAIL-1234.mail.com"
  74. msg = sender.get_message(
  75. sender="me@example.com",
  76. receivers="you@example.com",
  77. subject="Some news",
  78. html="<h3>Hi,</h3><p>nice to meet you.</p>",
  79. text="Hi, nice to meet you.",
  80. )
  81. msg = prune_generated_headers(str(msg))
  82. assert remove_email_content_id(str(msg)) == dedent("""
  83. From: me@example.com
  84. Subject: Some news
  85. To: you@example.com
  86. Message-ID: <<message_id>>
  87. Date: <date>
  88. MIME-Version: 1.0
  89. Content-Type: multipart/mixed; boundary="===============<ID>=="
  90. --===============<ID>==
  91. Content-Type: multipart/alternative;
  92. boundary="===============<ID>=="
  93. --===============<ID>==
  94. Content-Type: text/plain; charset="utf-8"
  95. Content-Transfer-Encoding: 7bit
  96. Hi, nice to meet you.
  97. --===============<ID>==
  98. Content-Type: text/html; charset="utf-8"
  99. Content-Transfer-Encoding: 7bit
  100. MIME-Version: 1.0
  101. <h3>Hi,</h3><p>nice to meet you.</p>
  102. --===============<ID>==--
  103. --===============<ID>==--
  104. """)[1:].replace('\n', '\r\n')
  105. @pytest.mark.parametrize(
  106. "html,expected_html,text,expected_text,extra", [
  107. pytest.param(
  108. "<h3>Hi,</h3> <p>This is {{ user }} from {{ node }}. I'm really {{ sender.full_name }}.</p>",
  109. f"<h3>Hi,</h3> <p>This is {getuser()} from {node()}. I'm really Me.</p>\n",
  110. "Hi, \nThis is {{ user }} from {{ node }}. I'm really {{ sender.full_name }}.",
  111. f"Hi, \nThis is {getuser()} from {node()}. I'm really Me.\n",
  112. None,
  113. id="With default extras"
  114. ),
  115. pytest.param(
  116. "<h3>Hi {{ receiver }},</h3> <p>This is {{ user }} from {{ node }}. I'm really {{ sender.full_name }}.</p>",
  117. f"<h3>Hi you,</h3> <p>This is overridden from {node()}. I'm really Me.</p>\n",
  118. "Hi {{ receiver }}, This is {{ user }} from {{ node }}. I'm really {{ sender.full_name }}.",
  119. f"Hi you, This is overridden from {node()}. I'm really Me.\n",
  120. {"user": "overridden", "receiver": "you"},
  121. id="Custom extra"
  122. ),
  123. ]
  124. )
  125. def test_with_jinja_params(html, text, extra, expected_html, expected_text):
  126. sender = EmailSender(host=None, port=1234)
  127. msg = sender.get_message(
  128. sender="me@gmail.com",
  129. receivers="you@gmail.com",
  130. subject="Some news",
  131. text=text,
  132. html=html,
  133. body_params=extra
  134. )
  135. # Validate structure
  136. structure = payloads_to_dict(msg)
  137. assert structure == {
  138. 'multipart/mixed': {
  139. 'multipart/alternative': {
  140. 'text/plain': structure["multipart/mixed"]["multipart/alternative"]["text/plain"],
  141. 'text/html': structure["multipart/mixed"]["multipart/alternative"]["text/html"],
  142. }
  143. }
  144. }
  145. assert "multipart/mixed" == msg.get_content_type()
  146. alternative = msg.get_payload()[0]
  147. text_part, html_part = alternative.get_payload()
  148. text = remove_email_extra(text_part.get_payload())
  149. html = remove_email_extra(html_part.get_payload())
  150. assert expected_html == html
  151. assert expected_text == text
  152. @pytest.mark.parametrize("use_jinja_obj,use_jinja", [
  153. pytest.param(None, False, id="Use arg"),
  154. pytest.param(False, None, id="Use attr"),
  155. pytest.param(True, False, id="Override"),
  156. ])
  157. def test_without_jinja(use_jinja_obj, use_jinja):
  158. html = "<h3>Hi,</h3> <p>This is {{ user }} from { node }. I'm really {{ sender.full_name }}.</p>"
  159. text = "Hi, \nThis is {{ user }} from { node }. I'm really {{ sender.full_name }}."
  160. sender = EmailSender(host=None, port=1234)
  161. if IS_PY37:
  162. # CI has FQDN that has UTF-8 chars and goes to new line
  163. # for Python <=3.7. We set a realistic looking domain
  164. # name for easier testing
  165. sender.domain = "REDMAIL-1234.mail.com"
  166. sender.use_jinja = use_jinja_obj
  167. msg = sender.get_message(
  168. sender="me@example.com",
  169. receivers="you@example.com",
  170. subject="Some news",
  171. text=text,
  172. html=html,
  173. use_jinja=use_jinja,
  174. )
  175. encoding = '7bit' if IS_PY37 else 'quoted-printable'
  176. expected = dedent("""
  177. From: me@example.com
  178. Subject: Some news
  179. To: you@example.com
  180. Message-ID: <<message_id>>
  181. Date: <date>
  182. MIME-Version: 1.0
  183. Content-Type: multipart/mixed; boundary="===============<ID>=="
  184. --===============<ID>==
  185. Content-Type: multipart/alternative;
  186. boundary="===============<ID>=="
  187. --===============<ID>==
  188. Content-Type: text/plain; charset="utf-8"
  189. Content-Transfer-Encoding: 7bit
  190. Hi,
  191. This is {{ user }} from { node }. I'm really {{ sender.full_name }}.
  192. --===============<ID>==
  193. Content-Type: text/html; charset="utf-8"
  194. Content-Transfer-Encoding: """ + encoding + """
  195. MIME-Version: 1.0
  196. <h3>Hi,</h3> <p>This is {{ user }} from { node }. I'm really {{ sender.full_n=
  197. ame }}.</p>
  198. --===============<ID>==--
  199. --===============<ID>==--
  200. """)[1:].replace('\n', '\r\n')
  201. if IS_PY37:
  202. expected = expected.replace('sender.full_n=\r\n', 'sender.full_n')
  203. msg = prune_generated_headers(str(msg))
  204. assert remove_email_content_id(msg) == expected
  205. def test_with_error():
  206. sender = EmailSender(host=None, port=1234)
  207. try:
  208. raise RuntimeError("Deliberate failure")
  209. except:
  210. msg = sender.get_message(
  211. sender="me@gmail.com",
  212. receivers="you@gmail.com",
  213. subject="Some news",
  214. text="Error occurred \n{{ error }}",
  215. html="<h1>Error occurred: </h1>{{ error }}",
  216. )
  217. alternative = msg.get_payload()[0]
  218. text_part, html_part = alternative.get_payload()
  219. text = remove_email_extra(text_part.get_payload())
  220. html = remove_email_extra(html_part.get_payload())
  221. if IS_PY37:
  222. text = text.replace('Error occurred \n', 'Error occurred\n')
  223. html = html.replace('<span style="color:', '<span style=3D"color:')
  224. assert text.startswith('Error occurred\nTraceback (most recent call last):\n File "')
  225. assert text.endswith(', in test_with_error\n raise RuntimeError("Deliberate failure")\nRuntimeError: Deliberate failure\n')
  226. assert html.startswith('<h1>Error occurred: </h1>\n <div>\n <h4>Traceback (most recent call last):</h4>\n <pre><code> File &quot;')
  227. assert html.endswith(', in test_with_error\nraise RuntimeError(&quot;Deliberate failure&quot;)</code></pre>\n <span style=3D"color: red; font-weight: bold">Deliberate failure</span>: <span>RuntimeError</span>\n </div>\n')
  228. def test_set_defaults():
  229. email = EmailSender(host=None, port=1234)
  230. email.sender = 'me@gmail.com'
  231. email.receivers = ['you@gmail.com', 'they@gmail.com']
  232. email.subject = "Some email"
  233. msg = email.get_message(text="Hi, an email")
  234. headers = {
  235. key: val if key not in ('Message-ID', 'Date') else '<ID>'
  236. for key, val in msg.items()
  237. }
  238. assert {
  239. 'From': 'me@gmail.com',
  240. 'To': 'you@gmail.com, they@gmail.com',
  241. 'Subject': 'Some email',
  242. 'Content-Type': 'text/plain; charset="utf-8"',
  243. 'Content-Transfer-Encoding': '7bit',
  244. 'MIME-Version': '1.0',
  245. 'Message-ID': '<ID>',
  246. 'Date': '<ID>',
  247. } == headers
  248. def test_missing_subject():
  249. email = EmailSender(host=None, port=1234)
  250. with pytest.raises(ValueError):
  251. email.get_message(sender="me@example.com", receivers=['you@example.com'])
  252. def test_no_table_templates():
  253. email = EmailSender(host="localhost", port=0)
  254. assert email.default_html_theme == "modest.html"
  255. assert email.default_text_theme == "pandas.txt"
  256. email.default_html_theme = None
  257. email.default_text_theme = None
  258. msg = email.get_message(
  259. sender="me@gmail.com",
  260. receivers="you@gmail.com",
  261. subject="Some news",
  262. text="An example",
  263. html="<h1>An example</h1>"
  264. )
  265. headers = {
  266. key: val
  267. for key, val in msg.items()
  268. if key not in ('Message-ID', 'Date')
  269. }
  270. assert headers == {
  271. 'From': 'me@gmail.com',
  272. 'Subject': 'Some news',
  273. 'To': 'you@gmail.com',
  274. 'MIME-Version': '1.0',
  275. 'Content-Type': 'multipart/mixed',
  276. }