changeset 5991:f65661179895

tests: introduce tests and reference dump for notification mails The mails are dumped to a tracked html file: * changes shows up as diffs and are easy to spot and review * all mails can easily can be investigated in a browser and checked for content and consistency The tests are mocking canonical_url because it has deep dependencies to pylons.url which requires (thread local?) environment setup that the tests doesn't have.
author Mads Kiilerich <madski@unity3d.com>
date Wed, 29 Jun 2016 16:52:07 +0200
parents 42d1e1ab41a8
children 07b022e6d988
files kallithea/model/db.py kallithea/tests/models/test_dump_html_mails.ref.html kallithea/tests/models/test_notifications.py
diffstat 3 files changed, 718 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/kallithea/model/db.py	Wed Jun 29 16:52:07 2016 +0200
+++ b/kallithea/model/db.py	Wed Jun 29 16:52:07 2016 +0200
@@ -2398,7 +2398,7 @@
 
     TYPE_CHANGESET_COMMENT = u'cs_comment'
     TYPE_MESSAGE = u'message'
-    TYPE_MENTION = u'mention'
+    TYPE_MENTION = u'mention' # not used
     TYPE_REGISTRATION = u'registration'
     TYPE_PULL_REQUEST = u'pull_request'
     TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kallithea/tests/models/test_dump_html_mails.ref.html	Wed Jun 29 16:52:07 2016 +0200
@@ -0,0 +1,629 @@
+<html><body>
+
+
+<h1>cs_comment, is_mention=False, status_change=None</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Comment] repo/name changeset cafe on brunch
+
+--------------------
+
+
+Comment from Commenter Name on repo_target changeset c0ffeecafe:
+This is the new comment.
+
+ - and here it ends indented.
+
+
+URL: http://comment.org
+
+Changeset: c0ffeecafe
+Description:
+This changeset did something clever which is hard to explain
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo_target changeset c0ffeecafe:</p>
+<p><div class="formatted-fixed">This is the new comment.
+
+ - and here it ends indented.</div></p>
+
+
+<p>URL: <a href="http://comment.org">http://comment.org</a></p>
+
+<p>Changeset: c0ffeecafe</p>
+<p>Description:<br/>
+This changeset did something clever which is hard to explain
+</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>cs_comment, is_mention=True, status_change=None</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Comment] repo/name changeset cafe on brunch
+
+--------------------
+
+
+Comment from Commenter Name on repo_target changeset c0ffeecafe mentioned you:
+This is the new comment.
+
+ - and here it ends indented.
+
+
+URL: http://comment.org
+
+Changeset: c0ffeecafe
+Description:
+This changeset did something clever which is hard to explain
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo_target changeset c0ffeecafe mentioned you:</p>
+<p><div class="formatted-fixed">This is the new comment.
+
+ - and here it ends indented.</div></p>
+
+
+<p>URL: <a href="http://comment.org">http://comment.org</a></p>
+
+<p>Changeset: c0ffeecafe</p>
+<p>Description:<br/>
+This changeset did something clever which is hard to explain
+</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>cs_comment, is_mention=False, status_change='Approved'</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Approved: Comment] repo/name changeset cafe on brunch
+
+--------------------
+
+
+Comment from Commenter Name on repo_target changeset c0ffeecafe:
+This is the new comment.
+
+ - and here it ends indented.
+
+The changeset status was changed to: Approved
+
+URL: http://comment.org
+
+Changeset: c0ffeecafe
+Description:
+This changeset did something clever which is hard to explain
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo_target changeset c0ffeecafe:</p>
+<p><div class="formatted-fixed">This is the new comment.
+
+ - and here it ends indented.</div></p>
+
+    <p>The changeset status was changed to: <b>Approved</b></p>
+
+<p>URL: <a href="http://comment.org">http://comment.org</a></p>
+
+<p>Changeset: c0ffeecafe</p>
+<p>Description:<br/>
+This changeset did something clever which is hard to explain
+</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>cs_comment, is_mention=True, status_change='Approved'</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Approved: Comment] repo/name changeset cafe on brunch
+
+--------------------
+
+
+Comment from Commenter Name on repo_target changeset c0ffeecafe mentioned you:
+This is the new comment.
+
+ - and here it ends indented.
+
+The changeset status was changed to: Approved
+
+URL: http://comment.org
+
+Changeset: c0ffeecafe
+Description:
+This changeset did something clever which is hard to explain
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo_target changeset c0ffeecafe mentioned you:</p>
+<p><div class="formatted-fixed">This is the new comment.
+
+ - and here it ends indented.</div></p>
+
+    <p>The changeset status was changed to: <b>Approved</b></p>
+
+<p>URL: <a href="http://comment.org">http://comment.org</a></p>
+
+<p>Changeset: c0ffeecafe</p>
+<p>Description:<br/>
+This changeset did something clever which is hard to explain
+</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>message</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: Test Message
+
+--------------------
+
+
+This is the body of the test message
+ - nothing interesting here except indentation.
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<div class="formatted-fixed">This is the body of the test message
+ - nothing interesting here except indentation.</div>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>registration</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: New user newbie registered
+
+--------------------
+
+
+Registration body
+
+View this user here: http://newbie.org
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<div class="formatted-fixed">Registration body</div>
+
+View this user here: <a href="http://newbie.org">http://newbie.org</a>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>pull_request, is_mention=False</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Added] repo/name pull request 7 from ref
+
+--------------------
+
+
+Requester Name requested your review of repo/name pull request "The Title"
+
+URL: http://pr.org
+
+Description:
+This PR is awesome because it does stuff
+ - please approve indented!
+
+Changesets:
+123abc: http://changeset_home/?repo_name=repo_org&amp;revision=123abc
+Introduce one and two
+
+567fed: http://changeset_home/?repo_name=repo_org&amp;revision=567fed
+Make one plus two equal tree
+
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Requester Name requested your review of repo/name pull request &#34;The Title&#34;</p>
+
+<p>URL: <a href="http://pr.org">http://pr.org</a></p>
+
+<p>Description:</p>
+<p style="white-space: pre-wrap; font-family: monospace"><div class="formatted-fixed">This PR is awesome because it does stuff
+ - please approve indented!</div></p>
+
+<p>Changesets:</p>
+<p style="white-space: pre-wrap">
+<i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=123abc">123abc</a></i>:
+Introduce one and two
+
+<i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=567fed">567fed</a></i>:
+Make one plus two equal tree
+
+</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>pull_request, is_mention=True</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Added] repo/name pull request 7 from ref
+
+--------------------
+
+
+Requester Name mentioned you on repo/name pull request "The Title"
+
+URL: http://pr.org
+
+Description:
+This PR is awesome because it does stuff
+ - please approve indented!
+
+Changesets:
+123abc: http://changeset_home/?repo_name=repo_org&amp;revision=123abc
+Introduce one and two
+
+567fed: http://changeset_home/?repo_name=repo_org&amp;revision=567fed
+Make one plus two equal tree
+
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Requester Name mentioned you on repo/name pull request &#34;The Title&#34;</p>
+
+<p>URL: <a href="http://pr.org">http://pr.org</a></p>
+
+<p>Description:</p>
+<p style="white-space: pre-wrap; font-family: monospace"><div class="formatted-fixed">This PR is awesome because it does stuff
+ - please approve indented!</div></p>
+
+<p>Changesets:</p>
+<p style="white-space: pre-wrap">
+<i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=123abc">123abc</a></i>:
+Introduce one and two
+
+<i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=567fed">567fed</a></i>:
+Make one plus two equal tree
+
+</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>pull_request_comment, status_change=None, closing_pr=False</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Comment] repo/name pull request 7 from ref
+
+--------------------
+
+
+Comment from Commenter Name on repo/name pull request "The Title":
+Me too!
+
+ - and indented on second line
+
+
+URL: http://pr.org/comment
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo/name pull request &#34;The Title&#34;:</p>
+<p><div class="formatted-fixed">Me too!
+
+ - and indented on second line</div></p>
+
+
+<p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>pull_request_comment, status_change='Under Review', closing_pr=False</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Under Review: Comment] repo/name pull request 7 from ref
+
+--------------------
+
+
+Comment from Commenter Name on repo/name pull request "The Title":
+Me too!
+
+ - and indented on second line
+
+The comment was made with status: Under Review
+
+URL: http://pr.org/comment
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo/name pull request &#34;The Title&#34;:</p>
+<p><div class="formatted-fixed">Me too!
+
+ - and indented on second line</div></p>
+
+       <p>The comment was made with status: <b>Under Review</b></p>
+
+<p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>pull_request_comment, status_change=None, closing_pr=True</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Closing: Comment] repo/name pull request 7 from ref
+
+--------------------
+
+
+Comment from Commenter Name on repo/name pull request "The Title":
+Me too!
+
+ - and indented on second line
+
+
+URL: http://pr.org/comment
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo/name pull request &#34;The Title&#34;:</p>
+<p><div class="formatted-fixed">Me too!
+
+ - and indented on second line</div></p>
+
+
+<p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>pull_request_comment, status_change='Under Review', closing_pr=True</h1>
+<pre>
+
+From: u1
+To: u2@example.com
+Subject: [Under Review, Closing: Comment] repo/name pull request 7 from ref
+
+--------------------
+
+
+Comment from Commenter Name on repo/name pull request "The Title":
+Me too!
+
+ - and indented on second line
+
+The comment closed the pull request with status: Under Review
+
+URL: http://pr.org/comment
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<p>Comment from Commenter Name on repo/name pull request &#34;The Title&#34;:</p>
+<p><div class="formatted-fixed">Me too!
+
+ - and indented on second line</div></p>
+
+       <p>The comment closed the pull request with status: <b>Under Review</b></p>
+
+<p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+
+<h1>TYPE_PASSWORD_RESET</h1>
+<pre>
+
+From: u1
+To: john@doe.com
+Subject: Password reset link
+
+--------------------
+
+
+Hello John Doe
+
+We have received a request to reset the password for your account.
+To set a new password, click the following link:
+
+http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746
+
+Should you not be able to use the link above, please type the following code into the password reset form: decbf64715098db5b0bd23eab44bd792670ab746
+
+If it weren't you who requested the password reset, just disregard this message.
+
+
+-- 
+This is an automatic notification. Don't reply to this mail.
+
+--------------------</pre>
+
+
+
+<h4>Hello John Doe</h4>
+
+<p>We have received a request to reset the password for your account.</p>
+<p>To set a new password, click the following link:</p>
+<p><a href="http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746">http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746</a></p>
+
+<p>Should you not be able to use the link above, please type the following code into the password reset form: <code>decbf64715098db5b0bd23eab44bd792670ab746</code></p>
+
+<p>If it weren&#39;t you who requested the password reset, just disregard this message.</p>
+
+
+<br/>
+<br/>
+-- <br/>
+This is an automatic notification. Don&#39;t reply to this mail.
+
+<pre>--------------------</pre>
+
+</body></html>
--- a/kallithea/tests/models/test_notifications.py	Wed Jun 29 16:52:07 2016 +0200
+++ b/kallithea/tests/models/test_notifications.py	Wed Jun 29 16:52:07 2016 +0200
@@ -1,10 +1,17 @@
+import os
+
+import mock
+import routes.util
+
 from kallithea.tests import *
-
+from kallithea.lib import helpers as h
 from kallithea.model.db import User, Notification, UserNotification
 from kallithea.model.user import UserModel
+from kallithea.model.meta import Session
+from kallithea.model.notification import NotificationModel, EmailNotificationModel
 
-from kallithea.model.meta import Session
-from kallithea.model.notification import NotificationModel
+import kallithea.lib.celerylib
+import kallithea.lib.celerylib.tasks
 
 
 class TestNotifications(TestController):
@@ -38,9 +45,16 @@
 
     def test_create_notification(self):
         usrs = [self.u1, self.u2]
-        notification = NotificationModel().create(created_by=self.u1,
-                                           subject=u'subj', body=u'hi there',
-                                           recipients=usrs)
+        def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
+            assert recipients == ['u2@example.com']
+            assert subject == 'Test Message'
+            assert body == "\n\nhi there\n\n\n-- \nThis is an automatic notification. Don't reply to this mail.\n"
+            assert '>hi there<' in html_body
+            assert author.username == 'u1'
+        with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email):
+            notification = NotificationModel().create(created_by=self.u1,
+                                               subject=u'subj', body=u'hi there',
+                                               recipients=usrs)
         Session().commit()
         u1 = User.get(self.u1)
         u2 = User.get(self.u2)
@@ -149,3 +163,71 @@
         assert NotificationModel().get_unread_cnt_for_user(self.u1) == 0
         assert NotificationModel().get_unread_cnt_for_user(self.u2) == 1
         assert NotificationModel().get_unread_cnt_for_user(self.u3) == 2
+
+    @mock.patch.object(h, 'canonical_url', (lambda arg, **kwargs: 'http://%s/?%s' % (arg, '&'.join('%s=%s' % (k, v) for (k, v) in sorted(kwargs.items())))))
+    def test_dump_html_mails(self):
+        # Exercise all notification types and dump them to one big html file
+        l = []
+
+        def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
+            l.append('\n\n<h1>%s</h1>\n' % desc) # desc is from outer scope
+            l.append('<pre>\n\n')
+            l.append('From: %s\n' % author.username)
+            l.append('To: %s\n' % ' '.join(recipients))
+            l.append('Subject: %s\n' % subject)
+            l.append('\n--------------------\n%s\n--------------------' % body)
+            l.append('</pre>\n')
+            l.append('\n%s\n' % html_body)
+            l.append('<pre>--------------------</pre>\n')
+
+        l.append('<html><body>\n')
+        with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email):
+            pr_kwargs = dict(pr_nice_id='7', ref='ref', org_repo_name='repo_org', pr_title='The Title', pr_url='http://pr.org')
+
+            for type_, body, kwargs in [
+                (Notification.TYPE_CHANGESET_COMMENT, 'This is the new comment.\n\n - and here it ends indented.', dict(short_id='cafe', raw_id='c0ffeecafe', branch='brunch', cs_comment_user='Commenter Name', cs_comment_url='http://comment.org', is_mention=[False, True], message='This changeset did something clever which is hard to explain', status_change=[None, 'Approved'], cs_target_repo='repo_target', cs_url='http://changeset.com')),
+                (Notification.TYPE_MESSAGE, 'This is the body of the test message\n - nothing interesting here except indentation.', dict()),
+                #(Notification.TYPE_MENTION, '$body', None), # not used
+                (Notification.TYPE_REGISTRATION, 'Registration body', dict(new_username='newbie', registered_user_url='http://newbie.org', new_email='new@email.com', new_full_name='New Full Name')),
+                (Notification.TYPE_PULL_REQUEST, 'This PR is awesome because it does stuff\n - please approve indented!', dict(pr_user_created='Requester Name', is_mention=[False, True], pr_revisions=[('123abc', 'Introduce one and two'), ('567fed', 'Make one plus two equal tree')], **pr_kwargs)),
+                (Notification.TYPE_PULL_REQUEST_COMMENT, 'Me too!\n\n - and indented on second line', dict(closing_pr=[False, True], pr_comment_user='Commenter Name', pr_comment_url='http://pr.org/comment', status_change=[None, 'Under Review'], pr_target_repo='http://target.com/repo', **pr_kwargs)),
+                ]:
+                kwargs['repo_name'] = 'repo/name'
+                params = [(type_, type_, body, kwargs)]
+                for param_name in ['is_mention', 'status_change', 'closing_pr']: # TODO: inline/general
+                    if not isinstance(kwargs.get(param_name), list):
+                        continue
+                    new_params = []
+                    for v in kwargs[param_name]:
+                        for desc, type_, body, kwargs in params:
+                            kwargs = dict(kwargs)
+                            kwargs[param_name] = v
+                            new_params.append(('%s, %s=%r' % (desc, param_name, v), type_, body, kwargs))
+                    params = new_params
+
+                for desc, type_, body, kwargs in params:
+                    # desc is used as "global" variable
+                    notification = NotificationModel().create(created_by=self.u1,
+                                                       subject='unused', body=body, email_kwargs=kwargs,
+                                                       recipients=[self.u2], type_=type_)
+
+            # Email type TYPE_PASSWORD_RESET has no corresponding notification type - test it directly:
+            desc = 'TYPE_PASSWORD_RESET'
+            kwargs = dict(user='John Doe', reset_token='decbf64715098db5b0bd23eab44bd792670ab746', reset_url='http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746')
+            kallithea.lib.celerylib.run_task(kallithea.lib.celerylib.tasks.send_email, ['john@doe.com'],
+                "Password reset link",
+                EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'txt', **kwargs),
+                EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'html', **kwargs),
+                author=User.get(self.u1))
+
+        l.append('\n</body></html>\n')
+        out = ''.join(l)
+
+        outfn = os.path.join(os.path.dirname(__file__), 'test_dump_html_mails.out.html')
+        reffn = os.path.join(os.path.dirname(__file__), 'test_dump_html_mails.ref.html')
+        with file(outfn, 'w') as f:
+            f.write(out)
+        with file(reffn) as f:
+            ref = f.read()
+        assert ref == out # copy test_dump_html_mails.out.html to test_dump_html_mails.ref.html to update expectations
+        os.unlink(outfn)