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',
[('',
- ''),
- ('[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 `` must not be converted to HTML
- (e.g., `<img ...>`). We just expect the raw string to appear in the
- outgoing email body.
+ strings like `` 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',), ('[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 -%}