##// END OF EJS Templates
model: comments: allow selective retrieval of inline comments...
Thomas De Schampheleire -
r7409:445d6875 default
parent child Browse files
Show More
@@ -1,283 +1,304 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 # This program is free software: you can redistribute it and/or modify
2 # This program is free software: you can redistribute it and/or modify
3 # it under the terms of the GNU General Public License as published by
3 # it under the terms of the GNU General Public License as published by
4 # the Free Software Foundation, either version 3 of the License, or
4 # the Free Software Foundation, either version 3 of the License, or
5 # (at your option) any later version.
5 # (at your option) any later version.
6 #
6 #
7 # This program is distributed in the hope that it will be useful,
7 # This program is distributed in the hope that it will be useful,
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
8 # but WITHOUT ANY WARRANTY; without even the implied warranty of
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
9 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 # GNU General Public License for more details.
10 # GNU General Public License for more details.
11 #
11 #
12 # You should have received a copy of the GNU General Public License
12 # You should have received a copy of the GNU General Public License
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
13 # along with this program. If not, see <http://www.gnu.org/licenses/>.
14 """
14 """
15 kallithea.model.comment
15 kallithea.model.comment
16 ~~~~~~~~~~~~~~~~~~~~~~~
16 ~~~~~~~~~~~~~~~~~~~~~~~
17
17
18 comments model for Kallithea
18 comments model for Kallithea
19
19
20 This file was forked by the Kallithea project in July 2014.
20 This file was forked by the Kallithea project in July 2014.
21 Original author and date, and relevant copyright and licensing information is below:
21 Original author and date, and relevant copyright and licensing information is below:
22 :created_on: Nov 11, 2011
22 :created_on: Nov 11, 2011
23 :author: marcink
23 :author: marcink
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 :copyright: (c) 2013 RhodeCode GmbH, and others.
25 :license: GPLv3, see LICENSE.md for more details.
25 :license: GPLv3, see LICENSE.md for more details.
26 """
26 """
27
27
28 import logging
28 import logging
29
29
30 from tg.i18n import ugettext as _
30 from tg.i18n import ugettext as _
31 from collections import defaultdict
31 from collections import defaultdict
32
32
33 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
33 from kallithea.lib.utils2 import extract_mentioned_users, safe_unicode
34 from kallithea.lib import helpers as h
34 from kallithea.lib import helpers as h
35 from kallithea.model.db import ChangesetComment, User, \
35 from kallithea.model.db import ChangesetComment, User, \
36 PullRequest, Repository
36 PullRequest, Repository
37 from kallithea.model.notification import NotificationModel
37 from kallithea.model.notification import NotificationModel
38 from kallithea.model.meta import Session
38 from kallithea.model.meta import Session
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 def _list_changeset_commenters(revision):
43 def _list_changeset_commenters(revision):
44 return (Session().query(User)
44 return (Session().query(User)
45 .join(ChangesetComment.author)
45 .join(ChangesetComment.author)
46 .filter(ChangesetComment.revision == revision)
46 .filter(ChangesetComment.revision == revision)
47 .all())
47 .all())
48
48
49 def _list_pull_request_commenters(pull_request):
49 def _list_pull_request_commenters(pull_request):
50 return (Session().query(User)
50 return (Session().query(User)
51 .join(ChangesetComment.author)
51 .join(ChangesetComment.author)
52 .filter(ChangesetComment.pull_request_id == pull_request.pull_request_id)
52 .filter(ChangesetComment.pull_request_id == pull_request.pull_request_id)
53 .all())
53 .all())
54
54
55
55
56 class ChangesetCommentsModel(object):
56 class ChangesetCommentsModel(object):
57
57
58 def _get_notification_data(self, repo, comment, author, comment_text,
58 def _get_notification_data(self, repo, comment, author, comment_text,
59 line_no=None, revision=None, pull_request=None,
59 line_no=None, revision=None, pull_request=None,
60 status_change=None, closing_pr=False):
60 status_change=None, closing_pr=False):
61 """
61 """
62 :returns: tuple (subj,body,recipients,notification_type,email_kwargs)
62 :returns: tuple (subj,body,recipients,notification_type,email_kwargs)
63 """
63 """
64 # make notification
64 # make notification
65 body = comment_text # text of the comment
65 body = comment_text # text of the comment
66 line = ''
66 line = ''
67 if line_no:
67 if line_no:
68 line = _('on line %s') % line_no
68 line = _('on line %s') % line_no
69
69
70 # changeset
70 # changeset
71 if revision:
71 if revision:
72 notification_type = NotificationModel.TYPE_CHANGESET_COMMENT
72 notification_type = NotificationModel.TYPE_CHANGESET_COMMENT
73 cs = repo.scm_instance.get_changeset(revision)
73 cs = repo.scm_instance.get_changeset(revision)
74 desc = cs.short_id
74 desc = cs.short_id
75
75
76 threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())]
76 threading = ['%s-rev-%s@%s' % (repo.repo_name, revision, h.canonical_hostname())]
77 if line_no: # TODO: url to file _and_ line number
77 if line_no: # TODO: url to file _and_ line number
78 threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no,
78 threading.append('%s-rev-%s-line-%s@%s' % (repo.repo_name, revision, line_no,
79 h.canonical_hostname()))
79 h.canonical_hostname()))
80 comment_url = h.canonical_url('changeset_home',
80 comment_url = h.canonical_url('changeset_home',
81 repo_name=repo.repo_name,
81 repo_name=repo.repo_name,
82 revision=revision,
82 revision=revision,
83 anchor='comment-%s' % comment.comment_id)
83 anchor='comment-%s' % comment.comment_id)
84 subj = safe_unicode(
84 subj = safe_unicode(
85 h.link_to('Re changeset: %(desc)s %(line)s' % \
85 h.link_to('Re changeset: %(desc)s %(line)s' % \
86 {'desc': desc, 'line': line},
86 {'desc': desc, 'line': line},
87 comment_url)
87 comment_url)
88 )
88 )
89 # get the current participants of this changeset
89 # get the current participants of this changeset
90 recipients = _list_changeset_commenters(revision)
90 recipients = _list_changeset_commenters(revision)
91 # add changeset author if it's known locally
91 # add changeset author if it's known locally
92 cs_author = User.get_from_cs_author(cs.author)
92 cs_author = User.get_from_cs_author(cs.author)
93 if not cs_author:
93 if not cs_author:
94 # use repo owner if we cannot extract the author correctly
94 # use repo owner if we cannot extract the author correctly
95 # FIXME: just use committer name even if not a user
95 # FIXME: just use committer name even if not a user
96 cs_author = repo.owner
96 cs_author = repo.owner
97 recipients.append(cs_author)
97 recipients.append(cs_author)
98
98
99 email_kwargs = {
99 email_kwargs = {
100 'status_change': status_change,
100 'status_change': status_change,
101 'cs_comment_user': author.full_name_and_username,
101 'cs_comment_user': author.full_name_and_username,
102 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name),
102 'cs_target_repo': h.canonical_url('summary_home', repo_name=repo.repo_name),
103 'cs_comment_url': comment_url,
103 'cs_comment_url': comment_url,
104 'cs_url': h.canonical_url('changeset_home', repo_name=repo.repo_name, revision=revision),
104 'cs_url': h.canonical_url('changeset_home', repo_name=repo.repo_name, revision=revision),
105 'raw_id': revision,
105 'raw_id': revision,
106 'message': cs.message,
106 'message': cs.message,
107 'message_short': h.shorter(cs.message, 50, firstline=True),
107 'message_short': h.shorter(cs.message, 50, firstline=True),
108 'cs_author': cs_author,
108 'cs_author': cs_author,
109 'repo_name': repo.repo_name,
109 'repo_name': repo.repo_name,
110 'short_id': h.short_id(revision),
110 'short_id': h.short_id(revision),
111 'branch': cs.branch,
111 'branch': cs.branch,
112 'comment_username': author.username,
112 'comment_username': author.username,
113 'threading': threading,
113 'threading': threading,
114 }
114 }
115 # pull request
115 # pull request
116 elif pull_request:
116 elif pull_request:
117 notification_type = NotificationModel.TYPE_PULL_REQUEST_COMMENT
117 notification_type = NotificationModel.TYPE_PULL_REQUEST_COMMENT
118 desc = comment.pull_request.title
118 desc = comment.pull_request.title
119 _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':')
119 _org_ref_type, org_ref_name, _org_rev = comment.pull_request.org_ref.split(':')
120 _other_ref_type, other_ref_name, _other_rev = comment.pull_request.other_ref.split(':')
120 _other_ref_type, other_ref_name, _other_rev = comment.pull_request.other_ref.split(':')
121 threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name,
121 threading = ['%s-pr-%s@%s' % (pull_request.other_repo.repo_name,
122 pull_request.pull_request_id,
122 pull_request.pull_request_id,
123 h.canonical_hostname())]
123 h.canonical_hostname())]
124 if line_no: # TODO: url to file _and_ line number
124 if line_no: # TODO: url to file _and_ line number
125 threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name,
125 threading.append('%s-pr-%s-line-%s@%s' % (pull_request.other_repo.repo_name,
126 pull_request.pull_request_id, line_no,
126 pull_request.pull_request_id, line_no,
127 h.canonical_hostname()))
127 h.canonical_hostname()))
128 comment_url = pull_request.url(canonical=True,
128 comment_url = pull_request.url(canonical=True,
129 anchor='comment-%s' % comment.comment_id)
129 anchor='comment-%s' % comment.comment_id)
130 subj = safe_unicode(
130 subj = safe_unicode(
131 h.link_to('Re pull request %(pr_nice_id)s: %(desc)s %(line)s' %
131 h.link_to('Re pull request %(pr_nice_id)s: %(desc)s %(line)s' %
132 {'desc': desc,
132 {'desc': desc,
133 'pr_nice_id': comment.pull_request.nice_id(),
133 'pr_nice_id': comment.pull_request.nice_id(),
134 'line': line},
134 'line': line},
135 comment_url)
135 comment_url)
136 )
136 )
137 # get the current participants of this pull request
137 # get the current participants of this pull request
138 recipients = _list_pull_request_commenters(pull_request)
138 recipients = _list_pull_request_commenters(pull_request)
139 recipients.append(pull_request.owner)
139 recipients.append(pull_request.owner)
140 recipients += pull_request.get_reviewer_users()
140 recipients += pull_request.get_reviewer_users()
141
141
142 # set some variables for email notification
142 # set some variables for email notification
143 email_kwargs = {
143 email_kwargs = {
144 'pr_title': pull_request.title,
144 'pr_title': pull_request.title,
145 'pr_title_short': h.shorter(pull_request.title, 50),
145 'pr_title_short': h.shorter(pull_request.title, 50),
146 'pr_nice_id': pull_request.nice_id(),
146 'pr_nice_id': pull_request.nice_id(),
147 'status_change': status_change,
147 'status_change': status_change,
148 'closing_pr': closing_pr,
148 'closing_pr': closing_pr,
149 'pr_comment_url': comment_url,
149 'pr_comment_url': comment_url,
150 'pr_url': pull_request.url(canonical=True),
150 'pr_url': pull_request.url(canonical=True),
151 'pr_comment_user': author.full_name_and_username,
151 'pr_comment_user': author.full_name_and_username,
152 'pr_target_repo': h.canonical_url('summary_home',
152 'pr_target_repo': h.canonical_url('summary_home',
153 repo_name=pull_request.other_repo.repo_name),
153 repo_name=pull_request.other_repo.repo_name),
154 'pr_target_branch': other_ref_name,
154 'pr_target_branch': other_ref_name,
155 'pr_source_repo': h.canonical_url('summary_home',
155 'pr_source_repo': h.canonical_url('summary_home',
156 repo_name=pull_request.org_repo.repo_name),
156 repo_name=pull_request.org_repo.repo_name),
157 'pr_source_branch': org_ref_name,
157 'pr_source_branch': org_ref_name,
158 'pr_owner': pull_request.owner,
158 'pr_owner': pull_request.owner,
159 'pr_owner_username': pull_request.owner.username,
159 'pr_owner_username': pull_request.owner.username,
160 'repo_name': pull_request.other_repo.repo_name,
160 'repo_name': pull_request.other_repo.repo_name,
161 'comment_username': author.username,
161 'comment_username': author.username,
162 'threading': threading,
162 'threading': threading,
163 }
163 }
164
164
165 return subj, body, recipients, notification_type, email_kwargs
165 return subj, body, recipients, notification_type, email_kwargs
166
166
167 def create(self, text, repo, author, revision=None, pull_request=None,
167 def create(self, text, repo, author, revision=None, pull_request=None,
168 f_path=None, line_no=None, status_change=None, closing_pr=False,
168 f_path=None, line_no=None, status_change=None, closing_pr=False,
169 send_email=True):
169 send_email=True):
170 """
170 """
171 Creates a new comment for either a changeset or a pull request.
171 Creates a new comment for either a changeset or a pull request.
172 status_change and closing_pr is only for the optional email.
172 status_change and closing_pr is only for the optional email.
173
173
174 Returns the created comment.
174 Returns the created comment.
175 """
175 """
176 if not status_change and not text:
176 if not status_change and not text:
177 log.warning('Missing text for comment, skipping...')
177 log.warning('Missing text for comment, skipping...')
178 return None
178 return None
179
179
180 repo = Repository.guess_instance(repo)
180 repo = Repository.guess_instance(repo)
181 author = User.guess_instance(author)
181 author = User.guess_instance(author)
182 comment = ChangesetComment()
182 comment = ChangesetComment()
183 comment.repo = repo
183 comment.repo = repo
184 comment.author = author
184 comment.author = author
185 comment.text = text
185 comment.text = text
186 comment.f_path = f_path
186 comment.f_path = f_path
187 comment.line_no = line_no
187 comment.line_no = line_no
188
188
189 if revision is not None:
189 if revision is not None:
190 comment.revision = revision
190 comment.revision = revision
191 elif pull_request is not None:
191 elif pull_request is not None:
192 pull_request = PullRequest.guess_instance(pull_request)
192 pull_request = PullRequest.guess_instance(pull_request)
193 comment.pull_request = pull_request
193 comment.pull_request = pull_request
194 else:
194 else:
195 raise Exception('Please specify revision or pull_request_id')
195 raise Exception('Please specify revision or pull_request_id')
196
196
197 Session().add(comment)
197 Session().add(comment)
198 Session().flush()
198 Session().flush()
199
199
200 if send_email:
200 if send_email:
201 (subj, body, recipients, notification_type,
201 (subj, body, recipients, notification_type,
202 email_kwargs) = self._get_notification_data(
202 email_kwargs) = self._get_notification_data(
203 repo, comment, author,
203 repo, comment, author,
204 comment_text=text,
204 comment_text=text,
205 line_no=line_no,
205 line_no=line_no,
206 revision=revision,
206 revision=revision,
207 pull_request=pull_request,
207 pull_request=pull_request,
208 status_change=status_change,
208 status_change=status_change,
209 closing_pr=closing_pr)
209 closing_pr=closing_pr)
210 email_kwargs['is_mention'] = False
210 email_kwargs['is_mention'] = False
211 # create notification objects, and emails
211 # create notification objects, and emails
212 NotificationModel().create(
212 NotificationModel().create(
213 created_by=author, subject=subj, body=body,
213 created_by=author, subject=subj, body=body,
214 recipients=recipients, type_=notification_type,
214 recipients=recipients, type_=notification_type,
215 email_kwargs=email_kwargs,
215 email_kwargs=email_kwargs,
216 )
216 )
217
217
218 mention_recipients = extract_mentioned_users(body).difference(recipients)
218 mention_recipients = extract_mentioned_users(body).difference(recipients)
219 if mention_recipients:
219 if mention_recipients:
220 email_kwargs['is_mention'] = True
220 email_kwargs['is_mention'] = True
221 subj = _('[Mention]') + ' ' + subj
221 subj = _('[Mention]') + ' ' + subj
222 # FIXME: this subject is wrong and unused!
222 # FIXME: this subject is wrong and unused!
223 NotificationModel().create(
223 NotificationModel().create(
224 created_by=author, subject=subj, body=body,
224 created_by=author, subject=subj, body=body,
225 recipients=mention_recipients,
225 recipients=mention_recipients,
226 type_=notification_type,
226 type_=notification_type,
227 email_kwargs=email_kwargs
227 email_kwargs=email_kwargs
228 )
228 )
229
229
230 return comment
230 return comment
231
231
232 def delete(self, comment):
232 def delete(self, comment):
233 comment = ChangesetComment.guess_instance(comment)
233 comment = ChangesetComment.guess_instance(comment)
234 Session().delete(comment)
234 Session().delete(comment)
235
235
236 return comment
236 return comment
237
237
238 def get_comments(self, repo_id, revision=None, pull_request=None):
238 def get_comments(self, repo_id, revision=None, pull_request=None):
239 """
239 """
240 Gets general comments for either revision or pull_request.
240 Gets general comments for either revision or pull_request.
241
241
242 Returns a list, ordered by creation date.
242 Returns a list, ordered by creation date.
243 """
243 """
244 return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
244 return self._get_comments(repo_id, revision=revision, pull_request=pull_request,
245 inline=False)
245 inline=False)
246
246
247 def get_inline_comments(self, repo_id, revision=None, pull_request=None):
247 def get_inline_comments(self, repo_id, revision=None, pull_request=None,
248 f_path=None, line_no=None):
248 """
249 """
249 Gets inline comments for either revision or pull_request.
250 Gets inline comments for either revision or pull_request.
250
251
251 Returns a list of tuples with file path and list of comments per line number.
252 Returns a list of tuples with file path and list of comments per line number.
252 """
253 """
253 comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
254 comments = self._get_comments(repo_id, revision=revision, pull_request=pull_request,
254 inline=True)
255 inline=True, f_path=f_path, line_no=line_no)
255
256
256 paths = defaultdict(lambda: defaultdict(list))
257 paths = defaultdict(lambda: defaultdict(list))
257 for co in comments:
258 for co in comments:
258 paths[co.f_path][co.line_no].append(co)
259 paths[co.f_path][co.line_no].append(co)
259 return paths.items()
260 return paths.items()
260
261
261 def _get_comments(self, repo_id, revision=None, pull_request=None, inline=False):
262 def _get_comments(self, repo_id, revision=None, pull_request=None,
263 inline=False, f_path=None, line_no=None):
262 """
264 """
263 Gets comments for either revision or pull_request_id, either inline or general.
265 Gets comments for either revision or pull_request_id, either inline or general.
266 If a file path and optionally line number are given, return only the matching inline comments.
264 """
267 """
268 if f_path is None and line_no is not None:
269 raise Exception("line_no only makes sense if f_path is given.")
270
271 if inline is None and f_path is not None:
272 raise Exception("f_path only makes sense for inline comments.")
273
265 q = Session().query(ChangesetComment)
274 q = Session().query(ChangesetComment)
266
275
267 if inline:
276 if inline:
268 q = q.filter(ChangesetComment.line_no != None) \
277 if f_path is not None:
269 .filter(ChangesetComment.f_path != None)
278 # inline comments for a given file...
279 q = q.filter(ChangesetComment.f_path == f_path)
280 if line_no is None:
281 # ... on any line
282 q = q.filter(ChangesetComment.line_no != None)
283 else:
284 # ... on specific line
285 q = q.filter(ChangesetComment.line_no == line_no)
286 else:
287 # all inline comments
288 q = q.filter(ChangesetComment.line_no != None) \
289 .filter(ChangesetComment.f_path != None)
270 else:
290 else:
291 # all general comments
271 q = q.filter(ChangesetComment.line_no == None) \
292 q = q.filter(ChangesetComment.line_no == None) \
272 .filter(ChangesetComment.f_path == None)
293 .filter(ChangesetComment.f_path == None)
273
294
274 if revision is not None:
295 if revision is not None:
275 q = q.filter(ChangesetComment.revision == revision) \
296 q = q.filter(ChangesetComment.revision == revision) \
276 .filter(ChangesetComment.repo_id == repo_id)
297 .filter(ChangesetComment.repo_id == repo_id)
277 elif pull_request is not None:
298 elif pull_request is not None:
278 pull_request = PullRequest.guess_instance(pull_request)
299 pull_request = PullRequest.guess_instance(pull_request)
279 q = q.filter(ChangesetComment.pull_request == pull_request)
300 q = q.filter(ChangesetComment.pull_request == pull_request)
280 else:
301 else:
281 raise Exception('Please specify either revision or pull_request')
302 raise Exception('Please specify either revision or pull_request')
282
303
283 return q.order_by(ChangesetComment.created_on).all()
304 return q.order_by(ChangesetComment.created_on).all()
@@ -1,155 +1,230 b''
1 from kallithea.tests.base import *
1 from kallithea.tests.base import *
2 from kallithea.model.comment import ChangesetCommentsModel
2 from kallithea.model.comment import ChangesetCommentsModel
3 from kallithea.model.db import Repository
3 from kallithea.model.db import Repository
4
4
5 import pytest
5 from tg.util.webtest import test_context
6 from tg.util.webtest import test_context
6
7
7 class TestComments(TestController):
8 class TestComments(TestController):
8
9
9 def _check_comment_count(self, repo_id, revision, expected_len_comments, expected_len_inline_comments):
10 def _check_comment_count(self, repo_id, revision,
11 expected_len_comments, expected_len_inline_comments,
12 f_path=None, line_no=None
13 ):
10 comments = ChangesetCommentsModel().get_comments(repo_id,
14 comments = ChangesetCommentsModel().get_comments(repo_id,
11 revision=revision)
15 revision=revision)
12 assert len(comments) == expected_len_comments
16 assert len(comments) == expected_len_comments
13 inline_comments = ChangesetCommentsModel().get_inline_comments(repo_id,
17 inline_comments = ChangesetCommentsModel().get_inline_comments(repo_id,
14 revision=revision)
18 revision=revision, f_path=f_path, line_no=line_no)
15 assert len(inline_comments) == expected_len_inline_comments
19 assert len(inline_comments) == expected_len_inline_comments
16
20
17 return comments, inline_comments
21 return comments, inline_comments
18
22
19 def test_create_delete_general_comment(self):
23 def test_create_delete_general_comment(self):
20 with test_context(self.app):
24 with test_context(self.app):
21 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
25 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
22 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
26 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
23
27
24 self._check_comment_count(repo_id, revision,
28 self._check_comment_count(repo_id, revision,
25 expected_len_comments=0, expected_len_inline_comments=0)
29 expected_len_comments=0, expected_len_inline_comments=0)
26
30
27 text = u'a comment'
31 text = u'a comment'
28 new_comment = ChangesetCommentsModel().create(
32 new_comment = ChangesetCommentsModel().create(
29 text=text,
33 text=text,
30 repo=HG_REPO,
34 repo=HG_REPO,
31 author=TEST_USER_REGULAR_LOGIN,
35 author=TEST_USER_REGULAR_LOGIN,
32 revision=revision,
36 revision=revision,
33 send_email=False)
37 send_email=False)
34
38
35 self._check_comment_count(repo_id, revision,
39 self._check_comment_count(repo_id, revision,
36 expected_len_comments=1, expected_len_inline_comments=0)
40 expected_len_comments=1, expected_len_inline_comments=0)
37
41
38 ChangesetCommentsModel().delete(new_comment)
42 ChangesetCommentsModel().delete(new_comment)
39
43
40 self._check_comment_count(repo_id, revision,
44 self._check_comment_count(repo_id, revision,
41 expected_len_comments=0, expected_len_inline_comments=0)
45 expected_len_comments=0, expected_len_inline_comments=0)
42
46
43 def test_create_delete_inline_comment(self):
47 def test_create_delete_inline_comment(self):
44 with test_context(self.app):
48 with test_context(self.app):
45 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
49 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
46 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
50 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
47
51
48 self._check_comment_count(repo_id, revision,
52 self._check_comment_count(repo_id, revision,
49 expected_len_comments=0, expected_len_inline_comments=0)
53 expected_len_comments=0, expected_len_inline_comments=0)
50
54
51 text = u'an inline comment'
55 text = u'an inline comment'
52 f_path = u'vcs/tests/base.py'
56 f_path = u'vcs/tests/base.py'
53 line_no = u'n50'
57 line_no = u'n50'
54 new_comment = ChangesetCommentsModel().create(
58 new_comment = ChangesetCommentsModel().create(
55 text=text,
59 text=text,
56 repo=HG_REPO,
60 repo=HG_REPO,
57 author=TEST_USER_REGULAR_LOGIN,
61 author=TEST_USER_REGULAR_LOGIN,
58 revision=revision,
62 revision=revision,
59 f_path=f_path,
63 f_path=f_path,
60 line_no=line_no,
64 line_no=line_no,
61 send_email=False)
65 send_email=False)
62
66
63 comments, inline_comments = self._check_comment_count(repo_id, revision,
67 comments, inline_comments = self._check_comment_count(repo_id, revision,
64 expected_len_comments=0, expected_len_inline_comments=1)
68 expected_len_comments=0, expected_len_inline_comments=1)
65 # inline_comments is a list of tuples (file_path, dict)
69 # inline_comments is a list of tuples (file_path, dict)
66 # where the dict keys are line numbers and values are lists of comments
70 # where the dict keys are line numbers and values are lists of comments
67 assert inline_comments[0][0] == f_path
71 assert inline_comments[0][0] == f_path
68 assert len(inline_comments[0][1]) == 1
72 assert len(inline_comments[0][1]) == 1
69 assert line_no in inline_comments[0][1]
73 assert line_no in inline_comments[0][1]
70 assert inline_comments[0][1][line_no][0].text == text
74 assert inline_comments[0][1][line_no][0].text == text
71
75
72 ChangesetCommentsModel().delete(new_comment)
76 ChangesetCommentsModel().delete(new_comment)
73
77
74 self._check_comment_count(repo_id, revision,
78 self._check_comment_count(repo_id, revision,
75 expected_len_comments=0, expected_len_inline_comments=0)
79 expected_len_comments=0, expected_len_inline_comments=0)
76
80
77 def test_create_delete_multiple_inline_comments(self):
81 def test_create_delete_multiple_inline_comments(self):
78 with test_context(self.app):
82 with test_context(self.app):
79 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
83 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
80 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
84 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
81
85
82 self._check_comment_count(repo_id, revision,
86 self._check_comment_count(repo_id, revision,
83 expected_len_comments=0, expected_len_inline_comments=0)
87 expected_len_comments=0, expected_len_inline_comments=0)
84
88
85 text = u'an inline comment'
89 text = u'an inline comment'
86 f_path = u'vcs/tests/base.py'
90 f_path = u'vcs/tests/base.py'
87 line_no = u'n50'
91 line_no = u'n50'
88 new_comment = ChangesetCommentsModel().create(
92 new_comment = ChangesetCommentsModel().create(
89 text=text,
93 text=text,
90 repo=HG_REPO,
94 repo=HG_REPO,
91 author=TEST_USER_REGULAR_LOGIN,
95 author=TEST_USER_REGULAR_LOGIN,
92 revision=revision,
96 revision=revision,
93 f_path=f_path,
97 f_path=f_path,
94 line_no=line_no,
98 line_no=line_no,
95 send_email=False)
99 send_email=False)
96
100
97 text2 = u'another inline comment, same file'
101 text2 = u'another inline comment, same file'
98 line_no2 = u'o41'
102 line_no2 = u'o41'
99 new_comment2 = ChangesetCommentsModel().create(
103 new_comment2 = ChangesetCommentsModel().create(
100 text=text2,
104 text=text2,
101 repo=HG_REPO,
105 repo=HG_REPO,
102 author=TEST_USER_REGULAR_LOGIN,
106 author=TEST_USER_REGULAR_LOGIN,
103 revision=revision,
107 revision=revision,
104 f_path=f_path,
108 f_path=f_path,
105 line_no=line_no2,
109 line_no=line_no2,
106 send_email=False)
110 send_email=False)
107
111
108 text3 = u'another inline comment, same file'
112 text3 = u'another inline comment, same file'
109 f_path3 = u'vcs/tests/test_hg.py'
113 f_path3 = u'vcs/tests/test_hg.py'
110 line_no3 = u'n159'
114 line_no3 = u'n159'
111 new_comment3 = ChangesetCommentsModel().create(
115 new_comment3 = ChangesetCommentsModel().create(
112 text=text3,
116 text=text3,
113 repo=HG_REPO,
117 repo=HG_REPO,
114 author=TEST_USER_REGULAR_LOGIN,
118 author=TEST_USER_REGULAR_LOGIN,
115 revision=revision,
119 revision=revision,
116 f_path=f_path3,
120 f_path=f_path3,
117 line_no=line_no3,
121 line_no=line_no3,
118 send_email=False)
122 send_email=False)
119
123
120 comments, inline_comments = self._check_comment_count(repo_id, revision,
124 comments, inline_comments = self._check_comment_count(repo_id, revision,
121 expected_len_comments=0, expected_len_inline_comments=2)
125 expected_len_comments=0, expected_len_inline_comments=2)
122 # inline_comments is a list of tuples (file_path, dict)
126 # inline_comments is a list of tuples (file_path, dict)
123 # where the dict keys are line numbers and values are lists of comments
127 # where the dict keys are line numbers and values are lists of comments
124 assert inline_comments[1][0] == f_path
128 assert inline_comments[1][0] == f_path
125 assert len(inline_comments[1][1]) == 2
129 assert len(inline_comments[1][1]) == 2
126 assert inline_comments[1][1][line_no][0].text == text
130 assert inline_comments[1][1][line_no][0].text == text
127 assert inline_comments[1][1][line_no2][0].text == text2
131 assert inline_comments[1][1][line_no2][0].text == text2
128
132
129 assert inline_comments[0][0] == f_path3
133 assert inline_comments[0][0] == f_path3
130 assert len(inline_comments[0][1]) == 1
134 assert len(inline_comments[0][1]) == 1
131 assert line_no3 in inline_comments[0][1]
135 assert line_no3 in inline_comments[0][1]
132 assert inline_comments[0][1][line_no3][0].text == text3
136 assert inline_comments[0][1][line_no3][0].text == text3
133
137
134 # now delete only one comment
138 # now delete only one comment
135 ChangesetCommentsModel().delete(new_comment2)
139 ChangesetCommentsModel().delete(new_comment2)
136
140
137 comments, inline_comments = self._check_comment_count(repo_id, revision,
141 comments, inline_comments = self._check_comment_count(repo_id, revision,
138 expected_len_comments=0, expected_len_inline_comments=2)
142 expected_len_comments=0, expected_len_inline_comments=2)
139 # inline_comments is a list of tuples (file_path, dict)
143 # inline_comments is a list of tuples (file_path, dict)
140 # where the dict keys are line numbers and values are lists of comments
144 # where the dict keys are line numbers and values are lists of comments
141 assert inline_comments[1][0] == f_path
145 assert inline_comments[1][0] == f_path
142 assert len(inline_comments[1][1]) == 1
146 assert len(inline_comments[1][1]) == 1
143 assert inline_comments[1][1][line_no][0].text == text
147 assert inline_comments[1][1][line_no][0].text == text
144
148
145 assert inline_comments[0][0] == f_path3
149 assert inline_comments[0][0] == f_path3
146 assert len(inline_comments[0][1]) == 1
150 assert len(inline_comments[0][1]) == 1
147 assert line_no3 in inline_comments[0][1]
151 assert line_no3 in inline_comments[0][1]
148 assert inline_comments[0][1][line_no3][0].text == text3
152 assert inline_comments[0][1][line_no3][0].text == text3
149
153
150 # now delete all others
154 # now delete all others
151 ChangesetCommentsModel().delete(new_comment)
155 ChangesetCommentsModel().delete(new_comment)
152 ChangesetCommentsModel().delete(new_comment3)
156 ChangesetCommentsModel().delete(new_comment3)
153
157
154 self._check_comment_count(repo_id, revision,
158 self._check_comment_count(repo_id, revision,
155 expected_len_comments=0, expected_len_inline_comments=0)
159 expected_len_comments=0, expected_len_inline_comments=0)
160
161 def test_selective_retrieval_of_inline_comments(self):
162 with test_context(self.app):
163 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
164 revision = '9a7b4ff9e8b40bbda72fc75f162325b9baa45cda'
165
166 self._check_comment_count(repo_id, revision,
167 expected_len_comments=0, expected_len_inline_comments=0)
168
169 text = u'an inline comment'
170 f_path = u'vcs/tests/base.py'
171 line_no = u'n50'
172 new_comment = ChangesetCommentsModel().create(
173 text=text,
174 repo=HG_REPO,
175 author=TEST_USER_REGULAR_LOGIN,
176 revision=revision,
177 f_path=f_path,
178 line_no=line_no,
179 send_email=False)
180
181 text2 = u'another inline comment, same file'
182 line_no2 = u'o41'
183 new_comment2 = ChangesetCommentsModel().create(
184 text=text2,
185 repo=HG_REPO,
186 author=TEST_USER_REGULAR_LOGIN,
187 revision=revision,
188 f_path=f_path,
189 line_no=line_no2,
190 send_email=False)
191
192 text3 = u'another inline comment, same file'
193 f_path3 = u'vcs/tests/test_hg.py'
194 line_no3 = u'n159'
195 new_comment3 = ChangesetCommentsModel().create(
196 text=text3,
197 repo=HG_REPO,
198 author=TEST_USER_REGULAR_LOGIN,
199 revision=revision,
200 f_path=f_path3,
201 line_no=line_no3,
202 send_email=False)
203
204 # now selectively retrieve comments of one file
205 comments, inline_comments = self._check_comment_count(repo_id, revision,
206 f_path=f_path,
207 expected_len_comments=0, expected_len_inline_comments=1)
208 # inline_comments is a list of tuples (file_path, dict)
209 # where the dict keys are line numbers and values are lists of comments
210 assert inline_comments[0][0] == f_path
211 assert len(inline_comments[0][1]) == 2
212 assert inline_comments[0][1][line_no][0].text == text
213 assert inline_comments[0][1][line_no2][0].text == text2
214
215 # now selectively retrieve comments of one file, one line
216 comments, inline_comments = self._check_comment_count(repo_id, revision,
217 f_path=f_path, line_no=line_no2,
218 expected_len_comments=0, expected_len_inline_comments=1)
219 # inline_comments is a list of tuples (file_path, dict)
220 # where the dict keys are line numbers and values are lists of comments
221 assert inline_comments[0][0] == f_path
222 assert len(inline_comments[0][1]) == 1
223 assert inline_comments[0][1][line_no2][0].text == text2
224
225 # verify that retrieval based on line_no but no f_path fails
226 with pytest.raises(Exception) as excinfo:
227 self._check_comment_count(repo_id, revision,
228 f_path=None, line_no=line_no2,
229 expected_len_comments=0, expected_len_inline_comments=0)
230 assert 'line_no only makes sense if f_path is given' in str(excinfo.value)
General Comments 0
You need to be logged in to leave comments. Login now