##// END OF EJS Templates
tests: add test coverage of PR comment @mention
Mads Kiilerich -
r6020:55280080 default
parent child Browse files
Show More
@@ -1,276 +1,277 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.model.comment
16 16 ~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 comments model for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Nov 11, 2011
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import logging
29 29
30 30 from pylons.i18n.translation import _
31 31 from collections import defaultdict
32 32
33 33 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
34 34 from kallithea.lib import helpers as h
35 35 from kallithea.model import BaseModel
36 36 from kallithea.model.db import ChangesetComment, User, \
37 37 Notification, PullRequest
38 38 from kallithea.model.notification import NotificationModel
39 39 from kallithea.model.meta import Session
40 40
41 41 log = logging.getLogger(__name__)
42 42
43 43
44 44 class ChangesetCommentsModel(BaseModel):
45 45
46 46 cls = ChangesetComment
47 47
48 48 def __get_changeset_comment(self, changeset_comment):
49 49 return self._get_instance(ChangesetComment, changeset_comment)
50 50
51 51 def __get_pull_request(self, pull_request):
52 52 return self._get_instance(PullRequest, pull_request)
53 53
54 54 def _get_notification_data(self, repo, comment, user, comment_text,
55 55 line_no=None, revision=None, pull_request=None,
56 56 status_change=None, closing_pr=False):
57 57 """
58 58 :returns: tuple (subj,body,recipients,notification_type,email_kwargs)
59 59 """
60 60 # make notification
61 61 body = comment_text # text of the comment
62 62 line = ''
63 63 if line_no:
64 64 line = _('on line %s') % line_no
65 65
66 66 #changeset
67 67 if revision:
68 68 notification_type = Notification.TYPE_CHANGESET_COMMENT
69 69 cs = repo.scm_instance.get_changeset(revision)
70 70 desc = cs.short_id
71 71
72 72 threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())]
73 73 if line_no: # TODO: url to file _and_ line number
74 74 threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no,
75 75 h.canonical_hostname()))
76 76 comment_url = h.canonical_url('changeset_home',
77 77 repo_name=repo.repo_name,
78 78 revision=revision,
79 79 anchor='comment-%s' % comment.comment_id)
80 80 subj = safe_unicode(
81 81 h.link_to('Re changeset: %(desc)s %(line)s' % \
82 82 {'desc': desc, 'line': line},
83 83 comment_url)
84 84 )
85 85 # get the current participants of this changeset
86 86 recipients = ChangesetComment.get_users(revision=revision)
87 87 # add changeset author if it's known locally
88 88 cs_author = User.get_from_cs_author(cs.author)
89 89 if not cs_author:
90 90 #use repo owner if we cannot extract the author correctly
91 91 # FIXME: just use committer name even if not a user
92 92 cs_author = repo.user
93 93 recipients += [cs_author]
94 94 email_kwargs = {
95 95 'status_change': status_change,
96 96 'cs_comment_user': user.full_name_and_username,
97 97 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name),
98 98 'cs_comment_url': comment_url,
99 99 'raw_id': revision,
100 100 'message': cs.message,
101 101 'cs_author': cs_author,
102 102 'repo_name': repo.repo_name,
103 103 'short_id': h.short_id(revision),
104 104 'branch': cs.branch,
105 105 'comment_username': user.username,
106 106 'threading': threading,
107 107 }
108 108 #pull request
109 109 elif pull_request:
110 110 notification_type = Notification.TYPE_PULL_REQUEST_COMMENT
111 111 desc = comment.pull_request.title
112 112 _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':')
113 113 _other_ref_type, other_ref_name, _other_rev = comment.pull_request.other_ref.split(':')
114 114 threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name,
115 115 pull_request.pull_request_id,
116 116 h.canonical_hostname())]
117 117 if line_no: # TODO: url to file _and_ line number
118 118 threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name,
119 119 pull_request.pull_request_id, line_no,
120 120 h.canonical_hostname()))
121 121 comment_url = pull_request.url(canonical=True,
122 122 anchor='comment-%s' % comment.comment_id)
123 123 subj = safe_unicode(
124 124 h.link_to('Re pull request %(pr_nice_id)s: %(desc)s %(line)s' % \
125 125 {'desc': desc,
126 126 'pr_nice_id': comment.pull_request.nice_id(),
127 127 'line': line},
128 128 comment_url)
129 129 )
130 130 # get the current participants of this pull request
131 131 recipients = ChangesetComment.get_users(pull_request_id=
132 132 pull_request.pull_request_id)
133 133 # add pull request author
134 134 recipients += [pull_request.owner]
135 135
136 136 # add the reviewers to notification
137 137 recipients += pull_request.get_reviewer_users()
138 138
139 139 #set some variables for email notification
140 140 email_kwargs = {
141 141 'pr_title': pull_request.title,
142 142 'pr_nice_id': pull_request.nice_id(),
143 143 'status_change': status_change,
144 144 'closing_pr': closing_pr,
145 145 'pr_comment_url': comment_url,
146 146 'pr_comment_user': user.full_name_and_username,
147 147 'pr_target_repo': h.canonical_url('summary_home',
148 148 repo_name=pull_request.other_repo.repo_name),
149 149 'pr_target_branch': other_ref_name,
150 150 'pr_source_repo': h.canonical_url('summary_home',
151 151 repo_name=pull_request.org_repo.repo_name),
152 152 'pr_source_branch': org_ref_name,
153 153 'pr_owner': pull_request.owner,
154 154 'repo_name': pull_request.other_repo.repo_name,
155 155 'comment_username': user.username,
156 156 'threading': threading,
157 157 }
158 158
159 159 return subj, body, recipients, notification_type, email_kwargs
160 160
161 161 def create(self, text, repo, user, revision=None, pull_request=None,
162 162 f_path=None, line_no=None, status_change=None, closing_pr=False,
163 163 send_email=True):
164 164 """
165 165 Creates a new comment for either a changeset or a pull request.
166 166 status_change and closing_pr is only for the optional email.
167 167
168 168 Returns the created comment.
169 169 """
170 170 if not status_change and not text:
171 171 log.warning('Missing text for comment, skipping...')
172 172 return None
173 173
174 174 repo = self._get_repo(repo)
175 175 user = self._get_user(user)
176 176 comment = ChangesetComment()
177 177 comment.repo = repo
178 178 comment.author = user
179 179 comment.text = text
180 180 comment.f_path = f_path
181 181 comment.line_no = line_no
182 182
183 183 if revision is not None:
184 184 comment.revision = revision
185 185 elif pull_request is not None:
186 186 pull_request = self.__get_pull_request(pull_request)
187 187 comment.pull_request = pull_request
188 188 else:
189 189 raise Exception('Please specify revision or pull_request_id')
190 190
191 191 Session().add(comment)
192 192 Session().flush()
193 193
194 194 if send_email:
195 195 (subj, body, recipients, notification_type,
196 196 email_kwargs) = self._get_notification_data(
197 197 repo, comment, user,
198 198 comment_text=text,
199 199 line_no=line_no,
200 200 revision=revision,
201 201 pull_request=pull_request,
202 202 status_change=status_change,
203 203 closing_pr=closing_pr)
204 204 email_kwargs['is_mention'] = False
205 205 # create notification objects, and emails
206 206 NotificationModel().create(
207 207 created_by=user, subject=subj, body=body,
208 208 recipients=recipients, type_=notification_type,
209 209 email_kwargs=email_kwargs,
210 210 )
211 211
212 212 mention_recipients = extract_mentioned_users(body).difference(recipients)
213 213 if mention_recipients:
214 214 email_kwargs['is_mention'] = True
215 215 subj = _('[Mention]') + ' ' + subj
216 # FIXME: this subject is wrong and unused!
216 217 NotificationModel().create(
217 218 created_by=user, subject=subj, body=body,
218 219 recipients=mention_recipients,
219 220 type_=notification_type,
220 221 email_kwargs=email_kwargs
221 222 )
222 223
223 224 return comment
224 225
225 226 def delete(self, comment):
226 227 comment = self.__get_changeset_comment(comment)
227 228 Session().delete(comment)
228 229
229 230 return comment
230 231
231 232 def get_comments(self, repo_id, revision=None, pull_request=None):
232 233 """
233 234 Gets general comments for either revision or pull_request.
234 235
235 236 Returns a list, ordered by creation date.
236 237 """
237 238 return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
238 239 inline=False)
239 240
240 241 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
241 242 """
242 243 Gets inline comments for either revision or pull_request.
243 244
244 245 Returns a list of tuples with file path and list of comments per line number.
245 246 """
246 247 comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
247 248 inline=True)
248 249
249 250 paths = defaultdict(lambda: defaultdict(list))
250 251 for co in comments:
251 252 paths[co.f_path][co.line_no].append(co)
252 253 return paths.items()
253 254
254 255 def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False):
255 256 """
256 257 Gets comments for either revision or pull_request_id, either inline or general.
257 258 """
258 259 q = Session().query(ChangesetComment)
259 260
260 261 if inline:
261 262 q = q.filter(ChangesetComment.line_no != None) \
262 263 .filter(ChangesetComment.f_path != None)
263 264 else:
264 265 q = q.filter(ChangesetComment.line_no == None) \
265 266 .filter(ChangesetComment.f_path == None)
266 267
267 268 if revision is not None:
268 269 q = q.filter(ChangesetComment.revision == revision) \
269 270 .filter(ChangesetComment.repo_id == repo_id)
270 271 elif pull_request is not None:
271 272 pull_request = self.__get_pull_request(pull_request)
272 273 q = q.filter(ChangesetComment.pull_request == pull_request)
273 274 else:
274 275 raise Exception('Please specify either revision or pull_request')
275 276
276 277 return q.order_by(ChangesetComment.created_on).all()
@@ -1,232 +1,233 b''
1 1 # -*- coding: utf-8 -*-
2 2 # This program is free software: you can redistribute it and/or modify
3 3 # it under the terms of the GNU General Public License as published by
4 4 # the Free Software Foundation, either version 3 of the License, or
5 5 # (at your option) any later version.
6 6 #
7 7 # This program is distributed in the hope that it will be useful,
8 8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 10 # GNU General Public License for more details.
11 11 #
12 12 # You should have received a copy of the GNU General Public License
13 13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 14 """
15 15 kallithea.model.pull_request
16 16 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
17 17
18 18 pull request model for Kallithea
19 19
20 20 This file was forked by the Kallithea project in July 2014.
21 21 Original author and date, and relevant copyright and licensing information is below:
22 22 :created_on: Jun 6, 2012
23 23 :author: marcink
24 24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 25 :license: GPLv3, see LICENSE.md for more details.
26 26 """
27 27
28 28 import logging
29 29 import datetime
30 30
31 31 from pylons.i18n.translation import _
32 32
33 33 from sqlalchemy.orm import joinedload
34 34
35 35 from kallithea.model.meta import Session
36 36 from kallithea.lib import helpers as h
37 37 from kallithea.lib.exceptions import UserInvalidException
38 38 from kallithea.model import BaseModel
39 39 from kallithea.model.db import PullRequest, PullRequestReviewers, Notification, \
40 40 ChangesetStatus, User
41 41 from kallithea.model.notification import NotificationModel
42 42 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
43 43
44 44
45 45 log = logging.getLogger(__name__)
46 46
47 47
48 48 class PullRequestModel(BaseModel):
49 49
50 50 cls = PullRequest
51 51
52 52 def __get_pull_request(self, pull_request):
53 53 return self._get_instance(PullRequest, pull_request)
54 54
55 55 def get_pullrequest_cnt_for_user(self, user):
56 56 return PullRequest.query() \
57 57 .join(PullRequestReviewers) \
58 58 .filter(PullRequestReviewers.user_id == user) \
59 59 .filter(PullRequest.status != PullRequest.STATUS_CLOSED) \
60 60 .count()
61 61
62 62 def get_all(self, repo_name, from_=False, closed=False):
63 63 """Get all PRs for repo.
64 64 Default is all PRs to the repo, PRs from the repo if from_.
65 65 Closed PRs are only included if closed is true."""
66 66 repo = self._get_repo(repo_name)
67 67 q = PullRequest.query()
68 68 if from_:
69 69 q = q.filter(PullRequest.org_repo == repo)
70 70 else:
71 71 q = q.filter(PullRequest.other_repo == repo)
72 72 if not closed:
73 73 q = q.filter(PullRequest.status != PullRequest.STATUS_CLOSED)
74 74 return q.order_by(PullRequest.created_on.desc()).all()
75 75
76 76 def _get_valid_reviewers(self, seq):
77 77 """ Generate User objects from a sequence of user IDs, usernames or
78 78 User objects. Raises UserInvalidException if the DEFAULT user is
79 79 specified, or if a given ID or username does not match any user.
80 80 """
81 81 for user_spec in seq:
82 82 user = self._get_user(user_spec)
83 83 if user is None or user.username == User.DEFAULT_USER:
84 84 raise UserInvalidException(user_spec)
85 85 yield user
86 86
87 87 def create(self, created_by, org_repo, org_ref, other_repo, other_ref,
88 88 revisions, reviewers, title, description=None):
89 89 from kallithea.model.changeset_status import ChangesetStatusModel
90 90
91 91 created_by_user = self._get_user(created_by)
92 92 org_repo = self._get_repo(org_repo)
93 93 other_repo = self._get_repo(other_repo)
94 94
95 95 new = PullRequest()
96 96 new.org_repo = org_repo
97 97 new.org_ref = org_ref
98 98 new.other_repo = other_repo
99 99 new.other_ref = other_ref
100 100 new.revisions = revisions
101 101 new.title = title
102 102 new.description = description
103 103 new.owner = created_by_user
104 104 Session().add(new)
105 105 Session().flush()
106 106
107 107 #reset state to under-review
108 108 from kallithea.model.comment import ChangesetCommentsModel
109 109 comment = ChangesetCommentsModel().create(
110 110 text=u'',
111 111 repo=org_repo,
112 112 user=new.owner,
113 113 pull_request=new,
114 114 send_email=False,
115 115 status_change=ChangesetStatus.STATUS_UNDER_REVIEW,
116 116 )
117 117 ChangesetStatusModel().set_status(
118 118 org_repo,
119 119 ChangesetStatus.STATUS_UNDER_REVIEW,
120 120 new.owner,
121 121 comment,
122 122 pull_request=new
123 123 )
124 124
125 125 reviewers = set(self._get_valid_reviewers(reviewers))
126 126 mention_recipients = extract_mentioned_users(new.description)
127 127 self.__add_reviewers(created_by_user, new, reviewers, mention_recipients)
128 128
129 129 return new
130 130
131 131 def __add_reviewers(self, user, pr, reviewers, mention_recipients):
132 132 # reviewers and mention_recipients should be sets of User objects.
133 133 #members
134 134 for reviewer in reviewers:
135 135 reviewer = PullRequestReviewers(reviewer, pr)
136 136 Session().add(reviewer)
137 137
138 138 revision_data = [(x.raw_id, x.message)
139 139 for x in map(pr.org_repo.get_changeset, pr.revisions)]
140 140
141 141 #notification to reviewers
142 142 pr_url = pr.url(canonical=True)
143 143 threading = ['%s-pr-%s@%s' % (pr.other_repo.repo_name,
144 144 pr.pull_request_id,
145 145 h.canonical_hostname())]
146 146 subject = safe_unicode(
147 147 h.link_to(
148 148 _('%(user)s wants you to review pull request %(pr_nice_id)s: %(pr_title)s') % \
149 149 {'user': user.username,
150 150 'pr_title': pr.title,
151 151 'pr_nice_id': pr.nice_id()},
152 152 pr_url)
153 153 )
154 154 body = pr.description
155 155 _org_ref_type, org_ref_name, _org_rev = pr.org_ref.split(':')
156 156 _other_ref_type, other_ref_name, _other_rev = pr.other_ref.split(':')
157 157 email_kwargs = {
158 158 'pr_title': pr.title,
159 159 'pr_user_created': user.full_name_and_username,
160 160 'pr_repo_url': h.canonical_url('summary_home', repo_name=pr.other_repo.repo_name),
161 161 'pr_url': pr_url,
162 162 'pr_revisions': revision_data,
163 163 'repo_name': pr.other_repo.repo_name,
164 164 'org_repo_name': pr.org_repo.repo_name,
165 165 'pr_nice_id': pr.nice_id(),
166 166 'pr_target_repo': h.canonical_url('summary_home',
167 167 repo_name=pr.other_repo.repo_name),
168 168 'pr_target_branch': other_ref_name,
169 169 'pr_source_repo': h.canonical_url('summary_home',
170 170 repo_name=pr.org_repo.repo_name),
171 171 'pr_source_branch': org_ref_name,
172 172 'pr_owner': pr.owner,
173 173 'pr_username': user.username,
174 174 'threading': threading,
175 175 'is_mention': False,
176 176 }
177 177 if reviewers:
178 178 NotificationModel().create(created_by=user, subject=subject, body=body,
179 179 recipients=reviewers,
180 180 type_=Notification.TYPE_PULL_REQUEST,
181 181 email_kwargs=email_kwargs)
182 182
183 183 if mention_recipients:
184 184 mention_recipients.difference_update(reviewers)
185 185 if mention_recipients:
186 186 email_kwargs['is_mention'] = True
187 187 subject = _('[Mention]') + ' ' + subject
188 # FIXME: this subject is wrong and unused!
188 189 NotificationModel().create(created_by=user, subject=subject, body=body,
189 190 recipients=mention_recipients,
190 191 type_=Notification.TYPE_PULL_REQUEST,
191 192 email_kwargs=email_kwargs)
192 193
193 194 def mention_from_description(self, user, pr, old_description=''):
194 195 mention_recipients = (extract_mentioned_users(pr.description) -
195 196 extract_mentioned_users(old_description))
196 197
197 198 log.debug("Mentioning %s", mention_recipients)
198 199 self.__add_reviewers(user, pr, set(), mention_recipients)
199 200
200 201 def update_reviewers(self, user, pull_request, reviewers_ids):
201 202 reviewers_ids = set(reviewers_ids)
202 203 pull_request = self.__get_pull_request(pull_request)
203 204 current_reviewers = PullRequestReviewers.query() \
204 205 .options(joinedload('user')) \
205 206 .filter_by(pull_request=pull_request) \
206 207 .all()
207 208 current_reviewer_users = set(x.user for x in current_reviewers)
208 209 new_reviewer_users = set(self._get_valid_reviewers(reviewers_ids))
209 210
210 211 to_add = new_reviewer_users - current_reviewer_users
211 212 to_remove = current_reviewer_users - new_reviewer_users
212 213
213 214 if not to_add and not to_remove:
214 215 return # all done
215 216
216 217 log.debug("Adding %s reviewers", to_add)
217 218 self.__add_reviewers(user, pull_request, to_add, set())
218 219
219 220 log.debug("Removing %s reviewers", to_remove)
220 221 for prr in current_reviewers:
221 222 if prr.user in to_remove:
222 223 Session().delete(prr)
223 224
224 225 def delete(self, pull_request):
225 226 pull_request = self.__get_pull_request(pull_request)
226 227 Session().delete(pull_request)
227 228
228 229 def close_pull_request(self, pull_request):
229 230 pull_request = self.__get_pull_request(pull_request)
230 231 pull_request.status = PullRequest.STATUS_CLOSED
231 232 pull_request.updated_on = datetime.datetime.now()
232 233 Session().add(pull_request)
@@ -1,637 +1,813 b''
1 1 <html><body>
2 2
3 3
4 4 <h1>cs_comment, is_mention=False, status_change=None</h1>
5 5 <pre>
6 6
7 7 From: u1
8 8 To: u2@example.com
9 9 Subject: [Comment] repo/name changeset cafe1234 on brunch
10 10
11 11 --------------------
12 12
13 13
14 14 Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff:
15 15 This is the new comment.
16 16
17 17 - and here it ends indented.
18 18
19 19
20 20 URL: http://comment.org
21 21
22 22 Changeset: cafe1234c0ff
23 23 Description:
24 24 This changeset did something clever which is hard to explain
25 25
26 26
27 27 --
28 28 This is an automatic notification. Don't reply to this mail.
29 29
30 30 --------------------</pre>
31 31
32 32
33 33
34 34 <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff:</p>
35 35 <p><div class="formatted-fixed">This is the new comment.
36 36
37 37 - and here it ends indented.</div></p>
38 38
39 39
40 40 <p>URL: <a href="http://comment.org">http://comment.org</a></p>
41 41
42 42 <p>Changeset: cafe1234c0ff</p>
43 43 <p>Description:<br/>
44 44 This changeset did something clever which is hard to explain
45 45 </p>
46 46
47 47
48 48 <br/>
49 49 <br/>
50 50 -- <br/>
51 51 This is an automatic notification. Don&#39;t reply to this mail.
52 52
53 53 <pre>--------------------</pre>
54 54
55 55
56 56 <h1>cs_comment, is_mention=True, status_change=None</h1>
57 57 <pre>
58 58
59 59 From: u1
60 60 To: u2@example.com
61 61 Subject: [Comment] repo/name changeset cafe1234 on brunch
62 62
63 63 --------------------
64 64
65 65
66 66 Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you:
67 67 This is the new comment.
68 68
69 69 - and here it ends indented.
70 70
71 71
72 72 URL: http://comment.org
73 73
74 74 Changeset: cafe1234c0ff
75 75 Description:
76 76 This changeset did something clever which is hard to explain
77 77
78 78
79 79 --
80 80 This is an automatic notification. Don't reply to this mail.
81 81
82 82 --------------------</pre>
83 83
84 84
85 85
86 86 <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you:</p>
87 87 <p><div class="formatted-fixed">This is the new comment.
88 88
89 89 - and here it ends indented.</div></p>
90 90
91 91
92 92 <p>URL: <a href="http://comment.org">http://comment.org</a></p>
93 93
94 94 <p>Changeset: cafe1234c0ff</p>
95 95 <p>Description:<br/>
96 96 This changeset did something clever which is hard to explain
97 97 </p>
98 98
99 99
100 100 <br/>
101 101 <br/>
102 102 -- <br/>
103 103 This is an automatic notification. Don&#39;t reply to this mail.
104 104
105 105 <pre>--------------------</pre>
106 106
107 107
108 108 <h1>cs_comment, is_mention=False, status_change='Approved'</h1>
109 109 <pre>
110 110
111 111 From: u1
112 112 To: u2@example.com
113 113 Subject: [Approved: Comment] repo/name changeset cafe1234 on brunch
114 114
115 115 --------------------
116 116
117 117
118 118 Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff:
119 119 This is the new comment.
120 120
121 121 - and here it ends indented.
122 122
123 123 The changeset status was changed to: Approved
124 124
125 125 URL: http://comment.org
126 126
127 127 Changeset: cafe1234c0ff
128 128 Description:
129 129 This changeset did something clever which is hard to explain
130 130
131 131
132 132 --
133 133 This is an automatic notification. Don't reply to this mail.
134 134
135 135 --------------------</pre>
136 136
137 137
138 138
139 139 <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff:</p>
140 140 <p><div class="formatted-fixed">This is the new comment.
141 141
142 142 - and here it ends indented.</div></p>
143 143
144 144 <p>The changeset status was changed to: <b>Approved</b></p>
145 145
146 146 <p>URL: <a href="http://comment.org">http://comment.org</a></p>
147 147
148 148 <p>Changeset: cafe1234c0ff</p>
149 149 <p>Description:<br/>
150 150 This changeset did something clever which is hard to explain
151 151 </p>
152 152
153 153
154 154 <br/>
155 155 <br/>
156 156 -- <br/>
157 157 This is an automatic notification. Don&#39;t reply to this mail.
158 158
159 159 <pre>--------------------</pre>
160 160
161 161
162 162 <h1>cs_comment, is_mention=True, status_change='Approved'</h1>
163 163 <pre>
164 164
165 165 From: u1
166 166 To: u2@example.com
167 167 Subject: [Approved: Comment] repo/name changeset cafe1234 on brunch
168 168
169 169 --------------------
170 170
171 171
172 172 Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you:
173 173 This is the new comment.
174 174
175 175 - and here it ends indented.
176 176
177 177 The changeset status was changed to: Approved
178 178
179 179 URL: http://comment.org
180 180
181 181 Changeset: cafe1234c0ff
182 182 Description:
183 183 This changeset did something clever which is hard to explain
184 184
185 185
186 186 --
187 187 This is an automatic notification. Don't reply to this mail.
188 188
189 189 --------------------</pre>
190 190
191 191
192 192
193 193 <p>Comment from Opinionated User (jsmith) on repo_target changeset cafe1234c0ff mentioned you:</p>
194 194 <p><div class="formatted-fixed">This is the new comment.
195 195
196 196 - and here it ends indented.</div></p>
197 197
198 198 <p>The changeset status was changed to: <b>Approved</b></p>
199 199
200 200 <p>URL: <a href="http://comment.org">http://comment.org</a></p>
201 201
202 202 <p>Changeset: cafe1234c0ff</p>
203 203 <p>Description:<br/>
204 204 This changeset did something clever which is hard to explain
205 205 </p>
206 206
207 207
208 208 <br/>
209 209 <br/>
210 210 -- <br/>
211 211 This is an automatic notification. Don&#39;t reply to this mail.
212 212
213 213 <pre>--------------------</pre>
214 214
215 215
216 216 <h1>message</h1>
217 217 <pre>
218 218
219 219 From: u1
220 220 To: u2@example.com
221 221 Subject: Test Message
222 222
223 223 --------------------
224 224
225 225
226 226 This is the body of the test message
227 227 - nothing interesting here except indentation.
228 228
229 229
230 230 --
231 231 This is an automatic notification. Don't reply to this mail.
232 232
233 233 --------------------</pre>
234 234
235 235
236 236
237 237 <div class="formatted-fixed">This is the body of the test message
238 238 - nothing interesting here except indentation.</div>
239 239
240 240
241 241 <br/>
242 242 <br/>
243 243 -- <br/>
244 244 This is an automatic notification. Don&#39;t reply to this mail.
245 245
246 246 <pre>--------------------</pre>
247 247
248 248
249 249 <h1>registration</h1>
250 250 <pre>
251 251
252 252 From: u1
253 253 To: u2@example.com
254 254 Subject: New user newbie registered
255 255
256 256 --------------------
257 257
258 258
259 259 Registration body
260 260
261 261 View this user here: http://newbie.org
262 262
263 263
264 264 --
265 265 This is an automatic notification. Don't reply to this mail.
266 266
267 267 --------------------</pre>
268 268
269 269
270 270
271 271 <div class="formatted-fixed">Registration body</div>
272 272
273 273 View this user here: <a href="http://newbie.org">http://newbie.org</a>
274 274
275 275
276 276 <br/>
277 277 <br/>
278 278 -- <br/>
279 279 This is an automatic notification. Don&#39;t reply to this mail.
280 280
281 281 <pre>--------------------</pre>
282 282
283 283
284 284 <h1>pull_request, is_mention=False</h1>
285 285 <pre>
286 286
287 287 From: u1
288 288 To: u2@example.com
289 289 Subject: [Added] repo/name pull request #7 from devbranch
290 290
291 291 --------------------
292 292
293 293
294 294 Requesting User (root) requested your review of repo/name pull request "The Title"
295 295
296 296 URL: http://pr.org/7
297 297
298 298 Description:
299 299 This PR is awesome because it does stuff
300 300 - please approve indented!
301 301
302 302 Changesets:
303 303 123abc123abc: http://changeset_home/?repo_name=repo_org&amp;revision=123abc123abc123abc123abc123abc123abc123abc
304 304 Introduce one and two
305 305
306 306 and that's it
307 307
308 308 567fed567fed: http://changeset_home/?repo_name=repo_org&amp;revision=567fed567fed567fed567fed567fed567fed567fed
309 309 Make one plus two equal tree
310 310
311 311
312 312
313 313 --
314 314 This is an automatic notification. Don't reply to this mail.
315 315
316 316 --------------------</pre>
317 317
318 318
319 319
320 320 <p>Requesting User (root) requested your review of repo/name pull request &#34;The Title&#34;</p>
321 321
322 322 <p>URL: <a href="http://pr.org/7">http://pr.org/7</a></p>
323 323
324 324 <p>Description:</p>
325 325 <p style="white-space: pre-wrap; font-family: monospace"><div class="formatted-fixed">This PR is awesome because it does stuff
326 326 - please approve indented!</div></p>
327 327
328 328 <p>Changesets:</p>
329 329 <p style="white-space: pre-wrap">
330 330 <i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=123abc123abc123abc123abc123abc123abc123abc">123abc123abc</a></i>:
331 331 Introduce one and two
332 332
333 333 and that&#39;s it
334 334
335 335 <i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=567fed567fed567fed567fed567fed567fed567fed">567fed567fed</a></i>:
336 336 Make one plus two equal tree
337 337
338 338 </p>
339 339
340 340
341 341 <br/>
342 342 <br/>
343 343 -- <br/>
344 344 This is an automatic notification. Don&#39;t reply to this mail.
345 345
346 346 <pre>--------------------</pre>
347 347
348 348
349 349 <h1>pull_request, is_mention=True</h1>
350 350 <pre>
351 351
352 352 From: u1
353 353 To: u2@example.com
354 354 Subject: [Added] repo/name pull request #7 from devbranch
355 355
356 356 --------------------
357 357
358 358
359 359 Requesting User (root) mentioned you on repo/name pull request "The Title"
360 360
361 361 URL: http://pr.org/7
362 362
363 363 Description:
364 364 This PR is awesome because it does stuff
365 365 - please approve indented!
366 366
367 367 Changesets:
368 368 123abc123abc: http://changeset_home/?repo_name=repo_org&amp;revision=123abc123abc123abc123abc123abc123abc123abc
369 369 Introduce one and two
370 370
371 371 and that's it
372 372
373 373 567fed567fed: http://changeset_home/?repo_name=repo_org&amp;revision=567fed567fed567fed567fed567fed567fed567fed
374 374 Make one plus two equal tree
375 375
376 376
377 377
378 378 --
379 379 This is an automatic notification. Don't reply to this mail.
380 380
381 381 --------------------</pre>
382 382
383 383
384 384
385 385 <p>Requesting User (root) mentioned you on repo/name pull request &#34;The Title&#34;</p>
386 386
387 387 <p>URL: <a href="http://pr.org/7">http://pr.org/7</a></p>
388 388
389 389 <p>Description:</p>
390 390 <p style="white-space: pre-wrap; font-family: monospace"><div class="formatted-fixed">This PR is awesome because it does stuff
391 391 - please approve indented!</div></p>
392 392
393 393 <p>Changesets:</p>
394 394 <p style="white-space: pre-wrap">
395 395 <i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=123abc123abc123abc123abc123abc123abc123abc">123abc123abc</a></i>:
396 396 Introduce one and two
397 397
398 398 and that&#39;s it
399 399
400 400 <i><a href="http://changeset_home/?repo_name=repo_org&amp;revision=567fed567fed567fed567fed567fed567fed567fed">567fed567fed</a></i>:
401 401 Make one plus two equal tree
402 402
403 403 </p>
404 404
405 405
406 406 <br/>
407 407 <br/>
408 408 -- <br/>
409 409 This is an automatic notification. Don&#39;t reply to this mail.
410 410
411 411 <pre>--------------------</pre>
412 412
413 413
414 <h1>pull_request_comment, status_change=None, closing_pr=False</h1>
414 <h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=False</h1>
415 415 <pre>
416 416
417 417 From: u1
418 418 To: u2@example.com
419 419 Subject: [Comment] repo/name pull request #7 from devbranch
420 420
421 421 --------------------
422 422
423 423
424 424 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
425 425 Me too!
426 426
427 427 - and indented on second line
428 428
429 429
430 430 URL: http://pr.org/comment
431 431
432 432
433 433 --
434 434 This is an automatic notification. Don't reply to this mail.
435 435
436 436 --------------------</pre>
437 437
438 438
439 439
440 440 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
441 441 <p><div class="formatted-fixed">Me too!
442 442
443 443 - and indented on second line</div></p>
444 444
445 445
446 446 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
447 447
448 448
449 449 <br/>
450 450 <br/>
451 451 -- <br/>
452 452 This is an automatic notification. Don&#39;t reply to this mail.
453 453
454 454 <pre>--------------------</pre>
455 455
456 456
457 <h1>pull_request_comment, status_change='Under Review', closing_pr=False</h1>
457 <h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=False</h1>
458 <pre>
459
460 From: u1
461 To: u2@example.com
462 Subject: [Comment] repo/name pull request #7 from devbranch
463
464 --------------------
465
466
467 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
468 Me too!
469
470 - and indented on second line
471
472
473 URL: http://pr.org/comment
474
475
476 --
477 This is an automatic notification. Don't reply to this mail.
478
479 --------------------</pre>
480
481
482
483 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
484 <p><div class="formatted-fixed">Me too!
485
486 - and indented on second line</div></p>
487
488
489 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
490
491
492 <br/>
493 <br/>
494 -- <br/>
495 This is an automatic notification. Don&#39;t reply to this mail.
496
497 <pre>--------------------</pre>
498
499
500 <h1>pull_request_comment, is_mention=False, status_change='Under Review', closing_pr=False</h1>
458 501 <pre>
459 502
460 503 From: u1
461 504 To: u2@example.com
462 505 Subject: [Under Review: Comment] repo/name pull request #7 from devbranch
463 506
464 507 --------------------
465 508
466 509
467 510 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
468 511 Me too!
469 512
470 513 - and indented on second line
471 514
472 515 The comment was made with status: Under Review
473 516
474 517 URL: http://pr.org/comment
475 518
476 519
477 520 --
478 521 This is an automatic notification. Don't reply to this mail.
479 522
480 523 --------------------</pre>
481 524
482 525
483 526
484 527 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
485 528 <p><div class="formatted-fixed">Me too!
486 529
487 530 - and indented on second line</div></p>
488 531
489 532 <p>The comment was made with status: <b>Under Review</b></p>
490 533
491 534 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
492 535
493 536
494 537 <br/>
495 538 <br/>
496 539 -- <br/>
497 540 This is an automatic notification. Don&#39;t reply to this mail.
498 541
499 542 <pre>--------------------</pre>
500 543
501 544
502 <h1>pull_request_comment, status_change=None, closing_pr=True</h1>
545 <h1>pull_request_comment, is_mention=True, status_change='Under Review', closing_pr=False</h1>
546 <pre>
547
548 From: u1
549 To: u2@example.com
550 Subject: [Under Review: Comment] repo/name pull request #7 from devbranch
551
552 --------------------
553
554
555 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
556 Me too!
557
558 - and indented on second line
559
560 The comment was made with status: Under Review
561
562 URL: http://pr.org/comment
563
564
565 --
566 This is an automatic notification. Don't reply to this mail.
567
568 --------------------</pre>
569
570
571
572 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
573 <p><div class="formatted-fixed">Me too!
574
575 - and indented on second line</div></p>
576
577 <p>The comment was made with status: <b>Under Review</b></p>
578
579 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
580
581
582 <br/>
583 <br/>
584 -- <br/>
585 This is an automatic notification. Don&#39;t reply to this mail.
586
587 <pre>--------------------</pre>
588
589
590 <h1>pull_request_comment, is_mention=False, status_change=None, closing_pr=True</h1>
503 591 <pre>
504 592
505 593 From: u1
506 594 To: u2@example.com
507 595 Subject: [Closing: Comment] repo/name pull request #7 from devbranch
508 596
509 597 --------------------
510 598
511 599
512 600 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
513 601 Me too!
514 602
515 603 - and indented on second line
516 604
517 605
518 606 URL: http://pr.org/comment
519 607
520 608
521 609 --
522 610 This is an automatic notification. Don't reply to this mail.
523 611
524 612 --------------------</pre>
525 613
526 614
527 615
528 616 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
529 617 <p><div class="formatted-fixed">Me too!
530 618
531 619 - and indented on second line</div></p>
532 620
533 621
534 622 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
535 623
536 624
537 625 <br/>
538 626 <br/>
539 627 -- <br/>
540 628 This is an automatic notification. Don&#39;t reply to this mail.
541 629
542 630 <pre>--------------------</pre>
543 631
544 632
545 <h1>pull_request_comment, status_change='Under Review', closing_pr=True</h1>
633 <h1>pull_request_comment, is_mention=True, status_change=None, closing_pr=True</h1>
634 <pre>
635
636 From: u1
637 To: u2@example.com
638 Subject: [Closing: Comment] repo/name pull request #7 from devbranch
639
640 --------------------
641
642
643 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
644 Me too!
645
646 - and indented on second line
647
648
649 URL: http://pr.org/comment
650
651
652 --
653 This is an automatic notification. Don't reply to this mail.
654
655 --------------------</pre>
656
657
658
659 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
660 <p><div class="formatted-fixed">Me too!
661
662 - and indented on second line</div></p>
663
664
665 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
666
667
668 <br/>
669 <br/>
670 -- <br/>
671 This is an automatic notification. Don&#39;t reply to this mail.
672
673 <pre>--------------------</pre>
674
675
676 <h1>pull_request_comment, is_mention=False, status_change='Under Review', closing_pr=True</h1>
677 <pre>
678
679 From: u1
680 To: u2@example.com
681 Subject: [Under Review, Closing: Comment] repo/name pull request #7 from devbranch
682
683 --------------------
684
685
686 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
687 Me too!
688
689 - and indented on second line
690
691 The comment closed the pull request with status: Under Review
692
693 URL: http://pr.org/comment
694
695
696 --
697 This is an automatic notification. Don't reply to this mail.
698
699 --------------------</pre>
700
701
702
703 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
704 <p><div class="formatted-fixed">Me too!
705
706 - and indented on second line</div></p>
707
708 <p>The comment closed the pull request with status: <b>Under Review</b></p>
709
710 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
711
712
713 <br/>
714 <br/>
715 -- <br/>
716 This is an automatic notification. Don&#39;t reply to this mail.
717
718 <pre>--------------------</pre>
719
720
721 <h1>pull_request_comment, is_mention=True, status_change='Under Review', closing_pr=True</h1>
546 722 <pre>
547 723
548 724 From: u1
549 725 To: u2@example.com
550 726 Subject: [Under Review, Closing: Comment] repo/name pull request #7 from devbranch
551 727
552 728 --------------------
553 729
554 730
555 731 Comment from Opinionated User (jsmith) on repo/name pull request "The Title":
556 732 Me too!
557 733
558 734 - and indented on second line
559 735
560 736 The comment closed the pull request with status: Under Review
561 737
562 738 URL: http://pr.org/comment
563 739
564 740
565 741 --
566 742 This is an automatic notification. Don't reply to this mail.
567 743
568 744 --------------------</pre>
569 745
570 746
571 747
572 748 <p>Comment from Opinionated User (jsmith) on repo/name pull request &#34;The Title&#34;:</p>
573 749 <p><div class="formatted-fixed">Me too!
574 750
575 751 - and indented on second line</div></p>
576 752
577 753 <p>The comment closed the pull request with status: <b>Under Review</b></p>
578 754
579 755 <p>URL: <a href="http://pr.org/comment">http://pr.org/comment</a></p>
580 756
581 757
582 758 <br/>
583 759 <br/>
584 760 -- <br/>
585 761 This is an automatic notification. Don&#39;t reply to this mail.
586 762
587 763 <pre>--------------------</pre>
588 764
589 765
590 766 <h1>TYPE_PASSWORD_RESET</h1>
591 767 <pre>
592 768
593 769 From: u1
594 770 To: john@doe.com
595 771 Subject: Password reset link
596 772
597 773 --------------------
598 774
599 775
600 776 Hello John Doe
601 777
602 778 We have received a request to reset the password for your account.
603 779 To set a new password, click the following link:
604 780
605 781 http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746
606 782
607 783 Should you not be able to use the link above, please type the following code into the password reset form: decbf64715098db5b0bd23eab44bd792670ab746
608 784
609 785 If it weren't you who requested the password reset, just disregard this message.
610 786
611 787
612 788 --
613 789 This is an automatic notification. Don't reply to this mail.
614 790
615 791 --------------------</pre>
616 792
617 793
618 794
619 795 <h4>Hello John Doe</h4>
620 796
621 797 <p>We have received a request to reset the password for your account.</p>
622 798 <p>To set a new password, click the following link:</p>
623 799 <p><a href="http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746">http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746</a></p>
624 800
625 801 <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>
626 802
627 803 <p>If it weren&#39;t you who requested the password reset, just disregard this message.</p>
628 804
629 805
630 806 <br/>
631 807 <br/>
632 808 -- <br/>
633 809 This is an automatic notification. Don&#39;t reply to this mail.
634 810
635 811 <pre>--------------------</pre>
636 812
637 813 </body></html>
@@ -1,277 +1,278 b''
1 1 import os
2 2
3 3 import mock
4 4 import routes.util
5 5
6 6 from kallithea.tests import *
7 7 from kallithea.lib import helpers as h
8 8 from kallithea.model.db import User, Notification, UserNotification
9 9 from kallithea.model.user import UserModel
10 10 from kallithea.model.meta import Session
11 11 from kallithea.model.notification import NotificationModel, EmailNotificationModel
12 12
13 13 import kallithea.lib.celerylib
14 14 import kallithea.lib.celerylib.tasks
15 15
16 16
17 17 class TestNotifications(TestController):
18 18
19 19 def setup_method(self, method):
20 20 Session.remove()
21 21 u1 = UserModel().create_or_update(username=u'u1',
22 22 password=u'qweqwe',
23 23 email=u'u1@example.com',
24 24 firstname=u'u1', lastname=u'u1')
25 25 Session().commit()
26 26 self.u1 = u1.user_id
27 27
28 28 u2 = UserModel().create_or_update(username=u'u2',
29 29 password=u'qweqwe',
30 30 email=u'u2@example.com',
31 31 firstname=u'u2', lastname=u'u3')
32 32 Session().commit()
33 33 self.u2 = u2.user_id
34 34
35 35 u3 = UserModel().create_or_update(username=u'u3',
36 36 password=u'qweqwe',
37 37 email=u'u3@example.com',
38 38 firstname=u'u3', lastname=u'u3')
39 39 Session().commit()
40 40 self.u3 = u3.user_id
41 41
42 42 self.remove_all_notifications()
43 43 assert [] == Notification.query().all()
44 44 assert [] == UserNotification.query().all()
45 45
46 46 def test_create_notification(self):
47 47 usrs = [self.u1, self.u2]
48 48 def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
49 49 assert recipients == ['u2@example.com']
50 50 assert subject == 'Test Message'
51 51 assert body == "\n\nhi there\n\n\n-- \nThis is an automatic notification. Don't reply to this mail.\n"
52 52 assert '>hi there<' in html_body
53 53 assert author.username == 'u1'
54 54 with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email):
55 55 notification = NotificationModel().create(created_by=self.u1,
56 56 subject=u'subj', body=u'hi there',
57 57 recipients=usrs)
58 58 Session().commit()
59 59 u1 = User.get(self.u1)
60 60 u2 = User.get(self.u2)
61 61 u3 = User.get(self.u3)
62 62 notifications = Notification.query().all()
63 63 assert len(notifications) == 1
64 64
65 65 assert notifications[0].recipients == [u1, u2]
66 66 assert notification.notification_id == notifications[0].notification_id
67 67
68 68 unotification = UserNotification.query() \
69 69 .filter(UserNotification.notification == notification).all()
70 70
71 71 assert len(unotification) == len(usrs)
72 72 assert set([x.user.user_id for x in unotification]) == set(usrs)
73 73
74 74 def test_user_notifications(self):
75 75 notification1 = NotificationModel().create(created_by=self.u1,
76 76 subject=u'subj', body=u'hi there1',
77 77 recipients=[self.u3])
78 78 Session().commit()
79 79 notification2 = NotificationModel().create(created_by=self.u1,
80 80 subject=u'subj', body=u'hi there2',
81 81 recipients=[self.u3])
82 82 Session().commit()
83 83 u3 = Session().query(User).get(self.u3)
84 84
85 85 assert sorted([x.notification for x in u3.notifications]) == sorted([notification2, notification1])
86 86
87 87 def test_delete_notifications(self):
88 88 notification = NotificationModel().create(created_by=self.u1,
89 89 subject=u'title', body=u'hi there3',
90 90 recipients=[self.u3, self.u1, self.u2])
91 91 Session().commit()
92 92 notifications = Notification.query().all()
93 93 assert notification in notifications
94 94
95 95 Notification.delete(notification.notification_id)
96 96 Session().commit()
97 97
98 98 notifications = Notification.query().all()
99 99 assert not notification in notifications
100 100
101 101 un = UserNotification.query().filter(UserNotification.notification
102 102 == notification).all()
103 103 assert un == []
104 104
105 105 def test_delete_association(self):
106 106 notification = NotificationModel().create(created_by=self.u1,
107 107 subject=u'title', body=u'hi there3',
108 108 recipients=[self.u3, self.u1, self.u2])
109 109 Session().commit()
110 110
111 111 unotification = UserNotification.query() \
112 112 .filter(UserNotification.notification ==
113 113 notification) \
114 114 .filter(UserNotification.user_id == self.u3) \
115 115 .scalar()
116 116
117 117 assert unotification.user_id == self.u3
118 118
119 119 NotificationModel().delete(self.u3,
120 120 notification.notification_id)
121 121 Session().commit()
122 122
123 123 u3notification = UserNotification.query() \
124 124 .filter(UserNotification.notification ==
125 125 notification) \
126 126 .filter(UserNotification.user_id == self.u3) \
127 127 .scalar()
128 128
129 129 assert u3notification == None
130 130
131 131 # notification object is still there
132 132 assert Notification.query().all() == [notification]
133 133
134 134 #u1 and u2 still have assignments
135 135 u1notification = UserNotification.query() \
136 136 .filter(UserNotification.notification ==
137 137 notification) \
138 138 .filter(UserNotification.user_id == self.u1) \
139 139 .scalar()
140 140 assert u1notification != None
141 141 u2notification = UserNotification.query() \
142 142 .filter(UserNotification.notification ==
143 143 notification) \
144 144 .filter(UserNotification.user_id == self.u2) \
145 145 .scalar()
146 146 assert u2notification != None
147 147
148 148 def test_notification_counter(self):
149 149 NotificationModel().create(created_by=self.u1,
150 150 subject=u'title', body=u'hi there_delete',
151 151 recipients=[self.u3, self.u1])
152 152 Session().commit()
153 153
154 154 assert NotificationModel().get_unread_cnt_for_user(self.u1) == 0
155 155 assert NotificationModel().get_unread_cnt_for_user(self.u2) == 0
156 156 assert NotificationModel().get_unread_cnt_for_user(self.u3) == 1
157 157
158 158 notification = NotificationModel().create(created_by=self.u1,
159 159 subject=u'title', body=u'hi there3',
160 160 recipients=[self.u3, self.u1, self.u2])
161 161 Session().commit()
162 162
163 163 assert NotificationModel().get_unread_cnt_for_user(self.u1) == 0
164 164 assert NotificationModel().get_unread_cnt_for_user(self.u2) == 1
165 165 assert NotificationModel().get_unread_cnt_for_user(self.u3) == 2
166 166
167 167 @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())))))
168 168 def test_dump_html_mails(self):
169 169 # Exercise all notification types and dump them to one big html file
170 170 l = []
171 171
172 172 def send_email(recipients, subject, body='', html_body='', headers=None, author=None):
173 173 l.append('\n\n<h1>%s</h1>\n' % desc) # desc is from outer scope
174 174 l.append('<pre>\n\n')
175 175 l.append('From: %s\n' % author.username)
176 176 l.append('To: %s\n' % ' '.join(recipients))
177 177 l.append('Subject: %s\n' % subject)
178 178 l.append('\n--------------------\n%s\n--------------------' % body)
179 179 l.append('</pre>\n')
180 180 l.append('\n%s\n' % html_body)
181 181 l.append('<pre>--------------------</pre>\n')
182 182
183 183 l.append('<html><body>\n')
184 184 with mock.patch.object(kallithea.lib.celerylib.tasks, 'send_email', send_email):
185 185 pr_kwargs = dict(
186 186 pr_nice_id='#7',
187 187 pr_title='The Title',
188 188 pr_url='http://pr.org/7',
189 189 pr_target_repo='http://mainline.com/repo',
190 190 pr_target_branch='trunk',
191 191 pr_source_repo='https://dev.org/repo',
192 192 pr_source_branch='devbranch',
193 193 pr_owner=User.get(self.u2),
194 194 )
195 195
196 196 for type_, body, kwargs in [
197 197 (Notification.TYPE_CHANGESET_COMMENT,
198 198 u'This is the new comment.\n\n - and here it ends indented.',
199 199 dict(
200 200 short_id='cafe1234',
201 201 raw_id='cafe1234c0ffeecafe',
202 202 branch='brunch',
203 203 cs_comment_user='Opinionated User (jsmith)',
204 204 cs_comment_url='http://comment.org',
205 205 is_mention=[False, True],
206 206 message='This changeset did something clever which is hard to explain',
207 207 status_change=[None, 'Approved'],
208 208 cs_target_repo='repo_target',
209 209 cs_url='http://changeset.com',
210 210 cs_author=User.get(self.u2))),
211 211 (Notification.TYPE_MESSAGE,
212 212 u'This is the body of the test message\n - nothing interesting here except indentation.',
213 213 dict()),
214 214 #(Notification.TYPE_MENTION, '$body', None), # not used
215 215 (Notification.TYPE_REGISTRATION,
216 216 u'Registration body',
217 217 dict(
218 218 new_username='newbie',
219 219 registered_user_url='http://newbie.org',
220 220 new_email='new@email.com',
221 221 new_full_name='New Full Name')),
222 222 (Notification.TYPE_PULL_REQUEST,
223 223 u'This PR is awesome because it does stuff\n - please approve indented!',
224 224 dict(
225 225 pr_user_created='Requesting User (root)', # pr_owner should perhaps be used for @mention in description ...
226 226 is_mention=[False, True],
227 227 pr_revisions=[('123abc'*7, "Introduce one and two\n\nand that's it"), ('567fed'*7, 'Make one plus two equal tree')],
228 228 org_repo_name='repo_org',
229 229 **pr_kwargs)),
230 230 (Notification.TYPE_PULL_REQUEST_COMMENT,
231 231 u'Me too!\n\n - and indented on second line',
232 232 dict(
233 233 closing_pr=[False, True],
234 is_mention=[False, True],
234 235 pr_comment_user='Opinionated User (jsmith)',
235 236 pr_comment_url='http://pr.org/comment',
236 237 status_change=[None, 'Under Review'],
237 238 **pr_kwargs)),
238 239 ]:
239 240 kwargs['repo_name'] = u'repo/name'
240 241 params = [(type_, type_, body, kwargs)]
241 242 for param_name in ['is_mention', 'status_change', 'closing_pr']: # TODO: inline/general
242 243 if not isinstance(kwargs.get(param_name), list):
243 244 continue
244 245 new_params = []
245 246 for v in kwargs[param_name]:
246 247 for desc, type_, body, kwargs in params:
247 248 kwargs = dict(kwargs)
248 249 kwargs[param_name] = v
249 250 new_params.append(('%s, %s=%r' % (desc, param_name, v), type_, body, kwargs))
250 251 params = new_params
251 252
252 253 for desc, type_, body, kwargs in params:
253 254 # desc is used as "global" variable
254 255 notification = NotificationModel().create(created_by=self.u1,
255 256 subject=u'unused', body=body, email_kwargs=kwargs,
256 257 recipients=[self.u2], type_=type_)
257 258
258 259 # Email type TYPE_PASSWORD_RESET has no corresponding notification type - test it directly:
259 260 desc = 'TYPE_PASSWORD_RESET'
260 261 kwargs = dict(user='John Doe', reset_token='decbf64715098db5b0bd23eab44bd792670ab746', reset_url='http://reset.com/decbf64715098db5b0bd23eab44bd792670ab746')
261 262 kallithea.lib.celerylib.run_task(kallithea.lib.celerylib.tasks.send_email, ['john@doe.com'],
262 263 "Password reset link",
263 264 EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'txt', **kwargs),
264 265 EmailNotificationModel().get_email_tmpl(EmailNotificationModel.TYPE_PASSWORD_RESET, 'html', **kwargs),
265 266 author=User.get(self.u1))
266 267
267 268 l.append('\n</body></html>\n')
268 269 out = ''.join(l)
269 270
270 271 outfn = os.path.join(os.path.dirname(__file__), 'test_dump_html_mails.out.html')
271 272 reffn = os.path.join(os.path.dirname(__file__), 'test_dump_html_mails.ref.html')
272 273 with file(outfn, 'w') as f:
273 274 f.write(out)
274 275 with file(reffn) as f:
275 276 ref = f.read()
276 277 assert ref == out # copy test_dump_html_mails.out.html to test_dump_html_mails.ref.html to update expectations
277 278 os.unlink(outfn)
General Comments 0
You need to be logged in to leave comments. Login now