瀏覽代碼

test: fix tests to follow new email structure

Mikael Koli 3 年之前
父節點
當前提交
ce1a524025

+ 42 - 5
redmail/test/email/test_attachments.py

@@ -7,7 +7,7 @@ from pathlib import Path
 import pytest
 
 from resources import get_mpl_fig, get_pil_image
-from convert import remove_extra_lines
+from convert import remove_extra_lines, payloads_to_dict
 
 def to_encoded(s:str):
     return str(base64.b64encode(s.encode()), 'ascii')
@@ -23,6 +23,14 @@ def test_with_text():
         text="Hi, this is an email",
         attachments={'data.txt': "Some content"}
     )
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'application/octet-stream': 'U29tZSBjb250ZW50\n',
+            'text/plain': 'Hi, this is an email\n',
+        }
+    }
+
     assert msg.get_content_type() == "multipart/mixed"
 
     text, attachment = msg.get_payload()
@@ -45,9 +53,20 @@ def test_with_html():
         html="<h1>Hi, this is an email.</h1>",
         attachments={'data.txt': "Some content"}
     )
-    assert msg.get_content_type() == "multipart/alternative"
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'application/octet-stream': 'U29tZSBjb250ZW50\n',
+            'multipart/alternative': {
+                'text/html': '<h1>Hi, this is an email.</h1>\n'
+            }
+        }
+    }
+
+    assert msg.get_content_type() == "multipart/mixed"
 
-    html, attachment = msg.get_payload()
+    alternative, attachment = msg.get_payload()
+    html = alternative.get_payload()[0]
     assert html.get_content_type() == 'text/html'
     assert attachment.get_content_type() == 'application/octet-stream'
 
@@ -70,9 +89,20 @@ def test_with_text_and_html():
         html="<h1>Hi, this is an email.</h1>",
         attachments={'data.txt': "Some content"}
     )
-    assert msg.get_content_type() == "multipart/alternative"
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'application/octet-stream': 'U29tZSBjb250ZW50\n',
+            'multipart/alternative': {
+                'text/html': '<h1>Hi, this is an email.</h1>\n',
+                'text/plain': 'Hi, this is an email.\n'
+            }
+        }
+    }
+    assert msg.get_content_type() == "multipart/mixed"
 
-    text, html, attachment = msg.get_payload()
+    alternative, attachment = msg.get_payload()
+    text, html = alternative.get_payload()
     assert text.get_content_type() == "text/plain"
     assert html.get_content_type() == "text/html"
     assert attachment.get_content_type() == 'application/octet-stream'
@@ -94,6 +124,13 @@ def test_no_body():
         subject="Some news",
         attachments={'data.txt': 'Some content'}
     )
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'application/octet-stream': 'U29tZSBjb250ZW50\n',
+        }
+    }
+
     assert msg.get_content_type() == "multipart/mixed"
     assert len(msg.get_payload()) == 1
 

+ 81 - 34
redmail/test/email/test_body.py

@@ -2,93 +2,123 @@ from redmail import EmailSender
 
 import pytest
 
-from convert import remove_extra_lines
+from convert import remove_extra_lines, payloads_to_dict
 from getpass import getpass, getuser
 from platform import node
 
 from convert import remove_email_extra
 
 def test_text_message():
-    text = "Hi, nice to meet you."
 
     sender = EmailSender(host=None, port=1234)
     msg = sender.get_message(
         sender="me@gmail.com",
         receivers="you@gmail.com",
         subject="Some news",
-        text=text,
+        text="Hi, nice to meet you.",
     )
-    payload = msg.get_payload()
+
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'text/plain': 'Hi, nice to meet you.\n',
+        }
+    }
+    assert msg.get_content_type() == "multipart/mixed"
+
+    text = msg.get_payload()[0]
+
     expected_headers = {
         'from': 'me@gmail.com', 
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         'MIME-Version': '1.0', 
-        'Content-Type': 'text/plain; charset="utf-8"',
-        'Content-Transfer-Encoding': '7bit',
+        'Content-Type': 'multipart/mixed',
+        #'Content-Transfer-Encoding': '7bit',
     }
 
-    assert "text/plain" == msg.get_content_type()
-    assert text + "\n" == payload
+    assert "text/plain" == text.get_content_type()
+    assert "Hi, nice to meet you.\n" == text.get_payload()
 
     # Test receivers etc.
     headers = dict(msg.items())
     assert expected_headers == headers
 
 def test_html_message():
-    html = "<h3>Hi,</h3><p>Nice to meet you</p>"
 
     sender = EmailSender(host=None, port=1234)
     msg = sender.get_message(
         sender="me@gmail.com",
         receivers="you@gmail.com",
         subject="Some news",
-        html=html,
+        html="<h3>Hi,</h3><p>Nice to meet you</p>",
     )
-    payload = msg.get_payload()
+
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'multipart/alternative': {
+                'text/html': '<h3>Hi,</h3><p>Nice to meet you</p>\n',
+            }
+        }
+    }
+    assert msg.get_content_type() == "multipart/mixed"
+
+    alternative = msg.get_payload()[0]
+    html = alternative.get_payload()[0]
     expected_headers = {
         'from': 'me@gmail.com', 
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         #'MIME-Version': '1.0', 
-        'Content-Type': 'multipart/alternative'
+        'Content-Type': 'multipart/mixed'
     }
 
-    assert "multipart/alternative" == msg.get_content_type()
-    assert html + "\n" == payload[0].get_content()
+    assert "<h3>Hi,</h3><p>Nice to meet you</p>\n" == html.get_content()
 
     # Test receivers etc.
     headers = dict(msg.items())
     assert expected_headers == headers
 
 def test_text_and_html_message():
-    html = "<h3>Hi,</h3><p>nice to meet you.</p>"
-    text = "Hi, nice to meet you."
 
     sender = EmailSender(host=None, port=1234)
     msg = sender.get_message(
         sender="me@gmail.com",
         receivers="you@gmail.com",
         subject="Some news",
-        html=html,
-        text=text,
+        html="<h3>Hi,</h3><p>nice to meet you.</p>",
+        text="Hi, nice to meet you.",
     )
-    payload = msg.get_payload()
+
+    # Validate structure
+    assert payloads_to_dict(msg) == {
+        'multipart/mixed': {
+            'multipart/alternative': {
+                'text/plain': 'Hi, nice to meet you.\n',
+                'text/html': '<h3>Hi,</h3><p>nice to meet you.</p>\n',
+            }
+        }
+    }
+
+    alternative = msg.get_payload()[0]
+    text, html = alternative.get_payload()
+
     expected_headers = {
         'from': 'me@gmail.com', 
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         'MIME-Version': '1.0', 
-        'Content-Type': 'multipart/alternative'
+        'Content-Type': 'multipart/mixed'
     }
 
-    assert "multipart/alternative" == msg.get_content_type()
+    assert "multipart/mixed" == msg.get_content_type()
 
-    assert "text/plain" == payload[0].get_content_type()
-    assert text + "\n" == payload[0].get_content()
+    assert "text/plain" == text.get_content_type()
+    assert "Hi, nice to meet you.\n" == text.get_content()
 
-    assert "text/html" == payload[1].get_content_type()
-    assert html + "\n" == payload[1].get_content()
+    assert "text/html" == html.get_content_type()
+    assert "<h3>Hi,</h3><p>nice to meet you.</p>\n" == html.get_content()
 
     # Test receivers etc.
     headers = dict(msg.items())
@@ -130,10 +160,23 @@ def test_with_jinja_params(html, text, extra, expected_html, expected_text):
         body_params=extra
     )
     
-    assert "multipart/alternative" == msg.get_content_type()
+    # Validate structure
+    structure = payloads_to_dict(msg)
+    assert structure == {
+        'multipart/mixed': {
+            'multipart/alternative': {
+                'text/plain': structure["multipart/mixed"]["multipart/alternative"]["text/plain"],
+                'text/html': structure["multipart/mixed"]["multipart/alternative"]["text/html"],
+            }
+        }
+    }
 
-    text = remove_email_extra(msg.get_payload()[0].get_payload())
-    html = remove_email_extra(msg.get_payload()[1].get_payload())
+    assert "multipart/mixed" == msg.get_content_type()
+    alternative = msg.get_payload()[0]
+    text_part, html_part = alternative.get_payload()
+    
+    text = remove_email_extra(text_part.get_payload())
+    html = remove_email_extra(html_part.get_payload())
 
     assert expected_html == html
     assert expected_text == text
@@ -150,8 +193,12 @@ def test_with_error():
             text="Error occurred \n{{ error }}",
             html="<h1>Error occurred: </h1>{{ error }}",
         )
-    text = remove_email_extra(msg.get_payload()[0].get_payload())
-    html = remove_email_extra(msg.get_payload()[1].get_payload())
+
+    alternative = msg.get_payload()[0]
+    text_part, html_part = alternative.get_payload()
+
+    text = remove_email_extra(text_part.get_payload())
+    html = remove_email_extra(html_part.get_payload())
 
     assert text.startswith('Error occurred\nTraceback (most recent call last):\n  File "')
     assert text.endswith(', in test_with_error\n    raise RuntimeError("Deliberate failure")\nRuntimeError: Deliberate failure\n')
@@ -169,15 +216,15 @@ def test_set_defaults():
         'from': 'me@gmail.com', 
         'to': 'you@gmail.com, they@gmail.com', 
         'subject': 'Some email', 
-        'Content-Type': 'text/plain; charset="utf-8"', 
-        'Content-Transfer-Encoding': '7bit', 
+        'Content-Type': 'multipart/mixed', 
+        #'Content-Transfer-Encoding': '7bit', 
         'MIME-Version': '1.0'
     } == dict(msg.items())
 
 def test_cc_bcc():
     email = EmailSender(host=None, port=1234)
     msg = email.get_message(sender="me@example.com", subject="Something", cc=['you@example.com'], bcc=['he@example.com', 'she@example.com'])
-    assert dict(msg.items()) == {'from': 'me@example.com', 'subject': 'Something', 'cc': 'you@example.com', 'bcc': 'he@example.com, she@example.com'}
+    assert dict(msg.items()) == {'from': 'me@example.com', 'subject': 'Something', 'cc': 'you@example.com', 'bcc': 'he@example.com, she@example.com', 'Content-Type': 'multipart/mixed'}
 
 def test_missing_subject():
     email = EmailSender(host=None, port=1234)
@@ -205,5 +252,5 @@ def test_no_table_templates():
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         'MIME-Version': '1.0', 
-        'Content-Type': 'multipart/alternative',
+        'Content-Type': 'multipart/mixed',
     }

+ 2 - 1
redmail/test/email/test_cookbook.py

@@ -41,5 +41,6 @@ def test_distributions():
         'from': 'me@example.com', 
         'subject': 'Some email', 
         'to': 'me@example.com, you@example.com', 
-        'cc': 'he@example.com, she@example.com', 
+        'cc': 'he@example.com, she@example.com',
+        'Content-Type': 'multipart/mixed'
     }

+ 36 - 19
redmail/test/email/test_inline_media.py

@@ -12,7 +12,7 @@ import pytest
 from redmail.email.utils import pd
 
 from resources import get_mpl_fig, get_pil_image
-from convert import remove_extra_lines
+from convert import remove_extra_lines, payloads_to_dict
 
 
 def compare_image_mime(mime_part, mime_part_html, orig_image:bytes, type_="image/png"):
@@ -52,11 +52,12 @@ def test_with_image_file(get_image_obj, dummy_png):
         body_images={"my_image": image_obj}
     )
     
-    assert "multipart/alternative" == msg.get_content_type()
+    assert "multipart/mixed" == msg.get_content_type()
 
-    #mime_text = msg.get_payload()[0]
-    mime_html = msg.get_payload()[0].get_payload()[0]
-    mime_image  = msg.get_payload()[0].get_payload()[1]
+    alternative = msg.get_payload()[0]
+    related = alternative.get_payload()[0]
+
+    mime_html, mime_image = related.get_payload()
 
     compare_image_mime(mime_image, mime_html, orig_image=dummy_bytes)
 
@@ -67,7 +68,7 @@ def test_with_image_file(get_image_obj, dummy_png):
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         #'MIME-Version': '1.0', 
-        'Content-Type': 'multipart/alternative'
+        'Content-Type': 'multipart/mixed'
     } == headers
 
 def test_with_image_dict_jpeg():
@@ -87,12 +88,24 @@ def test_with_image_dict_jpeg():
             }
         }
     )
-    
-    assert "multipart/alternative" == msg.get_content_type()
+    # Validate structure
+    structure = payloads_to_dict(msg)
+    assert structure == {
+        "multipart/mixed": {
+            "multipart/alternative": {
+                "multipart/related": {
+                    "text/html": structure["multipart/mixed"]["multipart/alternative"]["multipart/related"]["text/html"],
+                    "image/jpg": structure["multipart/mixed"]["multipart/alternative"]["multipart/related"]["image/jpg"],
+                }
+            }
+        }
+    }
+    assert "multipart/mixed" == msg.get_content_type()
 
-    #mime_text = msg.get_payload()[0]
-    mime_html = msg.get_payload()[0].get_payload()[0]
-    mime_image  = msg.get_payload()[0].get_payload()[1]
+    alternative = msg.get_payload()[0]
+    related = alternative.get_payload()[0]
+
+    mime_html, mime_image = related.get_payload()
 
     compare_image_mime(mime_image, mime_html, orig_image=img_bytes, type_="image/jpg")
 
@@ -103,7 +116,7 @@ def test_with_image_dict_jpeg():
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         #'MIME-Version': '1.0', 
-        'Content-Type': 'multipart/alternative'
+        'Content-Type': 'multipart/mixed'
     } == headers
 
 
@@ -125,11 +138,12 @@ def test_with_image_obj(get_image_obj):
         body_images={"my_image": image_obj}
     )
     
-    assert "multipart/alternative" == msg.get_content_type()
+    assert "multipart/mixed" == msg.get_content_type()
 
-    #mime_text = msg.get_payload()[0]
-    mime_html = msg.get_payload()[0].get_payload()[0]
-    mime_image  = msg.get_payload()[0].get_payload()[1]
+    alternative = msg.get_payload()[0]
+    related = alternative.get_payload()[0]
+
+    mime_html, mime_image = related.get_payload()
 
     compare_image_mime(mime_image, mime_html, orig_image=image_bytes)
 
@@ -140,7 +154,7 @@ def test_with_image_obj(get_image_obj):
         'subject': 'Some news', 
         'to': 'you@gmail.com', 
         #'MIME-Version': '1.0', 
-        'Content-Type': 'multipart/alternative'
+        'Content-Type': 'multipart/mixed'
     } == headers
 
 def test_with_image_error():
@@ -235,10 +249,13 @@ def test_with_html_table_no_error(get_df, tmpdir):
         body_tables={"my_table": df}
     )
     
-    assert "multipart/alternative" == msg.get_content_type()
+    assert "multipart/mixed" == msg.get_content_type()
+
+    alternative = msg.get_payload()[0]
+    mime_html = alternative.get_payload()[0]
 
     #mime_text = msg.get_payload()[0]
-    html = remove_extra_lines(msg.get_payload()[0].get_payload()).replace("=20", "").replace('"3D', "")
+    html = remove_extra_lines(mime_html.get_payload()).replace("=20", "").replace('"3D', "")
     #tmpdir.join("email.html").write(html)
 
     # TODO: Test the HTML is as required

+ 6 - 4
redmail/test/email/test_template.py

@@ -38,11 +38,13 @@ def test_template(tmpdir):
         body_params={"friend": "Jack", 'project_name': 'RedMail'}
     )
     
-    assert "multipart/alternative" == msg.get_content_type()
+    assert "multipart/mixed" == msg.get_content_type()
 
-    #text = remove_extra_lines(msg.get_payload()[0].get_payload())
-    text = remove_email_extra(msg.get_payload()[0].get_payload())
-    html = remove_email_extra(msg.get_payload()[1].get_payload())
+    alternative = msg.get_payload()[0]
+    text_part, html_part = alternative.get_payload()
+
+    text = remove_email_extra(text_part.get_payload())
+    html = remove_email_extra(html_part.get_payload())
 
     assert expected_html == html
     assert expected_text == text

+ 21 - 1
redmail/test/helpers/convert.py

@@ -1,4 +1,5 @@
 
+from collections import Counter
 import re
 
 def remove_extra_lines(s:str):
@@ -7,4 +8,23 @@ def remove_extra_lines(s:str):
 
 def remove_email_extra(s:str):
     s = remove_extra_lines(s)
-    return s.replace("=20", "").replace('"3D', "").replace("=\n", "")
+    return s.replace("=20", "").replace('"3D', "").replace("=\n", "")
+
+def payloads_to_dict(*parts):
+    data = {}
+    for part in parts:
+
+        payload = part.get_payload()
+        key = part.get_content_type()
+        if key in data:
+            new_key = key
+            n = 0
+            while new_key in data:
+                n += 1
+                new_key = key + f"_{n}"
+            key = new_key
+        if isinstance(payload, str):
+            data[key] = payload
+        else:
+            data[key] = payloads_to_dict(*payload)
+    return data

+ 48 - 22
redmail/test/log/test_handler.py

@@ -4,6 +4,8 @@ from redmail import EmailSender
 from redmail import EmailHandler
 import logging
 
+from convert import payloads_to_dict
+
 def _create_dummy_send(messages:list):
     def _dummy_send(msg):
         messages.append(msg)
@@ -28,11 +30,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'a message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'a message\n'
+                }
+            },
             id="Minimal",
         ),
         pytest.param(
@@ -48,11 +54,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'Log Record: \n_test: INFO: a message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'Log Record: \n_test: INFO: a message\n'
+                }
+            },
             id="Custom message (msg)",
         ),
         pytest.param(
@@ -68,11 +78,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'Log Record: \na message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'Log Record: \na message\n'
+                }
+            },
             id="Custom message (record)",
         ),
         pytest.param(
@@ -86,11 +100,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "Log: _test - INFO",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'a message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'a message\n'
+                }
+            },
             id="Sender with fomatted subject",
         ),
         pytest.param(
@@ -106,9 +124,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Type': 'multipart/alternative',
+                'Content-Type': 'multipart/mixed',
+            },
+            {
+                'multipart/mixed': {
+                    'multipart/alternative': {
+                        'text/html': "<h1>INFO</h1><p>_test: INFO: a message</p>\n"
+                    }
+                }
             },
-            ["<h1>INFO</h1><p>_test: INFO: a message</p>\n"],
             id="Custom message (HTML, msg)",
         ),
         pytest.param(
@@ -124,9 +148,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Type': 'multipart/alternative',
+                'Content-Type': 'multipart/mixed',
+            },
+            {
+                'multipart/mixed': {
+                    'multipart/alternative': {
+                        'text/html': "<h1>INFO</h1><p>a message</p>\n"
+                    }
+                }
             },
-            ["<h1>INFO</h1><p>a message</p>\n"],
             id="Custom message (HTML, record)",
         ),
     ]
@@ -150,9 +180,5 @@ def test_emit(logger, kwargs, exp_headers, exp_payload):
 
     assert headers == exp_headers
 
-    if isinstance(payload, str):
-        assert payload == exp_payload
-    else:
-        # HTML (and text) of payloads
-        payloads = [pl.get_payload() for pl in payload]
-        assert payloads == exp_payload
+    structure = payloads_to_dict(msg)
+    assert structure == exp_payload

+ 58 - 30
redmail/test/log/test_handler_multi.py

@@ -4,6 +4,8 @@ from redmail import EmailSender
 from redmail import MultiEmailHandler
 import logging
 
+from convert import payloads_to_dict
+
 def _create_dummy_send(messages:list):
     def _dummy_send(msg):
         messages.append(msg)
@@ -28,11 +30,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'Log Recods:\na message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'Log Recods:\na message\n'
+                }
+            },
             id="Minimal",
         ),
         pytest.param(
@@ -48,11 +54,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'The records: \nLog: _test - INFO - a message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'The records: \nLog: _test - INFO - a message\n'
+                }
+            },
             id="Custom message (msgs)",
         ),
         pytest.param(
@@ -68,11 +78,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'The records: \nLog: INFO - a message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'The records: \nLog: INFO - a message\n',
+                }
+            },
             id="Custom message (records)",
         ),
         pytest.param(
@@ -86,11 +100,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "Logs: INFO - INFO",
-                'Content-Transfer-Encoding': '7bit',
-                'Content-Type': 'text/plain; charset="utf-8"',
+                #'Content-Transfer-Encoding': '7bit',
+                'Content-Type': 'multipart/mixed',
                 'MIME-Version': '1.0',
             },
-            'Log Recods:\na message\n',
+            {
+                'multipart/mixed': {
+                    'text/plain': 'Log Recods:\na message\n',
+                }
+            },
             id="Sender with fomatted subject",
         ),
         pytest.param(
@@ -106,9 +124,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Type': 'multipart/alternative',
+                'Content-Type': 'multipart/mixed',
+            },
+            {
+                'multipart/mixed': {
+                    'multipart/alternative': {
+                        'text/html': "<h1>The records:</h1><p>Log: _test - INFO - a message</p>\n",
+                    }
+                }
             },
-            ["<h1>The records:</h1><p>Log: _test - INFO - a message</p>\n"],
             id="Custom message (HTML, msgs)",
         ),
         pytest.param(
@@ -124,9 +148,15 @@ def test_default_body():
                 "from": "me@example.com",
                 "to": "he@example.com, she@example.com",
                 "subject": "A log record",
-                'Content-Type': 'multipart/alternative',
+                'Content-Type': 'multipart/mixed',
+            },
+            {
+                'multipart/mixed': {
+                    'multipart/alternative': {
+                        'text/html': "<h1>The records:</h1><p>Log: INFO - a message</p>\n",
+                    }
+                }
             },
-            ["<h1>The records:</h1><p>Log: INFO - a message</p>\n"],
             id="Custom message (HTML, records)",
         ),
     ]
@@ -152,12 +182,8 @@ def test_emit(logger, kwargs, exp_headers, exp_payload):
 
     assert headers == exp_headers
 
-    if isinstance(payload, str):
-        assert payload == exp_payload
-    else:
-        # HTML (and text) of payloads
-        payloads = [pl.get_payload() for pl in payload]
-        assert payloads == exp_payload
+    structure = payloads_to_dict(msg)
+    assert structure == exp_payload
 
 
 def test_flush_multiple(logger):
@@ -181,18 +207,19 @@ def test_flush_multiple(logger):
     assert len(msgs) == 1
     msg = msgs[0]
     headers = dict(msg.items())
-    payload = msg.get_payload()
+    payload = msg.get_payload()[0]
+    text = payload.get_payload()
 
     assert headers == {
         "from": "None",
         "to": "he@example.com, she@example.com",
         "subject": "Logs: DEBUG - INFO",
-        'Content-Transfer-Encoding': '7bit',
-        'Content-Type': 'text/plain; charset="utf-8"',
+        #'Content-Transfer-Encoding': '7bit',
+        'Content-Type': 'multipart/mixed',
         'MIME-Version': '1.0',
     }
 
-    assert payload == "Records: \nINFO - an info\nDEBUG - a debug\n"
+    assert text == "Records: \nINFO - an info\nDEBUG - a debug\n"
 
 def test_flush_none():
     msgs = []
@@ -213,15 +240,16 @@ def test_flush_none():
     assert len(msgs) == 1
     msg = msgs[0]
     headers = dict(msg.items())
-    payload = msg.get_payload()
+    payload = msg.get_payload()[0]
+    text = payload.get_payload()
 
     assert headers == {
         "from": "None",
         "to": "he@example.com, she@example.com",
         "subject": "Logs: NOTSET - NOTSET",
-        'Content-Transfer-Encoding': '7bit',
-        'Content-Type': 'text/plain; charset="utf-8"',
+        #'Content-Transfer-Encoding': '7bit',
+        'Content-Type': 'multipart/mixed',
         'MIME-Version': '1.0',
     }
 
-    assert payload == "Records: \n"
+    assert text == "Records: \n"