##// END OF EJS Templates
tests: skip reading Git system and global configuration in test_vcs_operations...
Manuel Jacob -
r8769:511b20a6 stable
parent child Browse files
Show More
@@ -1,644 +1,646 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 Test suite for vcs push/pull operations.
16 16
17 17 The tests need Git > 1.8.1.
18 18
19 19 This file was forked by the Kallithea project in July 2014.
20 20 Original author and date, and relevant copyright and licensing information is below:
21 21 :created_on: Dec 30, 2010
22 22 :author: marcink
23 23 :copyright: (c) 2013 RhodeCode GmbH, and others.
24 24 :license: GPLv3, see LICENSE.md for more details.
25 25
26 26 """
27 27
28 28 import json
29 29 import os
30 30 import re
31 31 import tempfile
32 32 import time
33 33 import urllib.request
34 34 from subprocess import PIPE, Popen
35 35 from tempfile import _RandomNameSequence
36 36
37 37 import pytest
38 38
39 39 import kallithea
40 40 from kallithea.lib.utils2 import ascii_bytes, safe_str
41 41 from kallithea.model import db, meta
42 42 from kallithea.model.ssh_key import SshKeyModel
43 43 from kallithea.model.user import UserModel
44 44 from kallithea.tests import base
45 45 from kallithea.tests.fixture import Fixture
46 46
47 47
48 48 DEBUG = True
49 49 HOST = '127.0.0.1:4999' # test host
50 50
51 51 fixture = Fixture()
52 52
53 53
54 54 # Parameterize different kinds of VCS testing - both the kind of VCS and the
55 55 # access method (HTTP/SSH)
56 56
57 57 # Mixin for using HTTP and SSH URLs
58 58 class HttpVcsTest(object):
59 59 @staticmethod
60 60 def repo_url_param(webserver, repo_name, **kwargs):
61 61 return webserver.repo_url(repo_name, **kwargs)
62 62
63 63 class SshVcsTest(object):
64 64 public_keys = {
65 65 base.TEST_USER_REGULAR_LOGIN: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUQ== kallithea@localhost',
66 66 base.TEST_USER_ADMIN_LOGIN: 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAAAgQC6Ycnc2oUZHQnQwuqgZqTTdMDZD7ataf3JM7oG2Fw8JR6cdmz4QZLe5mfDwaFwG2pWHLRpVqzfrD/Pn3rIO++bgCJH5ydczrl1WScfryV1hYMJ/4EzLGM657J1/q5EI+b9SntKjf4ax+KP322L0TNQGbZUHLbfG2MwHMrYBQpHUq== kallithea@localhost',
67 67 }
68 68
69 69 @classmethod
70 70 def repo_url_param(cls, webserver, repo_name, username=base.TEST_USER_ADMIN_LOGIN, password=base.TEST_USER_ADMIN_PASS, client_ip=base.IP_ADDR):
71 71 user = db.User.get_by_username(username)
72 72 if user.ssh_keys:
73 73 ssh_key = user.ssh_keys[0]
74 74 else:
75 75 sshkeymodel = SshKeyModel()
76 76 ssh_key = sshkeymodel.create(user, 'test key', cls.public_keys[user.username])
77 77 meta.Session().commit()
78 78
79 79 return cls._ssh_param(repo_name, user, ssh_key, client_ip)
80 80
81 81 # Mixins for using Mercurial and Git
82 82 class HgVcsTest(object):
83 83 repo_type = 'hg'
84 84 repo_name = base.HG_REPO
85 85
86 86 class GitVcsTest(object):
87 87 repo_type = 'git'
88 88 repo_name = base.GIT_REPO
89 89
90 90 # Combine mixins to give the combinations we want to parameterize tests with
91 91 class HgHttpVcsTest(HgVcsTest, HttpVcsTest):
92 92 pass
93 93
94 94 class GitHttpVcsTest(GitVcsTest, HttpVcsTest):
95 95 pass
96 96
97 97 class HgSshVcsTest(HgVcsTest, SshVcsTest):
98 98 @staticmethod
99 99 def _ssh_param(repo_name, user, ssh_key, client_ip):
100 100 # Specify a custom ssh command on the command line
101 101 return r"""--config ui.ssh="bash -c 'SSH_ORIGINAL_COMMAND=\"\$2\" SSH_CONNECTION=\"%s 1024 127.0.0.1 22\" kallithea-cli ssh-serve -c %s %s %s' --" ssh://someuser@somehost/%s""" % (
102 102 client_ip,
103 103 kallithea.CONFIG['__file__'],
104 104 user.user_id,
105 105 ssh_key.user_ssh_key_id,
106 106 repo_name)
107 107
108 108 class GitSshVcsTest(GitVcsTest, SshVcsTest):
109 109 @staticmethod
110 110 def _ssh_param(repo_name, user, ssh_key, client_ip):
111 111 # Set a custom ssh command in the global environment
112 112 os.environ['GIT_SSH_COMMAND'] = r"""bash -c 'SSH_ORIGINAL_COMMAND="$2" SSH_CONNECTION="%s 1024 127.0.0.1 22" kallithea-cli ssh-serve -c %s %s %s' --""" % (
113 113 client_ip,
114 114 kallithea.CONFIG['__file__'],
115 115 user.user_id,
116 116 ssh_key.user_ssh_key_id)
117 117 return "ssh://someuser@somehost/%s""" % repo_name
118 118
119 119 parametrize_vcs_test = base.parametrize('vt', [
120 120 HgHttpVcsTest,
121 121 GitHttpVcsTest,
122 122 HgSshVcsTest,
123 123 GitSshVcsTest,
124 124 ])
125 125 parametrize_vcs_test_hg = base.parametrize('vt', [
126 126 HgHttpVcsTest,
127 127 HgSshVcsTest,
128 128 ])
129 129 parametrize_vcs_test_http = base.parametrize('vt', [
130 130 HgHttpVcsTest,
131 131 GitHttpVcsTest,
132 132 ])
133 133
134 134 class Command(object):
135 135
136 136 def __init__(self, cwd):
137 137 self.cwd = cwd
138 138
139 139 def execute(self, *args, **environ):
140 140 """
141 141 Runs command on the system with given ``args`` using simple space
142 142 join without safe quoting.
143 143 """
144 144 command = ' '.join(args)
145 145 ignoreReturnCode = environ.pop('ignoreReturnCode', False)
146 146 if DEBUG:
147 147 print('*** CMD %s ***' % command)
148 148 testenv = dict(os.environ)
149 149 testenv['LANG'] = 'en_US.UTF-8'
150 150 testenv['LANGUAGE'] = 'en_US:en'
151 151 testenv['HGPLAIN'] = ''
152 152 testenv['HGRCPATH'] = ''
153 testenv['GIT_CONFIG_SYSTEM'] = '/dev/null'
154 testenv['GIT_CONFIG_GLOBAL'] = '/dev/null'
153 155 testenv['GIT_COMMITTER_NAME'] = base.TEST_USER_ADMIN_LOGIN
154 156 testenv['GIT_COMMITTER_EMAIL'] = base.TEST_USER_ADMIN_EMAIL
155 157 testenv['GIT_AUTHOR_NAME'] = base.TEST_USER_REGULAR_LOGIN
156 158 testenv['GIT_AUTHOR_EMAIL'] = base.TEST_USER_REGULAR_EMAIL
157 159 testenv.update(environ)
158 160 p = Popen(command, shell=True, stdout=PIPE, stderr=PIPE, cwd=self.cwd, env=testenv)
159 161 stdout, stderr = p.communicate()
160 162 if DEBUG:
161 163 if stdout:
162 164 print('stdout:', stdout)
163 165 if stderr:
164 166 print('stderr:', stderr)
165 167 if not ignoreReturnCode:
166 168 assert p.returncode == 0
167 169 return safe_str(stdout), safe_str(stderr)
168 170
169 171
170 172 def _get_tmp_dir(prefix='vcs_operations-', suffix=''):
171 173 return tempfile.mkdtemp(dir=base.TESTS_TMP_PATH, prefix=prefix, suffix=suffix)
172 174
173 175
174 176 def _add_files(vcs, dest_dir, files_no=3):
175 177 """
176 178 Generate some files, add it to dest_dir repo and push back
177 179 vcs is git or hg and defines what VCS we want to make those files for
178 180
179 181 :param vcs:
180 182 :param dest_dir:
181 183 """
182 184 added_file = '%ssetup.py' % next(_RandomNameSequence())
183 185 open(os.path.join(dest_dir, added_file), 'a').close()
184 186 Command(dest_dir).execute(vcs, 'add', added_file)
185 187
186 188 email = 'me@example.com'
187 189 if os.name == 'nt':
188 190 author_str = 'User <%s>' % email
189 191 else:
190 192 author_str = 'User ǝɯɐᴎ <%s>' % email
191 193 for i in range(files_no):
192 194 cmd = """echo "added_line%s" >> %s""" % (i, added_file)
193 195 Command(dest_dir).execute(cmd)
194 196 if vcs == 'hg':
195 197 cmd = """hg commit -m "committed new %s" -u "%s" "%s" """ % (
196 198 i, author_str, added_file
197 199 )
198 200 elif vcs == 'git':
199 201 cmd = """git commit -m "committed new %s" --author "%s" "%s" """ % (
200 202 i, author_str, added_file
201 203 )
202 204 Command(dest_dir).execute(cmd)
203 205
204 206 def _add_files_and_push(webserver, vt, dest_dir, clone_url, ignoreReturnCode=False, files_no=3):
205 207 _add_files(vt.repo_type, dest_dir, files_no=files_no)
206 208 # PUSH it back
207 209 stdout = stderr = None
208 210 if vt.repo_type == 'hg':
209 211 stdout, stderr = Command(dest_dir).execute('hg push -f --verbose', clone_url, ignoreReturnCode=ignoreReturnCode)
210 212 elif vt.repo_type == 'git':
211 213 stdout, stderr = Command(dest_dir).execute('git push -f --verbose', clone_url, "master", ignoreReturnCode=ignoreReturnCode)
212 214
213 215 return stdout, stderr
214 216
215 217
216 218 def _check_outgoing(vcs, cwd, clone_url):
217 219 if vcs == 'hg':
218 220 # hg removes the password from default URLs, so we have to provide it here via the clone_url
219 221 return Command(cwd).execute('hg -q outgoing', clone_url, ignoreReturnCode=True)
220 222 elif vcs == 'git':
221 223 Command(cwd).execute('git remote update')
222 224 return Command(cwd).execute('git log origin/master..master')
223 225
224 226
225 227 def set_anonymous_access(enable=True):
226 228 user = db.User.get_default_user()
227 229 user.active = enable
228 230 meta.Session().commit()
229 231 if enable != db.User.get_default_user().active:
230 232 raise Exception('Cannot set anonymous access')
231 233
232 234
233 235 #==============================================================================
234 236 # TESTS
235 237 #==============================================================================
236 238
237 239
238 240 def _check_proper_git_push(stdout, stderr):
239 241 assert 'fatal' not in stderr
240 242 assert 'rejected' not in stderr
241 243 assert 'Pushing to' in stderr
242 244 assert 'master -> master' in stderr
243 245
244 246
245 247 @pytest.mark.usefixtures("test_context_fixture")
246 248 class TestVCSOperations(base.TestController):
247 249
248 250 @classmethod
249 251 def setup_class(cls):
250 252 # DISABLE ANONYMOUS ACCESS
251 253 set_anonymous_access(False)
252 254
253 255 @pytest.fixture()
254 256 def testhook_cleanup(self):
255 257 yield
256 258 # remove hook
257 259 for hook in ['prechangegroup', 'pretxnchangegroup', 'preoutgoing', 'changegroup', 'outgoing', 'incoming']:
258 260 entry = db.Ui.get_by_key('hooks', '%s.testhook' % hook)
259 261 if entry:
260 262 meta.Session().delete(entry)
261 263 meta.Session().commit()
262 264
263 265 @pytest.fixture(scope="module")
264 266 def testfork(self):
265 267 # create fork so the repo stays untouched
266 268 git_fork_name = '%s_fork%s' % (base.GIT_REPO, next(_RandomNameSequence()))
267 269 fixture.create_fork(base.GIT_REPO, git_fork_name)
268 270 hg_fork_name = '%s_fork%s' % (base.HG_REPO, next(_RandomNameSequence()))
269 271 fixture.create_fork(base.HG_REPO, hg_fork_name)
270 272 return {'git': git_fork_name, 'hg': hg_fork_name}
271 273
272 274 @parametrize_vcs_test
273 275 def test_clone_repo_by_admin(self, webserver, vt):
274 276 clone_url = vt.repo_url_param(webserver, vt.repo_name)
275 277 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, _get_tmp_dir())
276 278
277 279 if vt.repo_type == 'git':
278 280 assert 'Cloning into' in stdout + stderr
279 281 assert stderr == '' or stdout == ''
280 282 elif vt.repo_type == 'hg':
281 283 assert 'requesting all changes' in stdout
282 284 assert 'adding changesets' in stdout
283 285 assert 'adding manifests' in stdout
284 286 assert 'adding file changes' in stdout
285 287 assert stderr == ''
286 288
287 289 @parametrize_vcs_test_http
288 290 def test_clone_wrong_credentials(self, webserver, vt):
289 291 clone_url = vt.repo_url_param(webserver, vt.repo_name, password='bad!')
290 292 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True)
291 293 if vt.repo_type == 'git':
292 294 assert 'fatal: Authentication failed' in stderr
293 295 elif vt.repo_type == 'hg':
294 296 assert 'abort: authorization failed' in stderr
295 297
296 298 def test_clone_git_dir_as_hg(self, webserver):
297 299 clone_url = HgHttpVcsTest.repo_url_param(webserver, base.GIT_REPO)
298 300 stdout, stderr = Command(base.TESTS_TMP_PATH).execute('hg clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True)
299 301 assert 'HTTP Error 404: Not Found' in stderr or "not a valid repository" in stdout and 'abort:' in stderr
300 302
301 303 def test_clone_hg_repo_as_git(self, webserver):
302 304 clone_url = GitHttpVcsTest.repo_url_param(webserver, base.HG_REPO)
303 305 stdout, stderr = Command(base.TESTS_TMP_PATH).execute('git clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True)
304 306 assert 'not found' in stderr
305 307
306 308 @parametrize_vcs_test
307 309 def test_clone_non_existing_path(self, webserver, vt):
308 310 clone_url = vt.repo_url_param(webserver, 'trololo')
309 311 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True)
310 312 if vt.repo_type == 'git':
311 313 assert 'not found' in stderr or 'abort: Access to %r denied' % 'trololo' in stderr
312 314 elif vt.repo_type == 'hg':
313 315 assert 'HTTP Error 404: Not Found' in stderr or 'abort: no suitable response from remote hg' in stderr and 'remote: abort: Access to %r denied' % 'trololo' in stdout + stderr
314 316
315 317 @parametrize_vcs_test
316 318 def test_push_new_repo(self, webserver, vt):
317 319 # Clear the log so we know what is added
318 320 db.UserLog.query().delete()
319 321 meta.Session().commit()
320 322
321 323 # Create an empty server repo using the API
322 324 repo_name = 'new_%s_%s' % (vt.repo_type, next(_RandomNameSequence()))
323 325 usr = db.User.get_by_username(base.TEST_USER_ADMIN_LOGIN)
324 326 params = {
325 327 "id": 7,
326 328 "api_key": usr.api_key,
327 329 "method": 'create_repo',
328 330 "args": dict(repo_name=repo_name,
329 331 owner=base.TEST_USER_ADMIN_LOGIN,
330 332 repo_type=vt.repo_type),
331 333 }
332 334 req = urllib.request.Request(
333 335 'http://%s:%s/_admin/api' % webserver.server_address,
334 336 data=ascii_bytes(json.dumps(params)),
335 337 headers={'content-type': 'application/json'})
336 338 response = urllib.request.urlopen(req)
337 339 result = json.loads(response.read())
338 340 # Expect something like:
339 341 # {u'result': {u'msg': u'Created new repository `new_XXX`', u'task': None, u'success': True}, u'id': 7, u'error': None}
340 342 assert result['result']['success']
341 343
342 344 # Create local clone of the empty server repo
343 345 local_clone_dir = _get_tmp_dir()
344 346 clone_url = vt.repo_url_param(webserver, repo_name)
345 347 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, local_clone_dir)
346 348
347 349 # Make 3 commits and push to the empty server repo.
348 350 # The server repo doesn't have any other heads than the
349 351 # refs/heads/master we are pushing, but the `git log` in the push hook
350 352 # should still list the 3 commits.
351 353 stdout, stderr = _add_files_and_push(webserver, vt, local_clone_dir, clone_url=clone_url)
352 354 if vt.repo_type == 'git':
353 355 _check_proper_git_push(stdout, stderr)
354 356 elif vt.repo_type == 'hg':
355 357 assert 'pushing to ' in stdout
356 358 assert 'remote: added ' in stdout
357 359
358 360 # Verify that we got the right events in UserLog. Expect something like:
359 361 # <UserLog('id:new_git_XXX:started_following_repo')>
360 362 # <UserLog('id:new_git_XXX:user_created_repo')>
361 363 # <UserLog('id:new_git_XXX:pull')>
362 364 # <UserLog('id:new_git_XXX:push:aed9d4c1732a1927da3be42c47eb9afdc200d427,d38b083a07af10a9f44193486959a96a23db78da,4841ff9a2b385bec995f4679ef649adb3f437622')>
363 365 meta.Session.close() # make sure SA fetches all new log entries (apparently only needed for MariaDB/MySQL ...)
364 366 action_parts = [ul.action.split(':', 1) for ul in db.UserLog.query().order_by(db.UserLog.user_log_id)]
365 367 assert [(t[0], (t[1].count(',') + 1) if len(t) == 2 else 0) for t in action_parts] == ([
366 368 ('started_following_repo', 0),
367 369 ('user_created_repo', 0),
368 370 ('pull', 0),
369 371 ('push', 3)]
370 372 if vt.repo_type == 'git' else [
371 373 ('started_following_repo', 0),
372 374 ('user_created_repo', 0),
373 375 # (u'pull', 0), # Mercurial outgoing hook is not called for empty clones
374 376 ('push', 3)])
375 377
376 378 @parametrize_vcs_test
377 379 def test_push_new_file(self, webserver, testfork, vt):
378 380 db.UserLog.query().delete()
379 381 meta.Session().commit()
380 382
381 383 dest_dir = _get_tmp_dir()
382 384 clone_url = vt.repo_url_param(webserver, vt.repo_name)
383 385 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, dest_dir)
384 386
385 387 clone_url = vt.repo_url_param(webserver, testfork[vt.repo_type])
386 388 stdout, stderr = _add_files_and_push(webserver, vt, dest_dir, clone_url=clone_url)
387 389
388 390 if vt.repo_type == 'git':
389 391 _check_proper_git_push(stdout, stderr)
390 392 elif vt.repo_type == 'hg':
391 393 assert 'pushing to' in stdout
392 394 assert 'Repository size' in stdout
393 395 assert 'Last revision is now' in stdout
394 396
395 397 meta.Session.close() # make sure SA fetches all new log entries (apparently only needed for MariaDB/MySQL ...)
396 398 action_parts = [ul.action.split(':', 1) for ul in db.UserLog.query().order_by(db.UserLog.user_log_id)]
397 399 assert [(t[0], (t[1].count(',') + 1) if len(t) == 2 else 0) for t in action_parts] == \
398 400 [('pull', 0), ('push', 3)]
399 401
400 402 @parametrize_vcs_test
401 403 def test_pull(self, webserver, testfork, vt):
402 404 db.UserLog.query().delete()
403 405 meta.Session().commit()
404 406
405 407 dest_dir = _get_tmp_dir()
406 408 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'init', dest_dir)
407 409
408 410 clone_url = vt.repo_url_param(webserver, vt.repo_name)
409 411 stdout, stderr = Command(dest_dir).execute(vt.repo_type, 'pull', clone_url)
410 412 meta.Session.close() # make sure SA fetches all new log entries (apparently only needed for MariaDB/MySQL ...)
411 413
412 414 if vt.repo_type == 'git':
413 415 assert 'FETCH_HEAD' in stderr
414 416 elif vt.repo_type == 'hg':
415 417 assert 'new changesets' in stdout
416 418
417 419 action_parts = [ul.action for ul in db.UserLog.query().order_by(db.UserLog.user_log_id)]
418 420 assert action_parts == ['pull']
419 421
420 422 # Test handling of URLs with extra '/' around repo_name
421 423 stdout, stderr = Command(dest_dir).execute(vt.repo_type, 'pull', clone_url.replace('/' + vt.repo_name, '/./%s/' % vt.repo_name), ignoreReturnCode=True)
422 424 if issubclass(vt, HttpVcsTest):
423 425 if vt.repo_type == 'git':
424 426 # NOTE: when pulling from http://hostname/./vcs_test_git/ , the git client will normalize that and issue an HTTP request to /vcs_test_git/info/refs
425 427 assert 'Already up to date.' in stdout
426 428 else:
427 429 assert vt.repo_type == 'hg'
428 430 assert "abort: HTTP Error 404: Not Found" in stderr
429 431 else:
430 432 assert issubclass(vt, SshVcsTest)
431 433 if vt.repo_type == 'git':
432 434 assert "abort: Access to './%s' denied" % vt.repo_name in stderr
433 435 else:
434 436 assert "abort: Access to './%s' denied" % vt.repo_name in stdout + stderr
435 437
436 438 stdout, stderr = Command(dest_dir).execute(vt.repo_type, 'pull', clone_url.replace('/' + vt.repo_name, '/%s/' % vt.repo_name), ignoreReturnCode=True)
437 439 if vt.repo_type == 'git':
438 440 assert 'Already up to date.' in stdout
439 441 else:
440 442 assert vt.repo_type == 'hg'
441 443 assert "no changes found" in stdout
442 444 assert "denied" not in stderr
443 445 assert "denied" not in stdout
444 446 assert "404" not in stdout
445 447
446 448 @parametrize_vcs_test
447 449 def test_push_invalidates_cache(self, webserver, testfork, vt):
448 450 pre_cached_tip = [repo.get_api_data()['last_changeset']['short_id'] for repo in db.Repository.query().filter(db.Repository.repo_name == testfork[vt.repo_type])]
449 451
450 452 dest_dir = _get_tmp_dir()
451 453 clone_url = vt.repo_url_param(webserver, testfork[vt.repo_type])
452 454 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, dest_dir)
453 455
454 456 stdout, stderr = _add_files_and_push(webserver, vt, dest_dir, files_no=1, clone_url=clone_url)
455 457
456 458 if vt.repo_type == 'git':
457 459 _check_proper_git_push(stdout, stderr)
458 460
459 461 meta.Session.close() # expire session to make sure SA fetches new Repository instances after last_changeset has been updated by server side hook in another process
460 462 post_cached_tip = [repo.get_api_data()['last_changeset']['short_id'] for repo in db.Repository.query().filter(db.Repository.repo_name == testfork[vt.repo_type])]
461 463 assert pre_cached_tip != post_cached_tip
462 464
463 465 @parametrize_vcs_test_http
464 466 def test_push_wrong_credentials(self, webserver, vt):
465 467 dest_dir = _get_tmp_dir()
466 468 clone_url = vt.repo_url_param(webserver, vt.repo_name)
467 469 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, dest_dir)
468 470
469 471 clone_url = webserver.repo_url(vt.repo_name, username='bad', password='name')
470 472 stdout, stderr = _add_files_and_push(webserver, vt, dest_dir,
471 473 clone_url=clone_url, ignoreReturnCode=True)
472 474
473 475 if vt.repo_type == 'git':
474 476 assert 'fatal: Authentication failed' in stderr
475 477 elif vt.repo_type == 'hg':
476 478 assert 'abort: authorization failed' in stderr
477 479
478 480 @parametrize_vcs_test
479 481 def test_push_with_readonly_credentials(self, webserver, vt):
480 482 db.UserLog.query().delete()
481 483 meta.Session().commit()
482 484
483 485 dest_dir = _get_tmp_dir()
484 486 clone_url = vt.repo_url_param(webserver, vt.repo_name, username=base.TEST_USER_REGULAR_LOGIN, password=base.TEST_USER_REGULAR_PASS)
485 487 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, dest_dir)
486 488
487 489 stdout, stderr = _add_files_and_push(webserver, vt, dest_dir, ignoreReturnCode=True, clone_url=clone_url)
488 490
489 491 if vt.repo_type == 'git':
490 492 assert 'The requested URL returned error: 403' in stderr or 'abort: Push access to %r denied' % str(vt.repo_name) in stderr
491 493 elif vt.repo_type == 'hg':
492 494 assert 'abort: HTTP Error 403: Forbidden' in stderr or 'abort: push failed on remote' in stderr and 'remote: Push access to %r denied' % str(vt.repo_name) in stdout
493 495
494 496 meta.Session.close() # make sure SA fetches all new log entries (apparently only needed for MariaDB/MySQL ...)
495 497 action_parts = [ul.action.split(':', 1) for ul in db.UserLog.query().order_by(db.UserLog.user_log_id)]
496 498 assert [(t[0], (t[1].count(',') + 1) if len(t) == 2 else 0) for t in action_parts] == \
497 499 [('pull', 0)]
498 500
499 501 @parametrize_vcs_test
500 502 def test_push_back_to_wrong_url(self, webserver, vt):
501 503 dest_dir = _get_tmp_dir()
502 504 clone_url = vt.repo_url_param(webserver, vt.repo_name)
503 505 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, dest_dir)
504 506
505 507 stdout, stderr = _add_files_and_push(
506 508 webserver, vt, dest_dir, clone_url='http://%s:%s/tmp' % (
507 509 webserver.server_address[0], webserver.server_address[1]),
508 510 ignoreReturnCode=True)
509 511
510 512 if vt.repo_type == 'git':
511 513 assert 'not found' in stderr
512 514 elif vt.repo_type == 'hg':
513 515 assert 'HTTP Error 404: Not Found' in stderr
514 516
515 517 @parametrize_vcs_test
516 518 def test_ip_restriction(self, webserver, vt):
517 519 user_model = UserModel()
518 520 try:
519 521 # Add IP constraint that excludes the test context:
520 522 user_model.add_extra_ip(base.TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
521 523 meta.Session().commit()
522 524 # IP permissions are cached, need to wait for the cache in the server process to expire
523 525 time.sleep(1.5)
524 526 clone_url = vt.repo_url_param(webserver, vt.repo_name)
525 527 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, _get_tmp_dir(), ignoreReturnCode=True)
526 528 if vt.repo_type == 'git':
527 529 # The message apparently changed in Git 1.8.3, so match it loosely.
528 530 assert re.search(r'\b403\b', stderr) or 'abort: User test_admin from 127.0.0.127 cannot be authorized' in stderr
529 531 elif vt.repo_type == 'hg':
530 532 assert 'abort: HTTP Error 403: Forbidden' in stderr or 'remote: abort: User test_admin from 127.0.0.127 cannot be authorized' in stdout + stderr
531 533 finally:
532 534 # release IP restrictions
533 535 for ip in db.UserIpMap.query():
534 536 db.UserIpMap.delete(ip.ip_id)
535 537 meta.Session().commit()
536 538 # IP permissions are cached, need to wait for the cache in the server process to expire
537 539 time.sleep(1.5)
538 540
539 541 clone_url = vt.repo_url_param(webserver, vt.repo_name)
540 542 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, _get_tmp_dir())
541 543
542 544 if vt.repo_type == 'git':
543 545 assert 'Cloning into' in stdout + stderr
544 546 assert stderr == '' or stdout == ''
545 547 elif vt.repo_type == 'hg':
546 548 assert 'requesting all changes' in stdout
547 549 assert 'adding changesets' in stdout
548 550 assert 'adding manifests' in stdout
549 551 assert 'adding file changes' in stdout
550 552
551 553 assert stderr == ''
552 554
553 555 @parametrize_vcs_test_hg # git hooks doesn't work like hg hooks
554 556 def test_custom_hooks_preoutgoing(self, testhook_cleanup, webserver, testfork, vt):
555 557 # set prechangegroup to failing hook (returns True)
556 558 db.Ui.create_or_update_hook('preoutgoing.testhook', 'python:kallithea.tests.fixture.failing_test_hook')
557 559 meta.Session().commit()
558 560 # clone repo
559 561 clone_url = vt.repo_url_param(webserver, testfork[vt.repo_type], username=base.TEST_USER_ADMIN_LOGIN, password=base.TEST_USER_ADMIN_PASS)
560 562 dest_dir = _get_tmp_dir()
561 563 stdout, stderr = Command(base.TESTS_TMP_PATH) \
562 564 .execute(vt.repo_type, 'clone', clone_url, dest_dir, ignoreReturnCode=True)
563 565 if vt.repo_type == 'hg':
564 566 assert 'preoutgoing.testhook hook failed' in stdout + stderr
565 567 elif vt.repo_type == 'git':
566 568 assert 'error: 406' in stderr
567 569
568 570 @parametrize_vcs_test_hg # git hooks doesn't work like hg hooks
569 571 def test_custom_hooks_prechangegroup(self, testhook_cleanup, webserver, testfork, vt):
570 572 # set prechangegroup to failing hook (returns exit code 1)
571 573 db.Ui.create_or_update_hook('prechangegroup.testhook', 'python:kallithea.tests.fixture.failing_test_hook')
572 574 meta.Session().commit()
573 575 # clone repo
574 576 clone_url = vt.repo_url_param(webserver, testfork[vt.repo_type], username=base.TEST_USER_ADMIN_LOGIN, password=base.TEST_USER_ADMIN_PASS)
575 577 dest_dir = _get_tmp_dir()
576 578 stdout, stderr = Command(base.TESTS_TMP_PATH).execute(vt.repo_type, 'clone', clone_url, dest_dir)
577 579
578 580 stdout, stderr = _add_files_and_push(webserver, vt, dest_dir, clone_url,
579 581 ignoreReturnCode=True)
580 582 assert 'failing_test_hook failed' in stdout + stderr
581 583 assert 'Traceback' not in stdout + stderr
582 584 assert 'prechangegroup.testhook hook failed' in stdout + stderr
583 585 # there are still outgoing changesets
584 586 stdout, stderr = _check_outgoing(vt.repo_type, dest_dir, clone_url)
585 587 assert stdout != ''
586 588
587 589 # set prechangegroup hook to exception throwing method
588 590 db.Ui.create_or_update_hook('prechangegroup.testhook', 'python:kallithea.tests.fixture.exception_test_hook')
589 591 meta.Session().commit()
590 592 # re-try to push
591 593 stdout, stderr = Command(dest_dir).execute('%s push' % vt.repo_type, clone_url, ignoreReturnCode=True)
592 594 if vt is HgHttpVcsTest:
593 595 # like with 'hg serve...' 'HTTP Error 500: INTERNAL SERVER ERROR' should be returned
594 596 assert 'HTTP Error 500: INTERNAL SERVER ERROR' in stderr
595 597 elif vt is HgSshVcsTest:
596 598 assert 'remote: Exception: exception_test_hook threw an exception' in stdout
597 599 else: assert False
598 600 # there are still outgoing changesets
599 601 stdout, stderr = _check_outgoing(vt.repo_type, dest_dir, clone_url)
600 602 assert stdout != ''
601 603
602 604 # set prechangegroup hook to method that returns False
603 605 db.Ui.create_or_update_hook('prechangegroup.testhook', 'python:kallithea.tests.fixture.passing_test_hook')
604 606 meta.Session().commit()
605 607 # re-try to push
606 608 stdout, stderr = Command(dest_dir).execute('%s push' % vt.repo_type, clone_url, ignoreReturnCode=True)
607 609 assert 'passing_test_hook succeeded' in stdout + stderr
608 610 assert 'Traceback' not in stdout + stderr
609 611 assert 'prechangegroup.testhook hook failed' not in stdout + stderr
610 612 # no more outgoing changesets
611 613 stdout, stderr = _check_outgoing(vt.repo_type, dest_dir, clone_url)
612 614 assert stdout == ''
613 615 assert stderr == ''
614 616
615 617 def test_add_submodule_git(self, webserver, testfork):
616 618 dest_dir = _get_tmp_dir()
617 619 clone_url = GitHttpVcsTest.repo_url_param(webserver, base.GIT_REPO)
618 620
619 621 fork_url = GitHttpVcsTest.repo_url_param(webserver, testfork['git'])
620 622
621 623 # add submodule
622 624 stdout, stderr = Command(base.TESTS_TMP_PATH).execute('git clone', fork_url, dest_dir)
623 625 stdout, stderr = Command(dest_dir).execute('git submodule add', clone_url, 'testsubmodule')
624 626 stdout, stderr = Command(dest_dir).execute('git commit -am "added testsubmodule pointing to', clone_url, '"')
625 627 stdout, stderr = Command(dest_dir).execute('git push', fork_url, 'master')
626 628
627 629 # check for testsubmodule link in files page
628 630 self.log_user()
629 631 response = self.app.get(base.url(controller='files', action='index',
630 632 repo_name=testfork['git'],
631 633 revision='tip',
632 634 f_path='/'))
633 635 # check _repo_files_url that will be used to reload as AJAX
634 636 response.mustcontain('var _repo_files_url = ("/%s/files/");' % testfork['git'])
635 637
636 638 response.mustcontain('<a class="submodule-dir" href="%s" target="_blank"><i class="icon-file-submodule"></i><span>testsubmodule @ ' % clone_url)
637 639
638 640 # check that following a submodule link actually works - and redirects
639 641 response = self.app.get(base.url(controller='files', action='index',
640 642 repo_name=testfork['git'],
641 643 revision='tip',
642 644 f_path='/testsubmodule'),
643 645 status=302)
644 646 assert response.location == clone_url
General Comments 0
You need to be logged in to leave comments. Login now