Changeset - 552800808386
[Not reviewed]
default
0 4 0
Mads Kiilerich - 9 years ago 2016-07-28 16:31:40
madski@unity3d.com
tests: add test coverage of PR comment @mention
4 files changed with 183 insertions and 4 deletions:
0 comments (0 inline, 0 general)
kallithea/model/comment.py
Show inline comments
 
@@ -168,96 +168,97 @@ class ChangesetCommentsModel(BaseModel):
 
        Returns the created comment.
 
        """
 
        if not status_change and not text:
 
            log.warning('Missing text for comment, skipping...')
 
            return None
 

	
 
        repo = self._get_repo(repo)
 
        user = self._get_user(user)
 
        comment = ChangesetComment()
 
        comment.repo = repo
 
        comment.author = user
 
        comment.text = text
 
        comment.f_path = f_path
 
        comment.line_no = line_no
 

	
 
        if revision is not None:
 
            comment.revision = revision
 
        elif pull_request is not None:
 
            pull_request = self.__get_pull_request(pull_request)
 
            comment.pull_request = pull_request
 
        else:
 
            raise Exception('Please specify revision or pull_request_id')
 

	
 
        Session().add(comment)
 
        Session().flush()
 

	
 
        if send_email:
 
            (subj, body, recipients, notification_type,
 
             email_kwargs) = self._get_notification_data(
 
                                repo, comment, user,
 
                                comment_text=text,
 
                                line_no=line_no,
 
                                revision=revision,
 
                                pull_request=pull_request,
 
                                status_change=status_change,
 
                                closing_pr=closing_pr)
 
            email_kwargs['is_mention'] = False
 
            # create notification objects, and emails
 
            NotificationModel().create(
 
                created_by=user, subject=subj, body=body,
 
                recipients=recipients, type_=notification_type,
 
                email_kwargs=email_kwargs,
 
            )
 

	
 
            mention_recipients = extract_mentioned_users(body).difference(recipients)
 
            if mention_recipients:
 
                email_kwargs['is_mention'] = True
 
                subj = _('[Mention]') + ' ' + subj
 
                # FIXME: this subject is wrong and unused!
 
                NotificationModel().create(
 
                    created_by=user, subject=subj, body=body,
 
                    recipients=mention_recipients,
 
                    type_=notification_type,
 
                    email_kwargs=email_kwargs
 
                )
 

	
 
        return comment
 

	
 
    def delete(self, comment):
 
        comment = self.__get_changeset_comment(comment)
 
        Session().delete(comment)
 

	
 
        return comment
 

	
 
    def get_comments(self, repo_id, revision=None, pull_request=None):
 
        """
 
        Gets general comments for either revision or pull_request.
 

	
 
        Returns a list, ordered by creation date.
 
        """
 
        return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
 
                                  inline=False)
 

	
 
    def get_inline_comments(self, repo_id, revision=None, pull_request=None):
 
        """
 
        Gets inline comments for either revision or pull_request.
 

	
 
        Returns a list of tuples with file path and list of comments per line number.
 
        """
 
        comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
 
                                      inline=True)
 

	
 
        paths = defaultdict(lambda: defaultdict(list))
 
        for co in comments:
 
            paths[co.f_path][co.line_no].append(co)
 
        return paths.items()
 

	
 
    def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False):
 
        """
 
        Gets comments for either revision or pull_request_id, either inline or general.
 
        """
 
        q = Session().query(ChangesetComment)
 

	
 
        if inline:
 
            q = q.filter(ChangesetComment.line_no != None) \
 
                .filter(ChangesetComment.f_path != None)
 
        else:
kallithea/model/pull_request.py
Show inline comments
 
@@ -140,93 +140,94 @@ class PullRequestModel(BaseModel):
 

	
 
        #notification to reviewers
 
        pr_url = pr.url(canonical=True)
 
        threading = ['%s-pr-%s@%s' % (pr.other_repo.repo_name,
 
                                      pr.pull_request_id,
 
                                      h.canonical_hostname())]
 
        subject = safe_unicode(
 
            h.link_to(
 
              _('%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s') % \
 
                {'user': user.username,
 
                 'pr_title': pr.title,
 
                 'pr_nice_id': pr.nice_id()},
 
                pr_url)
 
            )
 
        body = pr.description
 
        _org_ref_type, org_ref_name, _org_rev = pr.org_ref.split(':')
 
        _other_ref_type, other_ref_name, _other_rev = pr.other_ref.split(':')
 
        email_kwargs = {
 
            'pr_title': pr.title,
 
            'pr_user_created': user.full_name_and_username,
 
            'pr_repo_url': h.canonical_url('summary_home', repo_name=pr.other_repo.repo_name),
 
            'pr_url': pr_url,
 
            'pr_revisions': revision_data,
 
            'repo_name': pr.other_repo.repo_name,
 
            'org_repo_name': pr.org_repo.repo_name,
 
            'pr_nice_id': pr.nice_id(),
 
            'pr_target_repo': h.canonical_url('summary_home',
 
                               repo_name=pr.other_repo.repo_name),
 
            'pr_target_branch': other_ref_name,
 
            'pr_source_repo': h.canonical_url('summary_home',
 
                               repo_name=pr.org_repo.repo_name),
 
            'pr_source_branch': org_ref_name,
 
            'pr_owner': pr.owner,
 
            'pr_username': user.username,
 
            'threading': threading,
 
            'is_mention': False,
 
            }
 
        if reviewers:
 
            NotificationModel().create(created_by=user, subject=subject, body=body,
 
                                       recipients=reviewers,
 
                                       type_=Notification.TYPE_PULL_REQUEST,
 
                                       email_kwargs=email_kwargs)
 

	
 
        if mention_recipients:
 
            mention_recipients.difference_update(reviewers)
 
        if mention_recipients:
 
            email_kwargs['is_mention'] = True
 
            subject = _('[Mention]') + ' ' + subject
 
            # FIXME: this subject is wrong and unused!
 
            NotificationModel().create(created_by=user, subject=subject, body=body,
 
                                       recipients=mention_recipients,
 
                                       type_=Notification.TYPE_PULL_REQUEST,
 
                                       email_kwargs=email_kwargs)
 

	
 
    def mention_from_description(self, user, pr, old_description=''):
 
        mention_recipients = (extract_mentioned_users(pr.description) -
 
                              extract_mentioned_users(old_description))
 

	
 
        log.debug("Mentioning %s", mention_recipients)
 
        self.__add_reviewers(user, pr, set(), mention_recipients)
 

	
 
    def update_reviewers(self, user, pull_request, reviewers_ids):
 
        reviewers_ids = set(reviewers_ids)
 
        pull_request = self.__get_pull_request(pull_request)
 
        current_reviewers = PullRequestReviewers.query() \
 
            .options(joinedload('user')) \
 
            .filter_by(pull_request=pull_request) \
 
            .all()
 
        current_reviewer_users = set(x.user for x in current_reviewers)
 
        new_reviewer_users = set(self._get_valid_reviewers(reviewers_ids))
 

	
 
        to_add = new_reviewer_users - current_reviewer_users
 
        to_remove = current_reviewer_users - new_reviewer_users
 

	
 
        if not to_add and not to_remove:
 
            return # all done
 

	
 
        log.debug("Adding %s reviewers", to_add)
 
        self.__add_reviewers(user, pull_request, to_add, set())
 

	
 
        log.debug("Removing %s reviewers", to_remove)
 
        for prr in current_reviewers:
 
            if prr.user in to_remove:
 
                Session().delete(prr)
 

	
 
    def delete(self, pull_request):
 
        pull_request = self.__get_pull_request(pull_request)
 
        Session().delete(pull_request)
 

	
 
    def close_pull_request(self, pull_request):
 
        pull_request = self.__get_pull_request(pull_request)
 
        pull_request.status = PullRequest.STATUS_CLOSED
 
        pull_request.updated_on = datetime.datetime.now()
 
        Session().add(pull_request)
kallithea/tests/models/test_dump_html_mails.ref.html
Show inline comments
 
@@ -366,228 +366,404 @@ This PR is awesome because it does stuff
 

	
 
Changesets:
 
123abc123abc: http://changeset_home/?repo_name=repo_org&revision=123abc123abc123abc123abc123abc123abc123abc
 
Introduce one and two
 

	
 
and that's it
 

	
 
567fed567fed: http://changeset_home/?repo_name=repo_org&revision=567fed567fed567fed567fed567fed567fed567fed
 
Make one plus two equal tree
 

	
 

	
 

	
 
-- 
 
This is an automatic notification. Don't reply to this mail.
 

	
 
--------------------</pre>
 

	
 

	
 

	
 
<p>Requesting User (root) mentioned you on repo/name pull request &#34;The Title&#34;</p>
 

	
 
<p>URL: <a href="http://pr.org/7">http://pr.org/7</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=123abc123abc123abc123abc123abc123abc123abc">123abc123abc</a></i>:
 
Introduce one and two
 

	
 
and that&#39;s it
 

	
 
<i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=567fed567fed567fed567fed567fed567fed567fed">567fed567fed</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>
 
<h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=False</h1>
 
<pre>
 

	
 
From: u1
 
To: u2@example.com
 
Subject: [Comment] repo/name pull request #7 from devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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>
 
<h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=False</h1>
 
<pre>
 

	
 
From: u1
 
To: u2@example.com
 
Subject: [Comment] repo/name pull request #7 from devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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, is_mention=False, 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 devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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>
 
<h1>pull_request_comment, is_mention=True, 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 devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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, is_mention=False, status_change=None, closing_pr=True</h1>
 
<pre>
 

	
 
From: u1
 
To: u2@example.com
 
Subject: [Closing: Comment] repo/name pull request #7 from devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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>
 
<h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=True</h1>
 
<pre>
 

	
 
From: u1
 
To: u2@example.com
 
Subject: [Closing: Comment] repo/name pull request #7 from devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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, is_mention=False, 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 devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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>pull_request_comment, is_mention=True, 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 devbranch
 

	
 
--------------------
 

	
 

	
 
Comment from Opinionated User (jsmith) 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 Opinionated User (jsmith) 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
kallithea/tests/models/test_notifications.py
Show inline comments
 
@@ -186,92 +186,93 @@ class TestNotifications(TestController):
 
                pr_nice_id='#7',
 
                pr_title='The Title',
 
                pr_url='http://pr.org/7',
 
                pr_target_repo='http://mainline.com/repo',
 
                pr_target_branch='trunk',
 
                pr_source_repo='https://dev.org/repo',
 
                pr_source_branch='devbranch',
 
                pr_owner=User.get(self.u2),
 
                )
 

	
 
            for type_, body, kwargs in [
 
                (Notification.TYPE_CHANGESET_COMMENT,
 
                 u'This is the new comment.\n\n - and here it ends indented.',
 
                 dict(
 
                    short_id='cafe1234',
 
                    raw_id='cafe1234c0ffeecafe',
 
                    branch='brunch',
 
                    cs_comment_user='Opinionated User (jsmith)',
 
                    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',
 
                    cs_author=User.get(self.u2))),
 
                (Notification.TYPE_MESSAGE,
 
                 u'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,
 
                 u'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,
 
                 u'This PR is awesome because it does stuff\n - please approve indented!',
 
                 dict(
 
                    pr_user_created='Requesting User (root)', # pr_owner should perhaps be used for @mention in description ...
 
                    is_mention=[False, True],
 
                    pr_revisions=[('123abc'*7, "Introduce one and two\n\nand that's it"), ('567fed'*7, 'Make one plus two equal tree')],
 
                    org_repo_name='repo_org',
 
                    **pr_kwargs)),
 
                (Notification.TYPE_PULL_REQUEST_COMMENT,
 
                 u'Me too!\n\n - and indented on second line',
 
                 dict(
 
                    closing_pr=[False, True],
 
                    is_mention=[False, True],
 
                    pr_comment_user='Opinionated User (jsmith)',
 
                    pr_comment_url='http://pr.org/comment',
 
                    status_change=[None, 'Under Review'],
 
                    **pr_kwargs)),
 
                ]:
 
                kwargs['repo_name'] = u'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=u'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)
0 comments (0 inline, 0 general)