##// END OF EJS Templates
observers: code cleanups and fixed tests.
marcink -
r4519:ea50ffa9 stable
parent child
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,368 +1,368
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.model.db import User
23 from rhodecode.model.db import User
24 from rhodecode.model.pull_request import PullRequestModel
24 from rhodecode.model.pull_request import PullRequestModel
25 from rhodecode.model.repo import RepoModel
25 from rhodecode.model.repo import RepoModel
26 from rhodecode.model.user import UserModel
26 from rhodecode.model.user import UserModel
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
27 from rhodecode.tests import TEST_USER_ADMIN_LOGIN, TEST_USER_REGULAR_LOGIN
28 from rhodecode.api.tests.utils import build_data, api_call, assert_error
28 from rhodecode.api.tests.utils import build_data, api_call, assert_error
29
29
30
30
31 @pytest.mark.usefixtures("testuser_api", "app")
31 @pytest.mark.usefixtures("testuser_api", "app")
32 class TestCreatePullRequestApi(object):
32 class TestCreatePullRequestApi(object):
33 finalizers = []
33 finalizers = []
34
34
35 def teardown_method(self, method):
35 def teardown_method(self, method):
36 if self.finalizers:
36 if self.finalizers:
37 for finalizer in self.finalizers:
37 for finalizer in self.finalizers:
38 finalizer()
38 finalizer()
39 self.finalizers = []
39 self.finalizers = []
40
40
41 def test_create_with_wrong_data(self):
41 def test_create_with_wrong_data(self):
42 required_data = {
42 required_data = {
43 'source_repo': 'tests/source_repo',
43 'source_repo': 'tests/source_repo',
44 'target_repo': 'tests/target_repo',
44 'target_repo': 'tests/target_repo',
45 'source_ref': 'branch:default:initial',
45 'source_ref': 'branch:default:initial',
46 'target_ref': 'branch:default:new-feature',
46 'target_ref': 'branch:default:new-feature',
47 }
47 }
48 for key in required_data:
48 for key in required_data:
49 data = required_data.copy()
49 data = required_data.copy()
50 data.pop(key)
50 data.pop(key)
51 id_, params = build_data(
51 id_, params = build_data(
52 self.apikey, 'create_pull_request', **data)
52 self.apikey, 'create_pull_request', **data)
53 response = api_call(self.app, params)
53 response = api_call(self.app, params)
54
54
55 expected = 'Missing non optional `{}` arg in JSON DATA'.format(key)
55 expected = 'Missing non optional `{}` arg in JSON DATA'.format(key)
56 assert_error(id_, expected, given=response.body)
56 assert_error(id_, expected, given=response.body)
57
57
58 @pytest.mark.backends("git", "hg")
58 @pytest.mark.backends("git", "hg")
59 @pytest.mark.parametrize('source_ref', [
59 @pytest.mark.parametrize('source_ref', [
60 'bookmarg:default:initial'
60 'bookmarg:default:initial'
61 ])
61 ])
62 def test_create_with_wrong_refs_data(self, backend, source_ref):
62 def test_create_with_wrong_refs_data(self, backend, source_ref):
63
63
64 data = self._prepare_data(backend)
64 data = self._prepare_data(backend)
65 data['source_ref'] = source_ref
65 data['source_ref'] = source_ref
66
66
67 id_, params = build_data(
67 id_, params = build_data(
68 self.apikey_regular, 'create_pull_request', **data)
68 self.apikey_regular, 'create_pull_request', **data)
69
69
70 response = api_call(self.app, params)
70 response = api_call(self.app, params)
71
71
72 expected = "Ref `{}` type is not allowed. " \
72 expected = "Ref `{}` type is not allowed. " \
73 "Only:['bookmark', 'book', 'tag', 'branch'] " \
73 "Only:['bookmark', 'book', 'tag', 'branch'] " \
74 "are possible.".format(source_ref)
74 "are possible.".format(source_ref)
75 assert_error(id_, expected, given=response.body)
75 assert_error(id_, expected, given=response.body)
76
76
77 @pytest.mark.backends("git", "hg")
77 @pytest.mark.backends("git", "hg")
78 def test_create_with_correct_data(self, backend):
78 def test_create_with_correct_data(self, backend):
79 data = self._prepare_data(backend)
79 data = self._prepare_data(backend)
80 RepoModel().revoke_user_permission(
80 RepoModel().revoke_user_permission(
81 self.source.repo_name, User.DEFAULT_USER)
81 self.source.repo_name, User.DEFAULT_USER)
82 id_, params = build_data(
82 id_, params = build_data(
83 self.apikey_regular, 'create_pull_request', **data)
83 self.apikey_regular, 'create_pull_request', **data)
84 response = api_call(self.app, params)
84 response = api_call(self.app, params)
85 expected_message = "Created new pull request `{title}`".format(
85 expected_message = "Created new pull request `{title}`".format(
86 title=data['title'])
86 title=data['title'])
87 result = response.json
87 result = response.json
88 assert result['error'] is None
88 assert result['error'] is None
89 assert result['result']['msg'] == expected_message
89 assert result['result']['msg'] == expected_message
90 pull_request_id = result['result']['pull_request_id']
90 pull_request_id = result['result']['pull_request_id']
91 pull_request = PullRequestModel().get(pull_request_id)
91 pull_request = PullRequestModel().get(pull_request_id)
92 assert pull_request.title == data['title']
92 assert pull_request.title == data['title']
93 assert pull_request.description == data['description']
93 assert pull_request.description == data['description']
94 assert pull_request.source_ref == data['source_ref']
94 assert pull_request.source_ref == data['source_ref']
95 assert pull_request.target_ref == data['target_ref']
95 assert pull_request.target_ref == data['target_ref']
96 assert pull_request.source_repo.repo_name == data['source_repo']
96 assert pull_request.source_repo.repo_name == data['source_repo']
97 assert pull_request.target_repo.repo_name == data['target_repo']
97 assert pull_request.target_repo.repo_name == data['target_repo']
98 assert pull_request.revisions == [self.commit_ids['change']]
98 assert pull_request.revisions == [self.commit_ids['change']]
99 assert len(pull_request.reviewers) == 1
99 assert len(pull_request.reviewers) == 1
100
100
101 @pytest.mark.backends("git", "hg")
101 @pytest.mark.backends("git", "hg")
102 def test_create_with_empty_description(self, backend):
102 def test_create_with_empty_description(self, backend):
103 data = self._prepare_data(backend)
103 data = self._prepare_data(backend)
104 data.pop('description')
104 data.pop('description')
105 id_, params = build_data(
105 id_, params = build_data(
106 self.apikey_regular, 'create_pull_request', **data)
106 self.apikey_regular, 'create_pull_request', **data)
107 response = api_call(self.app, params)
107 response = api_call(self.app, params)
108 expected_message = "Created new pull request `{title}`".format(
108 expected_message = "Created new pull request `{title}`".format(
109 title=data['title'])
109 title=data['title'])
110 result = response.json
110 result = response.json
111 assert result['error'] is None
111 assert result['error'] is None
112 assert result['result']['msg'] == expected_message
112 assert result['result']['msg'] == expected_message
113 pull_request_id = result['result']['pull_request_id']
113 pull_request_id = result['result']['pull_request_id']
114 pull_request = PullRequestModel().get(pull_request_id)
114 pull_request = PullRequestModel().get(pull_request_id)
115 assert pull_request.description == ''
115 assert pull_request.description == ''
116
116
117 @pytest.mark.backends("git", "hg")
117 @pytest.mark.backends("git", "hg")
118 def test_create_with_empty_title(self, backend):
118 def test_create_with_empty_title(self, backend):
119 data = self._prepare_data(backend)
119 data = self._prepare_data(backend)
120 data.pop('title')
120 data.pop('title')
121 id_, params = build_data(
121 id_, params = build_data(
122 self.apikey_regular, 'create_pull_request', **data)
122 self.apikey_regular, 'create_pull_request', **data)
123 response = api_call(self.app, params)
123 response = api_call(self.app, params)
124 result = response.json
124 result = response.json
125 pull_request_id = result['result']['pull_request_id']
125 pull_request_id = result['result']['pull_request_id']
126 pull_request = PullRequestModel().get(pull_request_id)
126 pull_request = PullRequestModel().get(pull_request_id)
127 data['ref'] = backend.default_branch_name
127 data['ref'] = backend.default_branch_name
128 title = '{source_repo}#{ref} to {target_repo}'.format(**data)
128 title = '{source_repo}#{ref} to {target_repo}'.format(**data)
129 assert pull_request.title == title
129 assert pull_request.title == title
130
130
131 @pytest.mark.backends("git", "hg")
131 @pytest.mark.backends("git", "hg")
132 def test_create_with_reviewers_specified_by_names(
132 def test_create_with_reviewers_specified_by_names(
133 self, backend, no_notifications):
133 self, backend, no_notifications):
134 data = self._prepare_data(backend)
134 data = self._prepare_data(backend)
135 reviewers = [
135 reviewers = [
136 {'username': TEST_USER_REGULAR_LOGIN,
136 {'username': TEST_USER_REGULAR_LOGIN,
137 'reasons': ['{} added manually'.format(TEST_USER_REGULAR_LOGIN)]},
137 'reasons': ['{} added manually'.format(TEST_USER_REGULAR_LOGIN)]},
138 {'username': TEST_USER_ADMIN_LOGIN,
138 {'username': TEST_USER_ADMIN_LOGIN,
139 'reasons': ['{} added manually'.format(TEST_USER_ADMIN_LOGIN)],
139 'reasons': ['{} added manually'.format(TEST_USER_ADMIN_LOGIN)],
140 'mandatory': True},
140 'mandatory': True},
141 ]
141 ]
142 data['reviewers'] = reviewers
142 data['reviewers'] = reviewers
143
143
144 id_, params = build_data(
144 id_, params = build_data(
145 self.apikey_regular, 'create_pull_request', **data)
145 self.apikey_regular, 'create_pull_request', **data)
146 response = api_call(self.app, params)
146 response = api_call(self.app, params)
147
147
148 expected_message = "Created new pull request `{title}`".format(
148 expected_message = "Created new pull request `{title}`".format(
149 title=data['title'])
149 title=data['title'])
150 result = response.json
150 result = response.json
151 assert result['error'] is None
151 assert result['error'] is None
152 assert result['result']['msg'] == expected_message
152 assert result['result']['msg'] == expected_message
153 pull_request_id = result['result']['pull_request_id']
153 pull_request_id = result['result']['pull_request_id']
154 pull_request = PullRequestModel().get(pull_request_id)
154 pull_request = PullRequestModel().get(pull_request_id)
155
155
156 actual_reviewers = []
156 actual_reviewers = []
157 for rev in pull_request.reviewers:
157 for rev in pull_request.reviewers:
158 entry = {
158 entry = {
159 'username': rev.user.username,
159 'username': rev.user.username,
160 'reasons': rev.reasons,
160 'reasons': rev.reasons,
161 }
161 }
162 if rev.mandatory:
162 if rev.mandatory:
163 entry['mandatory'] = rev.mandatory
163 entry['mandatory'] = rev.mandatory
164 actual_reviewers.append(entry)
164 actual_reviewers.append(entry)
165
165
166 owner_username = pull_request.target_repo.user.username
166 owner_username = pull_request.target_repo.user.username
167 for spec_reviewer in reviewers[::]:
167 for spec_reviewer in reviewers[::]:
168 # default reviewer will be added who is an owner of the repo
168 # default reviewer will be added who is an owner of the repo
169 # this get's overridden by a add owner to reviewers rule
169 # this get's overridden by a add owner to reviewers rule
170 if spec_reviewer['username'] == owner_username:
170 if spec_reviewer['username'] == owner_username:
171 spec_reviewer['reasons'] = [u'Default reviewer', u'Repository owner']
171 spec_reviewer['reasons'] = [u'Default reviewer', u'Repository owner']
172 # since owner is more important, we don't inherit mandatory flag
172 # since owner is more important, we don't inherit mandatory flag
173 del spec_reviewer['mandatory']
173 del spec_reviewer['mandatory']
174
174
175 assert sorted(actual_reviewers, key=lambda e: e['username']) \
175 assert sorted(actual_reviewers, key=lambda e: e['username']) \
176 == sorted(reviewers, key=lambda e: e['username'])
176 == sorted(reviewers, key=lambda e: e['username'])
177
177
178 @pytest.mark.backends("git", "hg")
178 @pytest.mark.backends("git", "hg")
179 def test_create_with_reviewers_specified_by_ids(
179 def test_create_with_reviewers_specified_by_ids(
180 self, backend, no_notifications):
180 self, backend, no_notifications):
181 data = self._prepare_data(backend)
181 data = self._prepare_data(backend)
182 reviewers = [
182 reviewers = [
183 {'username': UserModel().get_by_username(
183 {'username': UserModel().get_by_username(
184 TEST_USER_REGULAR_LOGIN).user_id,
184 TEST_USER_REGULAR_LOGIN).user_id,
185 'reasons': ['added manually']},
185 'reasons': ['added manually']},
186 {'username': UserModel().get_by_username(
186 {'username': UserModel().get_by_username(
187 TEST_USER_ADMIN_LOGIN).user_id,
187 TEST_USER_ADMIN_LOGIN).user_id,
188 'reasons': ['added manually']},
188 'reasons': ['added manually']},
189 ]
189 ]
190
190
191 data['reviewers'] = reviewers
191 data['reviewers'] = reviewers
192 id_, params = build_data(
192 id_, params = build_data(
193 self.apikey_regular, 'create_pull_request', **data)
193 self.apikey_regular, 'create_pull_request', **data)
194 response = api_call(self.app, params)
194 response = api_call(self.app, params)
195
195
196 expected_message = "Created new pull request `{title}`".format(
196 expected_message = "Created new pull request `{title}`".format(
197 title=data['title'])
197 title=data['title'])
198 result = response.json
198 result = response.json
199 assert result['error'] is None
199 assert result['error'] is None
200 assert result['result']['msg'] == expected_message
200 assert result['result']['msg'] == expected_message
201 pull_request_id = result['result']['pull_request_id']
201 pull_request_id = result['result']['pull_request_id']
202 pull_request = PullRequestModel().get(pull_request_id)
202 pull_request = PullRequestModel().get(pull_request_id)
203
203
204 actual_reviewers = []
204 actual_reviewers = []
205 for rev in pull_request.reviewers:
205 for rev in pull_request.reviewers:
206 entry = {
206 entry = {
207 'username': rev.user.user_id,
207 'username': rev.user.user_id,
208 'reasons': rev.reasons,
208 'reasons': rev.reasons,
209 }
209 }
210 if rev.mandatory:
210 if rev.mandatory:
211 entry['mandatory'] = rev.mandatory
211 entry['mandatory'] = rev.mandatory
212 actual_reviewers.append(entry)
212 actual_reviewers.append(entry)
213
213
214 owner_user_id = pull_request.target_repo.user.user_id
214 owner_user_id = pull_request.target_repo.user.user_id
215 for spec_reviewer in reviewers[::]:
215 for spec_reviewer in reviewers[::]:
216 # default reviewer will be added who is an owner of the repo
216 # default reviewer will be added who is an owner of the repo
217 # this get's overridden by a add owner to reviewers rule
217 # this get's overridden by a add owner to reviewers rule
218 if spec_reviewer['username'] == owner_user_id:
218 if spec_reviewer['username'] == owner_user_id:
219 spec_reviewer['reasons'] = [u'Default reviewer', u'Repository owner']
219 spec_reviewer['reasons'] = [u'Default reviewer', u'Repository owner']
220
220
221 assert sorted(actual_reviewers, key=lambda e: e['username']) \
221 assert sorted(actual_reviewers, key=lambda e: e['username']) \
222 == sorted(reviewers, key=lambda e: e['username'])
222 == sorted(reviewers, key=lambda e: e['username'])
223
223
224 @pytest.mark.backends("git", "hg")
224 @pytest.mark.backends("git", "hg")
225 def test_create_fails_when_the_reviewer_is_not_found(self, backend):
225 def test_create_fails_when_the_reviewer_is_not_found(self, backend):
226 data = self._prepare_data(backend)
226 data = self._prepare_data(backend)
227 data['reviewers'] = [{'username': 'somebody'}]
227 data['reviewers'] = [{'username': 'somebody'}]
228 id_, params = build_data(
228 id_, params = build_data(
229 self.apikey_regular, 'create_pull_request', **data)
229 self.apikey_regular, 'create_pull_request', **data)
230 response = api_call(self.app, params)
230 response = api_call(self.app, params)
231 expected_message = 'user `somebody` does not exist'
231 expected_message = 'user `somebody` does not exist'
232 assert_error(id_, expected_message, given=response.body)
232 assert_error(id_, expected_message, given=response.body)
233
233
234 @pytest.mark.backends("git", "hg")
234 @pytest.mark.backends("git", "hg")
235 def test_cannot_create_with_reviewers_in_wrong_format(self, backend):
235 def test_cannot_create_with_reviewers_in_wrong_format(self, backend):
236 data = self._prepare_data(backend)
236 data = self._prepare_data(backend)
237 reviewers = ','.join([TEST_USER_REGULAR_LOGIN, TEST_USER_ADMIN_LOGIN])
237 reviewers = ','.join([TEST_USER_REGULAR_LOGIN, TEST_USER_ADMIN_LOGIN])
238 data['reviewers'] = reviewers
238 data['reviewers'] = reviewers
239 id_, params = build_data(
239 id_, params = build_data(
240 self.apikey_regular, 'create_pull_request', **data)
240 self.apikey_regular, 'create_pull_request', **data)
241 response = api_call(self.app, params)
241 response = api_call(self.app, params)
242 expected_message = {u'': '"test_regular,test_admin" is not iterable'}
242 expected_message = {u'': '"test_regular,test_admin" is not iterable'}
243 assert_error(id_, expected_message, given=response.body)
243 assert_error(id_, expected_message, given=response.body)
244
244
245 @pytest.mark.backends("git", "hg")
245 @pytest.mark.backends("git", "hg")
246 def test_create_with_no_commit_hashes(self, backend):
246 def test_create_with_no_commit_hashes(self, backend):
247 data = self._prepare_data(backend)
247 data = self._prepare_data(backend)
248 expected_source_ref = data['source_ref']
248 expected_source_ref = data['source_ref']
249 expected_target_ref = data['target_ref']
249 expected_target_ref = data['target_ref']
250 data['source_ref'] = 'branch:{}'.format(backend.default_branch_name)
250 data['source_ref'] = 'branch:{}'.format(backend.default_branch_name)
251 data['target_ref'] = 'branch:{}'.format(backend.default_branch_name)
251 data['target_ref'] = 'branch:{}'.format(backend.default_branch_name)
252 id_, params = build_data(
252 id_, params = build_data(
253 self.apikey_regular, 'create_pull_request', **data)
253 self.apikey_regular, 'create_pull_request', **data)
254 response = api_call(self.app, params)
254 response = api_call(self.app, params)
255 expected_message = "Created new pull request `{title}`".format(
255 expected_message = "Created new pull request `{title}`".format(
256 title=data['title'])
256 title=data['title'])
257 result = response.json
257 result = response.json
258 assert result['result']['msg'] == expected_message
258 assert result['result']['msg'] == expected_message
259 pull_request_id = result['result']['pull_request_id']
259 pull_request_id = result['result']['pull_request_id']
260 pull_request = PullRequestModel().get(pull_request_id)
260 pull_request = PullRequestModel().get(pull_request_id)
261 assert pull_request.source_ref == expected_source_ref
261 assert pull_request.source_ref == expected_source_ref
262 assert pull_request.target_ref == expected_target_ref
262 assert pull_request.target_ref == expected_target_ref
263
263
264 @pytest.mark.backends("git", "hg")
264 @pytest.mark.backends("git", "hg")
265 @pytest.mark.parametrize("data_key", ["source_repo", "target_repo"])
265 @pytest.mark.parametrize("data_key", ["source_repo", "target_repo"])
266 def test_create_fails_with_wrong_repo(self, backend, data_key):
266 def test_create_fails_with_wrong_repo(self, backend, data_key):
267 repo_name = 'fake-repo'
267 repo_name = 'fake-repo'
268 data = self._prepare_data(backend)
268 data = self._prepare_data(backend)
269 data[data_key] = repo_name
269 data[data_key] = repo_name
270 id_, params = build_data(
270 id_, params = build_data(
271 self.apikey_regular, 'create_pull_request', **data)
271 self.apikey_regular, 'create_pull_request', **data)
272 response = api_call(self.app, params)
272 response = api_call(self.app, params)
273 expected_message = 'repository `{}` does not exist'.format(repo_name)
273 expected_message = 'repository `{}` does not exist'.format(repo_name)
274 assert_error(id_, expected_message, given=response.body)
274 assert_error(id_, expected_message, given=response.body)
275
275
276 @pytest.mark.backends("git", "hg")
276 @pytest.mark.backends("git", "hg")
277 @pytest.mark.parametrize("data_key", ["source_ref", "target_ref"])
277 @pytest.mark.parametrize("data_key", ["source_ref", "target_ref"])
278 def test_create_fails_with_non_existing_branch(self, backend, data_key):
278 def test_create_fails_with_non_existing_branch(self, backend, data_key):
279 branch_name = 'test-branch'
279 branch_name = 'test-branch'
280 data = self._prepare_data(backend)
280 data = self._prepare_data(backend)
281 data[data_key] = "branch:{}".format(branch_name)
281 data[data_key] = "branch:{}".format(branch_name)
282 id_, params = build_data(
282 id_, params = build_data(
283 self.apikey_regular, 'create_pull_request', **data)
283 self.apikey_regular, 'create_pull_request', **data)
284 response = api_call(self.app, params)
284 response = api_call(self.app, params)
285 expected_message = 'The specified value:{type}:`{name}` ' \
285 expected_message = 'The specified value:{type}:`{name}` ' \
286 'does not exist, or is not allowed.'.format(type='branch',
286 'does not exist, or is not allowed.'.format(type='branch',
287 name=branch_name)
287 name=branch_name)
288 assert_error(id_, expected_message, given=response.body)
288 assert_error(id_, expected_message, given=response.body)
289
289
290 @pytest.mark.backends("git", "hg")
290 @pytest.mark.backends("git", "hg")
291 @pytest.mark.parametrize("data_key", ["source_ref", "target_ref"])
291 @pytest.mark.parametrize("data_key", ["source_ref", "target_ref"])
292 def test_create_fails_with_ref_in_a_wrong_format(self, backend, data_key):
292 def test_create_fails_with_ref_in_a_wrong_format(self, backend, data_key):
293 data = self._prepare_data(backend)
293 data = self._prepare_data(backend)
294 ref = 'stange-ref'
294 ref = 'stange-ref'
295 data[data_key] = ref
295 data[data_key] = ref
296 id_, params = build_data(
296 id_, params = build_data(
297 self.apikey_regular, 'create_pull_request', **data)
297 self.apikey_regular, 'create_pull_request', **data)
298 response = api_call(self.app, params)
298 response = api_call(self.app, params)
299 expected_message = (
299 expected_message = (
300 'Ref `{ref}` given in a wrong format. Please check the API'
300 'Ref `{ref}` given in a wrong format. Please check the API'
301 ' documentation for more details'.format(ref=ref))
301 ' documentation for more details'.format(ref=ref))
302 assert_error(id_, expected_message, given=response.body)
302 assert_error(id_, expected_message, given=response.body)
303
303
304 @pytest.mark.backends("git", "hg")
304 @pytest.mark.backends("git", "hg")
305 @pytest.mark.parametrize("data_key", ["source_ref", "target_ref"])
305 @pytest.mark.parametrize("data_key", ["source_ref", "target_ref"])
306 def test_create_fails_with_non_existing_ref(self, backend, data_key):
306 def test_create_fails_with_non_existing_ref(self, backend, data_key):
307 commit_id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
307 commit_id = 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa10'
308 ref = self._get_full_ref(backend, commit_id)
308 ref = self._get_full_ref(backend, commit_id)
309 data = self._prepare_data(backend)
309 data = self._prepare_data(backend)
310 data[data_key] = ref
310 data[data_key] = ref
311 id_, params = build_data(
311 id_, params = build_data(
312 self.apikey_regular, 'create_pull_request', **data)
312 self.apikey_regular, 'create_pull_request', **data)
313 response = api_call(self.app, params)
313 response = api_call(self.app, params)
314 expected_message = 'Ref `{}` does not exist'.format(ref)
314 expected_message = 'Ref `{}` does not exist'.format(ref)
315 assert_error(id_, expected_message, given=response.body)
315 assert_error(id_, expected_message, given=response.body)
316
316
317 @pytest.mark.backends("git", "hg")
317 @pytest.mark.backends("git", "hg")
318 def test_create_fails_when_no_revisions(self, backend):
318 def test_create_fails_when_no_revisions(self, backend):
319 data = self._prepare_data(backend, source_head='initial')
319 data = self._prepare_data(backend, source_head='initial')
320 id_, params = build_data(
320 id_, params = build_data(
321 self.apikey_regular, 'create_pull_request', **data)
321 self.apikey_regular, 'create_pull_request', **data)
322 response = api_call(self.app, params)
322 response = api_call(self.app, params)
323 expected_message = 'no commits found'
323 expected_message = 'no commits found for merge between specified references'
324 assert_error(id_, expected_message, given=response.body)
324 assert_error(id_, expected_message, given=response.body)
325
325
326 @pytest.mark.backends("git", "hg")
326 @pytest.mark.backends("git", "hg")
327 def test_create_fails_when_no_permissions(self, backend):
327 def test_create_fails_when_no_permissions(self, backend):
328 data = self._prepare_data(backend)
328 data = self._prepare_data(backend)
329 RepoModel().revoke_user_permission(
329 RepoModel().revoke_user_permission(
330 self.source.repo_name, self.test_user)
330 self.source.repo_name, self.test_user)
331 RepoModel().revoke_user_permission(
331 RepoModel().revoke_user_permission(
332 self.source.repo_name, User.DEFAULT_USER)
332 self.source.repo_name, User.DEFAULT_USER)
333
333
334 id_, params = build_data(
334 id_, params = build_data(
335 self.apikey_regular, 'create_pull_request', **data)
335 self.apikey_regular, 'create_pull_request', **data)
336 response = api_call(self.app, params)
336 response = api_call(self.app, params)
337 expected_message = 'repository `{}` does not exist'.format(
337 expected_message = 'repository `{}` does not exist'.format(
338 self.source.repo_name)
338 self.source.repo_name)
339 assert_error(id_, expected_message, given=response.body)
339 assert_error(id_, expected_message, given=response.body)
340
340
341 def _prepare_data(
341 def _prepare_data(
342 self, backend, source_head='change', target_head='initial'):
342 self, backend, source_head='change', target_head='initial'):
343 commits = [
343 commits = [
344 {'message': 'initial'},
344 {'message': 'initial'},
345 {'message': 'change'},
345 {'message': 'change'},
346 {'message': 'new-feature', 'parents': ['initial']},
346 {'message': 'new-feature', 'parents': ['initial']},
347 ]
347 ]
348 self.commit_ids = backend.create_master_repo(commits)
348 self.commit_ids = backend.create_master_repo(commits)
349 self.source = backend.create_repo(heads=[source_head])
349 self.source = backend.create_repo(heads=[source_head])
350 self.target = backend.create_repo(heads=[target_head])
350 self.target = backend.create_repo(heads=[target_head])
351
351
352 data = {
352 data = {
353 'source_repo': self.source.repo_name,
353 'source_repo': self.source.repo_name,
354 'target_repo': self.target.repo_name,
354 'target_repo': self.target.repo_name,
355 'source_ref': self._get_full_ref(
355 'source_ref': self._get_full_ref(
356 backend, self.commit_ids[source_head]),
356 backend, self.commit_ids[source_head]),
357 'target_ref': self._get_full_ref(
357 'target_ref': self._get_full_ref(
358 backend, self.commit_ids[target_head]),
358 backend, self.commit_ids[target_head]),
359 'title': 'Test PR 1',
359 'title': 'Test PR 1',
360 'description': 'Test'
360 'description': 'Test'
361 }
361 }
362 RepoModel().grant_user_permission(
362 RepoModel().grant_user_permission(
363 self.source.repo_name, self.TEST_USER_LOGIN, 'repository.read')
363 self.source.repo_name, self.TEST_USER_LOGIN, 'repository.read')
364 return data
364 return data
365
365
366 def _get_full_ref(self, backend, commit_id):
366 def _get_full_ref(self, backend, commit_id):
367 return 'branch:{branch}:{commit_id}'.format(
367 return 'branch:{branch}:{commit_id}'.format(
368 branch=backend.default_branch_name, commit_id=commit_id)
368 branch=backend.default_branch_name, commit_id=commit_id)
@@ -1,80 +1,82
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pytest
22 import pytest
23
23
24 from rhodecode.model.meta import Session
24 from rhodecode.model.meta import Session
25 from rhodecode.model.pull_request import PullRequestModel
25 from rhodecode.model.pull_request import PullRequestModel
26 from rhodecode.api.tests.utils import (
26 from rhodecode.api.tests.utils import (
27 build_data, api_call, assert_error)
27 build_data, api_call, assert_error)
28
28
29
29
30 @pytest.mark.usefixtures("testuser_api", "app")
30 @pytest.mark.usefixtures("testuser_api", "app")
31 class TestGetPullRequest(object):
31 class TestGetPullRequest(object):
32
32 @pytest.mark.backends("git", "hg")
33 @pytest.mark.backends("git", "hg")
33 def test_api_get_pull_requests(self, pr_util):
34 def test_api_get_pull_requests(self, pr_util):
34 pull_request = pr_util.create_pull_request()
35 pull_request = pr_util.create_pull_request()
35 pull_request_2 = PullRequestModel().create(
36 pull_request_2 = PullRequestModel().create(
36 created_by=pull_request.author,
37 created_by=pull_request.author,
37 source_repo=pull_request.source_repo,
38 source_repo=pull_request.source_repo,
38 source_ref=pull_request.source_ref,
39 source_ref=pull_request.source_ref,
39 target_repo=pull_request.target_repo,
40 target_repo=pull_request.target_repo,
40 target_ref=pull_request.target_ref,
41 target_ref=pull_request.target_ref,
41 revisions=pull_request.revisions,
42 revisions=pull_request.revisions,
42 reviewers=(),
43 reviewers=(),
44 observers=(),
43 title=pull_request.title,
45 title=pull_request.title,
44 description=pull_request.description,
46 description=pull_request.description,
45 )
47 )
46 Session().commit()
48 Session().commit()
47 id_, params = build_data(
49 id_, params = build_data(
48 self.apikey, 'get_pull_requests',
50 self.apikey, 'get_pull_requests',
49 repoid=pull_request.target_repo.repo_name)
51 repoid=pull_request.target_repo.repo_name)
50 response = api_call(self.app, params)
52 response = api_call(self.app, params)
51 assert response.status == '200 OK'
53 assert response.status == '200 OK'
52 assert len(response.json['result']) == 2
54 assert len(response.json['result']) == 2
53
55
54 PullRequestModel().close_pull_request(
56 PullRequestModel().close_pull_request(
55 pull_request_2, pull_request_2.author)
57 pull_request_2, pull_request_2.author)
56 Session().commit()
58 Session().commit()
57
59
58 id_, params = build_data(
60 id_, params = build_data(
59 self.apikey, 'get_pull_requests',
61 self.apikey, 'get_pull_requests',
60 repoid=pull_request.target_repo.repo_name,
62 repoid=pull_request.target_repo.repo_name,
61 status='new')
63 status='new')
62 response = api_call(self.app, params)
64 response = api_call(self.app, params)
63 assert response.status == '200 OK'
65 assert response.status == '200 OK'
64 assert len(response.json['result']) == 1
66 assert len(response.json['result']) == 1
65
67
66 id_, params = build_data(
68 id_, params = build_data(
67 self.apikey, 'get_pull_requests',
69 self.apikey, 'get_pull_requests',
68 repoid=pull_request.target_repo.repo_name,
70 repoid=pull_request.target_repo.repo_name,
69 status='closed')
71 status='closed')
70 response = api_call(self.app, params)
72 response = api_call(self.app, params)
71 assert response.status == '200 OK'
73 assert response.status == '200 OK'
72 assert len(response.json['result']) == 1
74 assert len(response.json['result']) == 1
73
75
74 @pytest.mark.backends("git", "hg")
76 @pytest.mark.backends("git", "hg")
75 def test_api_get_pull_requests_repo_error(self):
77 def test_api_get_pull_requests_repo_error(self):
76 id_, params = build_data(self.apikey, 'get_pull_requests', repoid=666)
78 id_, params = build_data(self.apikey, 'get_pull_requests', repoid=666)
77 response = api_call(self.app, params)
79 response = api_call(self.app, params)
78
80
79 expected = 'repository `666` does not exist'
81 expected = 'repository `666` does not exist'
80 assert_error(id_, expected, given=response.body)
82 assert_error(id_, expected, given=response.body)
@@ -1,212 +1,215
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import pytest
21 import pytest
22
22
23 from rhodecode.lib.vcs.nodes import FileNode
23 from rhodecode.lib.vcs.nodes import FileNode
24 from rhodecode.model.db import User
24 from rhodecode.model.db import User
25 from rhodecode.model.pull_request import PullRequestModel
25 from rhodecode.model.pull_request import PullRequestModel
26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
26 from rhodecode.tests import TEST_USER_ADMIN_LOGIN
27 from rhodecode.api.tests.utils import (
27 from rhodecode.api.tests.utils import (
28 build_data, api_call, assert_ok, assert_error)
28 build_data, api_call, assert_ok, assert_error)
29
29
30
30
31 @pytest.mark.usefixtures("testuser_api", "app")
31 @pytest.mark.usefixtures("testuser_api", "app")
32 class TestUpdatePullRequest(object):
32 class TestUpdatePullRequest(object):
33
33
34 @pytest.mark.backends("git", "hg")
34 @pytest.mark.backends("git", "hg")
35 def test_api_update_pull_request_title_or_description(
35 def test_api_update_pull_request_title_or_description(
36 self, pr_util, no_notifications):
36 self, pr_util, no_notifications):
37 pull_request = pr_util.create_pull_request()
37 pull_request = pr_util.create_pull_request()
38
38
39 id_, params = build_data(
39 id_, params = build_data(
40 self.apikey, 'update_pull_request',
40 self.apikey, 'update_pull_request',
41 repoid=pull_request.target_repo.repo_name,
41 repoid=pull_request.target_repo.repo_name,
42 pullrequestid=pull_request.pull_request_id,
42 pullrequestid=pull_request.pull_request_id,
43 title='New TITLE OF A PR',
43 title='New TITLE OF A PR',
44 description='New DESC OF A PR',
44 description='New DESC OF A PR',
45 )
45 )
46 response = api_call(self.app, params)
46 response = api_call(self.app, params)
47
47
48 expected = {
48 expected = {
49 "msg": "Updated pull request `{}`".format(
49 "msg": "Updated pull request `{}`".format(
50 pull_request.pull_request_id),
50 pull_request.pull_request_id),
51 "pull_request": response.json['result']['pull_request'],
51 "pull_request": response.json['result']['pull_request'],
52 "updated_commits": {"added": [], "common": [], "removed": []},
52 "updated_commits": {"added": [], "common": [], "removed": []},
53 "updated_reviewers": {"added": [], "removed": []},
53 "updated_reviewers": {"added": [], "removed": []},
54 "updated_observers": {"added": [], "removed": []},
54 }
55 }
55
56
56 response_json = response.json['result']
57 response_json = response.json['result']
57 assert response_json == expected
58 assert response_json == expected
58 pr = response_json['pull_request']
59 pr = response_json['pull_request']
59 assert pr['title'] == 'New TITLE OF A PR'
60 assert pr['title'] == 'New TITLE OF A PR'
60 assert pr['description'] == 'New DESC OF A PR'
61 assert pr['description'] == 'New DESC OF A PR'
61
62
62 @pytest.mark.backends("git", "hg")
63 @pytest.mark.backends("git", "hg")
63 def test_api_try_update_closed_pull_request(
64 def test_api_try_update_closed_pull_request(
64 self, pr_util, no_notifications):
65 self, pr_util, no_notifications):
65 pull_request = pr_util.create_pull_request()
66 pull_request = pr_util.create_pull_request()
66 PullRequestModel().close_pull_request(
67 PullRequestModel().close_pull_request(
67 pull_request, TEST_USER_ADMIN_LOGIN)
68 pull_request, TEST_USER_ADMIN_LOGIN)
68
69
69 id_, params = build_data(
70 id_, params = build_data(
70 self.apikey, 'update_pull_request',
71 self.apikey, 'update_pull_request',
71 repoid=pull_request.target_repo.repo_name,
72 repoid=pull_request.target_repo.repo_name,
72 pullrequestid=pull_request.pull_request_id)
73 pullrequestid=pull_request.pull_request_id)
73 response = api_call(self.app, params)
74 response = api_call(self.app, params)
74
75
75 expected = 'pull request `{}` update failed, pull request ' \
76 expected = 'pull request `{}` update failed, pull request ' \
76 'is closed'.format(pull_request.pull_request_id)
77 'is closed'.format(pull_request.pull_request_id)
77
78
78 assert_error(id_, expected, response.body)
79 assert_error(id_, expected, response.body)
79
80
80 @pytest.mark.backends("git", "hg")
81 @pytest.mark.backends("git", "hg")
81 def test_api_update_update_commits(self, pr_util, no_notifications):
82 def test_api_update_update_commits(self, pr_util, no_notifications):
82 commits = [
83 commits = [
83 {'message': 'a'},
84 {'message': 'a'},
84 {'message': 'b', 'added': [FileNode('file_b', 'test_content\n')]},
85 {'message': 'b', 'added': [FileNode('file_b', 'test_content\n')]},
85 {'message': 'c', 'added': [FileNode('file_c', 'test_content\n')]},
86 {'message': 'c', 'added': [FileNode('file_c', 'test_content\n')]},
86 ]
87 ]
87 pull_request = pr_util.create_pull_request(
88 pull_request = pr_util.create_pull_request(
88 commits=commits, target_head='a', source_head='b', revisions=['b'])
89 commits=commits, target_head='a', source_head='b', revisions=['b'])
89 pr_util.update_source_repository(head='c')
90 pr_util.update_source_repository(head='c')
90 repo = pull_request.source_repo.scm_instance()
91 repo = pull_request.source_repo.scm_instance()
91 commits = [x for x in repo.get_commits()]
92 commits = [x for x in repo.get_commits()]
92
93
93 added_commit_id = commits[-1].raw_id # c commit
94 added_commit_id = commits[-1].raw_id # c commit
94 common_commit_id = commits[1].raw_id # b commit is common ancestor
95 common_commit_id = commits[1].raw_id # b commit is common ancestor
95 total_commits = [added_commit_id, common_commit_id]
96 total_commits = [added_commit_id, common_commit_id]
96
97
97 id_, params = build_data(
98 id_, params = build_data(
98 self.apikey, 'update_pull_request',
99 self.apikey, 'update_pull_request',
99 repoid=pull_request.target_repo.repo_name,
100 repoid=pull_request.target_repo.repo_name,
100 pullrequestid=pull_request.pull_request_id,
101 pullrequestid=pull_request.pull_request_id,
101 update_commits=True
102 update_commits=True
102 )
103 )
103 response = api_call(self.app, params)
104 response = api_call(self.app, params)
104
105
105 expected = {
106 expected = {
106 "msg": "Updated pull request `{}`".format(
107 "msg": "Updated pull request `{}`".format(
107 pull_request.pull_request_id),
108 pull_request.pull_request_id),
108 "pull_request": response.json['result']['pull_request'],
109 "pull_request": response.json['result']['pull_request'],
109 "updated_commits": {"added": [added_commit_id],
110 "updated_commits": {"added": [added_commit_id],
110 "common": [common_commit_id],
111 "common": [common_commit_id],
111 "total": total_commits,
112 "total": total_commits,
112 "removed": []},
113 "removed": []},
113 "updated_reviewers": {"added": [], "removed": []},
114 "updated_reviewers": {"added": [], "removed": []},
115