You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@allura.apache.org by gc...@apache.org on 2026/03/19 22:37:31 UTC

(allura) branch gc/8596 updated: fixup! [#8596] add test for email notification that send markdown content

This is an automated email from the ASF dual-hosted git repository.

gcruz pushed a commit to branch gc/8596
in repository https://gitbox.apache.org/repos/asf/allura.git


The following commit(s) were added to refs/heads/gc/8596 by this push:
     new ed32cbaf2 fixup! [#8596] add test for email notification that send markdown content
ed32cbaf2 is described below

commit ed32cbaf2e498a79e3beec74337256a55cb9d2d4
Author: Guillermo Cruz <gu...@slashdotmedia.com>
AuthorDate: Thu Mar 19 16:35:56 2026 -0600

    fixup! [#8596] add test for email notification that send markdown content
---
 .../templates/repo/merge_request_changed.html      |  2 +-
 Allura/allura/tests/model/test_notification.py     | 90 +++++++++++++++-------
 ForgeTracker/forgetracker/data/mass_report.html    |  2 +-
 ForgeTracker/forgetracker/data/ticket_changed.html |  6 +-
 4 files changed, 68 insertions(+), 32 deletions(-)

diff --git a/Allura/allura/templates/repo/merge_request_changed.html b/Allura/allura/templates/repo/merge_request_changed.html
index 479bab316..2bba7a005 100644
--- a/Allura/allura/templates/repo/merge_request_changed.html
+++ b/Allura/allura/templates/repo/merge_request_changed.html
@@ -27,6 +27,6 @@ Diff:
 ~~~~
 
 {% else %}
-- **{{ field }}**: {{ values[0] }} --> {{ values[1] }}
+- **{{ field }}**: {{ values[0] |escape_markdown }} --> {{ values[1] |escape_markdown }}
 {% endif %}
 {% endfor %}
diff --git a/Allura/allura/tests/model/test_notification.py b/Allura/allura/tests/model/test_notification.py
index 995d68f74..96b4d86c7 100644
--- a/Allura/allura/tests/model/test_notification.py
+++ b/Allura/allura/tests/model/test_notification.py
@@ -17,7 +17,7 @@
 
 from datetime import timedelta
 import collections
-
+import email.iterators
 import pytest
 from tg import tmpl_context as c, app_globals as g
 from ming.odm import ThreadLocalODMSession
@@ -30,6 +30,7 @@
 from allura.lib import helpers as h
 from allura.tests import decorators as td
 from forgewiki import model as WM
+from forgetracker import model as TM
 
 
 class TestNotification:
@@ -286,40 +287,29 @@ def test_footer(self):
 
     @pytest.mark.parametrize('markdown, result',
                              [('![img](https://static.vecteezy.com/system/resources/thumbnails/012/042/301/small_2x/warning-sign-icon-transparent-background-free-png.png)',
-                               '![img](https://static.vecteezy.com/system/resources/thumbnails/012/042/301/small_2x/warning-sign-icon-transparent-background-free-png.png)'),
-                                 ('[link text](https://example.com)', '[link text](https://example.com)'),
-                                 # make sure no errors thrown
+                               'src="https://static.vecteezy.com/system/'),
+                                 ('[link text](https://example.com)', 'href="https://example.com"'),
                              ])
     def test_email_markdown_not_parsed(self, markdown, result):
         """
-        strings like `![img](url)` must not be converted to HTML
-        (e.g., `<img ...>`). We just expect the raw string to appear in the
-        outgoing email body.
+        strings like `![img](url)` in Wiki notification body should be converted to HTML
+        (e.g., `<img ...>`).
         """
         self._subscribe()  # current user: test-admin
-        self._post_notification(text=markdown, subject=markdown)
-        ThreadLocalODMSession.flush_all()
-
+        self._post_notification(text=markdown, subject=markdown, link='https://example.com')
         M.MonQTask.run_ready()
         ThreadLocalODMSession.flush_all()
 
-        email_task = M.MonQTask.query.get(
-            task_name='allura.tasks.mail_tasks.sendmail',
-            state='ready')
-        assert email_task is not None
-        body = email_task.kwargs['text']
-        subject = email_task.kwargs['subject']
-        # no markdown rendering in email
-        assert '<img' not in body
-        assert '<a ' not in body
-        assert markdown in body
-
-        # subject should also not render html/markdown
-        assert '<img' not in subject
-        assert '<a ' not in subject
-        assert '<' not in subject
-        assert '>' not in subject
-        assert markdown in subject
+        with mock.patch('allura.tasks.mail_tasks.smtp_client.sendmail') as sendmail:
+            while M.MonQTask.run_ready():
+                ThreadLocalODMSession.flush_all()
+                # have to run them all multiple times since one task creates another
+                pass
+
+        multipart_msg = sendmail.call_args_list[0][0][6]
+        html_body = next(email.iterators.typed_subpart_iterator(multipart_msg, 'text', 'html'))\
+            .get_payload(decode=True).decode('utf-8')
+        assert result in html_body
 
     def _subscribe(self, **kw):
         self.pg.subscribe(type='direct', **kw)
@@ -330,6 +320,52 @@ def _post_notification(self, **kwargs):
         return M.Notification.post(self.pg, 'metadata', **kwargs)
 
 
+class TestTicketNotifications(TestPostNotifications):
+
+    def setup_method(self, method):
+        super().setup_method(method)
+        self.setup_with_tools()
+
+    @td.with_tracker
+    def setup_with_tools(self):
+        setup_global_objects()
+        g.set_app('bugs')
+        ticket = TM.Ticket.new()
+        ticket.summary = 'test ticket'
+        ThreadLocalODMSession.flush_all()
+        self.pg = ticket
+        M.notification.MAILBOX_QUIESCENT = None  # disable message combining
+        while M.MonQTask.run_ready('setup'):
+            ThreadLocalODMSession.flush_all()
+
+    @pytest.mark.parametrize('markdown, rendered_snippet',
+        [('![img](https://static.vecteezy.com/system/resources/thumbnails/012/042/301/small_2x/warning-sign-icon-transparent-background-free-png.png)', '<img',), ('[link text](https://example.com)', '<a',), ])
+    def test_ticket_email_markdown_not_parsed(self, markdown, rendered_snippet):
+        self._subscribe()
+        self.pg.summary = markdown  # should not render to html
+        self.pg.status = markdown  # should not render to html
+        self.pg.ticket_num = getattr(self.pg, 'ticket_num', 1) or 1
+        self.pg.description = markdown  # should render to html
+        self.pg.app_config.options.mount_point = getattr(self.pg.app_config.options, 'mount_point', 'bugs') or 'bugs'
+        ThreadLocalODMSession.flush_all()
+        self._post_notification(text=markdown, subject=markdown, link='https://example.com')
+        while M.MonQTask.run_ready():
+            ThreadLocalODMSession.flush_all()
+
+        with mock.patch('allura.tasks.mail_tasks.smtp_client.sendmail') as sendmail:
+            while M.MonQTask.run_ready():
+                ThreadLocalODMSession.flush_all()
+            assert sendmail.call_count >= 1, "Expected at least one outbound email"
+            multipart_msg = sendmail.call_args_list[0][0][6]
+
+        html_body = next(email.iterators.typed_subpart_iterator(multipart_msg, 'text', 'html')).get_payload(
+            decode=True).decode('utf-8')
+        # Raw markdown must appear in the html body
+        assert markdown in html_body
+        # And also the rendered html should appear in the body
+        assert rendered_snippet in html_body
+
+
 class TestSubscriptionTypes:
 
     def setup_method(self, method):
diff --git a/ForgeTracker/forgetracker/data/mass_report.html b/ForgeTracker/forgetracker/data/mass_report.html
index 70fe1f705..8aac69b45 100644
--- a/ForgeTracker/forgetracker/data/mass_report.html
+++ b/ForgeTracker/forgetracker/data/mass_report.html
@@ -19,7 +19,7 @@
 {{ data.header }}
 
 {% for ticket, change_text in data.changes %}
-ticket: {{ context.app.config.options.mount_point }}:#{{ ticket.ticket_num }} {{ ticket.summary }}
+ticket: {{ context.app.config.options.mount_point }}:#{{ ticket.ticket_num }} {{ ticket.summary |escape_markdown }}
 
 {{ change_text }}
 {% endfor %}
diff --git a/ForgeTracker/forgetracker/data/ticket_changed.html b/ForgeTracker/forgetracker/data/ticket_changed.html
index 668b08dea..8b922149b 100644
--- a/ForgeTracker/forgetracker/data/ticket_changed.html
+++ b/ForgeTracker/forgetracker/data/ticket_changed.html
@@ -31,11 +31,11 @@ Diff:
 {% else -%}
 {% set oldv, newv = values -%}
 {% if key == 'assigned_to' or oldv.type_s == 'User' or newv.type_s == 'User' -%}
-- **{{key}}**: {% if oldv %}{{oldv.get_pref('display_name')}} --> {% endif %}{% if newv %}{{newv.get_pref('display_name')}}{% else %} nobody {% endif %}
+- **{{key}}**: {% if oldv %}{{oldv.get_pref('display_name') |escape_markdown }} --> {% endif %}{% if newv %}{{newv.get_pref('display_name') |escape_markdown }}{% else %} nobody {% endif %}
 {% elif key == 'labels' -%}
-- **{{key}}**: {{', '.join(oldv)}} --> {{', '.join(newv)}}
+- **{{key}}**: {{', '.join(oldv) |escape_markdown }} --> {{', '.join(newv) |escape_markdown }}
 {% else -%}
-- **{{key}}**: {{oldv if oldv is not none else ''}} --> {{newv if newv is not none else ''}}
+- **{{key}}**: {{oldv |escape_markdown if oldv is not none else ''}} --> {{newv |escape_markdown if newv is not none else ''}}
 {% endif -%}
 {% endif -%}
 {% endfor -%}