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