##// END OF EJS Templates
tests: fixes for gunicorn tests workers
marcink -
r2455:82863912 default
parent child Browse files
Show More
@@ -1,271 +1,271 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 py.test config for test suite for making push/pull operations.
23 23
24 24 .. important::
25 25
26 26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 27 to redirect things to stderr instead of stdout.
28 28 """
29 29
30 30 import ConfigParser
31 31 import os
32 32 import subprocess32
33 33 import tempfile
34 34 import textwrap
35 35 import pytest
36 36
37 37 import rhodecode
38 38 from rhodecode.model.db import Repository
39 39 from rhodecode.model.meta import Session
40 40 from rhodecode.model.settings import SettingsModel
41 41 from rhodecode.tests import (
42 42 GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS,)
43 43 from rhodecode.tests.fixture import Fixture
44 44 from rhodecode.tests.utils import is_url_reachable, wait_for_url
45 45
46 46 RC_LOG = os.path.join(tempfile.gettempdir(), 'rc.log')
47 47 REPO_GROUP = 'a_repo_group'
48 48 HG_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, HG_REPO)
49 49 GIT_REPO_WITH_GROUP = '%s/%s' % (REPO_GROUP, GIT_REPO)
50 50
51 51
52 52 def assert_no_running_instance(url):
53 53 if is_url_reachable(url):
54 54 print("Hint: Usually this means another instance of Enterprise "
55 55 "is running in the background.")
56 56 pytest.fail(
57 57 "Port is not free at %s, cannot start web interface" % url)
58 58
59 59
60 60 def get_port(pyramid_config):
61 61 config = ConfigParser.ConfigParser()
62 62 config.read(pyramid_config)
63 63 return config.get('server:main', 'port')
64 64
65 65
66 66 def get_host_url(pyramid_config):
67 67 """Construct the host url using the port in the test configuration."""
68 68 return '127.0.0.1:%s' % get_port(pyramid_config)
69 69
70 70
71 71 class RcWebServer(object):
72 72 """
73 73 Represents a running RCE web server used as a test fixture.
74 74 """
75 75 def __init__(self, pyramid_config, log_file):
76 76 self.pyramid_config = pyramid_config
77 77 self.log_file = log_file
78 78
79 79 def repo_clone_url(self, repo_name, **kwargs):
80 80 params = {
81 81 'user': TEST_USER_ADMIN_LOGIN,
82 82 'passwd': TEST_USER_ADMIN_PASS,
83 83 'host': get_host_url(self.pyramid_config),
84 84 'cloned_repo': repo_name,
85 85 }
86 86 params.update(**kwargs)
87 87 _url = 'http://%(user)s:%(passwd)s@%(host)s/%(cloned_repo)s' % params
88 88 return _url
89 89
90 90 def host_url(self):
91 91 return 'http://' + get_host_url(self.pyramid_config)
92 92
93 93 def get_rc_log(self):
94 94 with open(self.log_file) as f:
95 95 return f.read()
96 96
97 97
98 98 @pytest.fixture(scope="module")
99 99 def rcextensions(request, baseapp, tmpdir_factory):
100 100 """
101 101 Installs a testing rcextensions pack to ensure they work as expected.
102 102 """
103 103 init_content = textwrap.dedent("""
104 104 # Forward import the example rcextensions to make it
105 105 # active for our tests.
106 106 from rhodecode.tests.other.example_rcextensions import *
107 107 """)
108 108
109 109 # Note: rcextensions are looked up based on the path of the ini file
110 110 root_path = tmpdir_factory.getbasetemp()
111 111 rcextensions_path = root_path.join('rcextensions')
112 112 init_path = rcextensions_path.join('__init__.py')
113 113
114 114 if rcextensions_path.check():
115 115 pytest.fail(
116 116 "Path for rcextensions already exists, please clean up before "
117 117 "test run this path: %s" % (rcextensions_path, ))
118 118 return
119 119
120 120 request.addfinalizer(rcextensions_path.remove)
121 121 init_path.write_binary(init_content, ensure=True)
122 122
123 123
124 124 @pytest.fixture(scope="module")
125 125 def repos(request, baseapp):
126 126 """Create a copy of each test repo in a repo group."""
127 127 fixture = Fixture()
128 128 repo_group = fixture.create_repo_group(REPO_GROUP)
129 129 repo_group_id = repo_group.group_id
130 130 fixture.create_fork(HG_REPO, HG_REPO,
131 131 repo_name_full=HG_REPO_WITH_GROUP,
132 132 repo_group=repo_group_id)
133 133 fixture.create_fork(GIT_REPO, GIT_REPO,
134 134 repo_name_full=GIT_REPO_WITH_GROUP,
135 135 repo_group=repo_group_id)
136 136
137 137 @request.addfinalizer
138 138 def cleanup():
139 139 fixture.destroy_repo(HG_REPO_WITH_GROUP)
140 140 fixture.destroy_repo(GIT_REPO_WITH_GROUP)
141 141 fixture.destroy_repo_group(repo_group_id)
142 142
143 143
144 144 @pytest.fixture(scope="module")
145 145 def rc_web_server_config(testini_factory):
146 146 """
147 147 Configuration file used for the fixture `rc_web_server`.
148 148 """
149 149 CUSTOM_PARAMS = [
150 150 {'handler_console': {'level': 'DEBUG'}},
151 151 ]
152 152 return testini_factory(CUSTOM_PARAMS)
153 153
154 154
155 155 @pytest.fixture(scope="module")
156 156 def rc_web_server(
157 157 request, baseapp, rc_web_server_config, repos, rcextensions):
158 158 """
159 159 Run the web server as a subprocess.
160 160
161 161 Since we have already a running vcsserver, this is not spawned again.
162 162 """
163 163 env = os.environ.copy()
164 164 env['RC_NO_TMP_PATH'] = '1'
165 165
166 166 rc_log = list(RC_LOG.partition('.log'))
167 167 rc_log.insert(1, get_port(rc_web_server_config))
168 168 rc_log = ''.join(rc_log)
169 169
170 170 server_out = open(rc_log, 'w')
171 171
172 172 host_url = 'http://' + get_host_url(rc_web_server_config)
173 173 assert_no_running_instance(host_url)
174 174 command = ['gunicorn', '--worker-class', 'gevent', '--paste', rc_web_server_config]
175 175
176 print('Starting rhodecode server: {}'.format(host_url))
177 print('Command: {}'.format(command))
178 print('Logfile: {}'.format(rc_log))
176 print('rhodecode-web starting at: {}'.format(host_url))
177 print('rhodecode-web command: {}'.format(command))
178 print('rhodecode-web logfile: {}'.format(rc_log))
179 179
180 180 proc = subprocess32.Popen(
181 181 command, bufsize=0, env=env, stdout=server_out, stderr=server_out)
182 182
183 183 wait_for_url(host_url, timeout=30)
184 184
185 185 @request.addfinalizer
186 186 def stop_web_server():
187 187 # TODO: Find out how to integrate with the reporting of py.test to
188 188 # make this information available.
189 189 print("\nServer log file written to %s" % (rc_log, ))
190 190 proc.kill()
191 191 server_out.flush()
192 192 server_out.close()
193 193
194 194 return RcWebServer(rc_web_server_config, log_file=rc_log)
195 195
196 196
197 197 @pytest.fixture
198 198 def disable_locking(baseapp):
199 199 r = Repository.get_by_repo_name(GIT_REPO)
200 200 Repository.unlock(r)
201 201 r.enable_locking = False
202 202 Session().add(r)
203 203 Session().commit()
204 204
205 205 r = Repository.get_by_repo_name(HG_REPO)
206 206 Repository.unlock(r)
207 207 r.enable_locking = False
208 208 Session().add(r)
209 209 Session().commit()
210 210
211 211
212 212 @pytest.fixture
213 213 def enable_auth_plugins(request, baseapp, csrf_token):
214 214 """
215 215 Return a factory object that when called, allows to control which
216 216 authentication plugins are enabled.
217 217 """
218 218 def _enable_plugins(plugins_list, override=None):
219 219 override = override or {}
220 220 params = {
221 221 'auth_plugins': ','.join(plugins_list),
222 222 }
223 223
224 224 # helper translate some names to others
225 225 name_map = {
226 226 'token': 'authtoken'
227 227 }
228 228
229 229 for module in plugins_list:
230 230 plugin_name = module.partition('#')[-1]
231 231 if plugin_name in name_map:
232 232 plugin_name = name_map[plugin_name]
233 233 enabled_plugin = 'auth_%s_enabled' % plugin_name
234 234 cache_ttl = 'auth_%s_cache_ttl' % plugin_name
235 235
236 236 # default params that are needed for each plugin,
237 237 # `enabled` and `cache_ttl`
238 238 params.update({
239 239 enabled_plugin: True,
240 240 cache_ttl: 0
241 241 })
242 242 if override.get:
243 243 params.update(override.get(module, {}))
244 244
245 245 validated_params = params
246 246 for k, v in validated_params.items():
247 247 setting = SettingsModel().create_or_update_setting(k, v)
248 248 Session().add(setting)
249 249 Session().commit()
250 250
251 251 def cleanup():
252 252 _enable_plugins(['egg:rhodecode-enterprise-ce#rhodecode'])
253 253
254 254 request.addfinalizer(cleanup)
255 255
256 256 return _enable_plugins
257 257
258 258
259 259 @pytest.fixture
260 260 def fs_repo_only(request, rhodecode_fixtures):
261 261 def fs_repo_fabric(repo_name, repo_type):
262 262 rhodecode_fixtures.create_repo(repo_name, repo_type=repo_type)
263 263 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=False)
264 264
265 265 def cleanup():
266 266 rhodecode_fixtures.destroy_repo(repo_name, fs_remove=True)
267 267 rhodecode_fixtures.destroy_repo_on_filesystem(repo_name)
268 268
269 269 request.addfinalizer(cleanup)
270 270
271 271 return fs_repo_fabric
@@ -1,77 +1,79 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Test suite for making push/pull operations, on specially modified INI files
23 23
24 24 .. important::
25 25
26 26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 27 to redirect things to stderr instead of stdout.
28 28 """
29 29
30 30 import os
31 31 import pytest
32 32
33 33 from rhodecode.lib.vcs.backends.git.repository import GitRepository
34 34 from rhodecode.lib.vcs.nodes import FileNode
35 35 from rhodecode.tests import GIT_REPO
36 36 from rhodecode.tests.other.vcs_operations import Command
37 37 from .test_vcs_operations import _check_proper_clone, _check_proper_git_push
38 38
39 39
40 40 # override rc_web_server_config fixture with custom INI
41 41 @pytest.fixture(scope="module")
42 42 def rc_web_server_config(testini_factory):
43 43 """
44 44 Configuration file used for the fixture `rc_web_server`.
45 45 """
46 46 CUSTOM_PARAMS = [
47 47 {'handler_console': {'level': 'DEBUG'}},
48 48 ]
49 49 return testini_factory(CUSTOM_PARAMS)
50 50
51 51
52 52 def test_git_clone_with_small_push_buffer(backend_git, rc_web_server, tmpdir):
53 53 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
54 54 cmd = Command('/tmp')
55 stdout, stderr = cmd.execute('git -c http.postBuffer=1024 clone', clone_url, tmpdir.strpath)
55 stdout, stderr = cmd.execute(
56 'git -c http.postBuffer=1024 clone', clone_url, tmpdir.strpath)
56 57 _check_proper_clone(stdout, stderr, 'git')
57 58 cmd.assert_returncode_success()
58 59
59 60
60 61 def test_git_push_with_small_push_buffer(backend_git, rc_web_server, tmpdir):
61 62 empty_repo = backend_git.create_repo()
62 63
63 64 clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name)
64 65
65 66 cmd = Command(tmpdir.strpath)
66 67 cmd.execute('git clone', clone_url)
67 68
68 69 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
69 70 repo.in_memory_commit.add(FileNode('readme.md', content='## Hello'))
70 71 repo.in_memory_commit.commit(
71 72 message='Commit on branch Master',
72 73 author='Automatic test',
73 74 branch='master')
74 75
75 76 repo_cmd = Command(repo.path)
76 stdout, stderr = repo_cmd.execute('git -c http.postBuffer=1024 push --verbose origin master')
77 stdout, stderr = repo_cmd.execute(
78 'git -c http.postBuffer=1024 push --verbose origin master')
77 79 _check_proper_git_push(stdout, stderr, branch='master')
@@ -1,678 +1,682 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Test suite for making push/pull operations, on specially modified INI files
23 23
24 24 .. important::
25 25
26 26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 27 to redirect things to stderr instead of stdout.
28 28 """
29 29
30 30
31 31 import os
32 32 import time
33 33
34 34 import pytest
35 35
36 36 from rhodecode.lib.vcs.backends.git.repository import GitRepository
37 37 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
38 38 from rhodecode.lib.vcs.nodes import FileNode
39 39 from rhodecode.model.auth_token import AuthTokenModel
40 40 from rhodecode.model.db import Repository, UserIpMap, CacheKey
41 41 from rhodecode.model.meta import Session
42 42 from rhodecode.model.repo import RepoModel
43 43 from rhodecode.model.user import UserModel
44 44 from rhodecode.tests import (GIT_REPO, HG_REPO, TEST_USER_ADMIN_LOGIN)
45 45
46 46 from rhodecode.tests.other.vcs_operations import (
47 47 Command, _check_proper_clone, _check_proper_git_push,
48 48 _check_proper_hg_push, _add_files_and_push,
49 49 HG_REPO_WITH_GROUP, GIT_REPO_WITH_GROUP)
50 50
51 51
52 @pytest.fixture(scope="session")
53 def vcs_server_config_override():
54 return ({'server:main': {'workers': 2}},)
55
56
52 57 @pytest.mark.usefixtures("disable_locking", "disable_anonymous_user")
53 58 class TestVCSOperations(object):
54 59
55 60 def test_clone_hg_repo_by_admin(self, rc_web_server, tmpdir):
56 61 clone_url = rc_web_server.repo_clone_url(HG_REPO)
57 62 stdout, stderr = Command('/tmp').execute(
58 63 'hg clone', clone_url, tmpdir.strpath)
59 64 _check_proper_clone(stdout, stderr, 'hg')
60 65
61 66 def test_clone_git_repo_by_admin(self, rc_web_server, tmpdir):
62 67 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
63 68 cmd = Command('/tmp')
64 69 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
65 70 _check_proper_clone(stdout, stderr, 'git')
66 71 cmd.assert_returncode_success()
67 72
68 73 def test_clone_git_repo_by_admin_with_git_suffix(self, rc_web_server, tmpdir):
69 74 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
70 75 cmd = Command('/tmp')
71 76 stdout, stderr = cmd.execute('git clone', clone_url+".git", tmpdir.strpath)
72 77 _check_proper_clone(stdout, stderr, 'git')
73 78 cmd.assert_returncode_success()
74 79
75 80 def test_clone_hg_repo_by_id_by_admin(self, rc_web_server, tmpdir):
76 81 repo_id = Repository.get_by_repo_name(HG_REPO).repo_id
77 82 clone_url = rc_web_server.repo_clone_url('_%s' % repo_id)
78 83 stdout, stderr = Command('/tmp').execute(
79 84 'hg clone', clone_url, tmpdir.strpath)
80 85 _check_proper_clone(stdout, stderr, 'hg')
81 86
82 87 def test_clone_git_repo_by_id_by_admin(self, rc_web_server, tmpdir):
83 88 repo_id = Repository.get_by_repo_name(GIT_REPO).repo_id
84 89 clone_url = rc_web_server.repo_clone_url('_%s' % repo_id)
85 90 cmd = Command('/tmp')
86 91 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
87 92 _check_proper_clone(stdout, stderr, 'git')
88 93 cmd.assert_returncode_success()
89 94
90 95 def test_clone_hg_repo_with_group_by_admin(self, rc_web_server, tmpdir):
91 96 clone_url = rc_web_server.repo_clone_url(HG_REPO_WITH_GROUP)
92 97 stdout, stderr = Command('/tmp').execute(
93 98 'hg clone', clone_url, tmpdir.strpath)
94 99 _check_proper_clone(stdout, stderr, 'hg')
95 100
96 101 def test_clone_git_repo_with_group_by_admin(self, rc_web_server, tmpdir):
97 102 clone_url = rc_web_server.repo_clone_url(GIT_REPO_WITH_GROUP)
98 103 cmd = Command('/tmp')
99 104 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
100 105 _check_proper_clone(stdout, stderr, 'git')
101 106 cmd.assert_returncode_success()
102 107
103 108 def test_clone_git_repo_shallow_by_admin(self, rc_web_server, tmpdir):
104 109 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
105 110 cmd = Command('/tmp')
106 111 stdout, stderr = cmd.execute(
107 112 'git clone --depth=1', clone_url, tmpdir.strpath)
108 113
109 114 assert '' == stdout
110 115 assert 'Cloning into' in stderr
111 116 cmd.assert_returncode_success()
112 117
113 118 def test_clone_wrong_credentials_hg(self, rc_web_server, tmpdir):
114 119 clone_url = rc_web_server.repo_clone_url(HG_REPO, passwd='bad!')
115 120 stdout, stderr = Command('/tmp').execute(
116 121 'hg clone', clone_url, tmpdir.strpath)
117 122 assert 'abort: authorization failed' in stderr
118 123
119 124 def test_clone_wrong_credentials_git(self, rc_web_server, tmpdir):
120 125 clone_url = rc_web_server.repo_clone_url(GIT_REPO, passwd='bad!')
121 126 stdout, stderr = Command('/tmp').execute(
122 127 'git clone', clone_url, tmpdir.strpath)
123 128 assert 'fatal: Authentication failed' in stderr
124 129
125 130 def test_clone_git_dir_as_hg(self, rc_web_server, tmpdir):
126 131 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
127 132 stdout, stderr = Command('/tmp').execute(
128 133 'hg clone', clone_url, tmpdir.strpath)
129 134 assert 'HTTP Error 404: Not Found' in stderr
130 135
131 136 def test_clone_hg_repo_as_git(self, rc_web_server, tmpdir):
132 137 clone_url = rc_web_server.repo_clone_url(HG_REPO)
133 138 stdout, stderr = Command('/tmp').execute(
134 139 'git clone', clone_url, tmpdir.strpath)
135 140 assert 'not found' in stderr
136 141
137 142 def test_clone_non_existing_path_hg(self, rc_web_server, tmpdir):
138 143 clone_url = rc_web_server.repo_clone_url('trololo')
139 144 stdout, stderr = Command('/tmp').execute(
140 145 'hg clone', clone_url, tmpdir.strpath)
141 146 assert 'HTTP Error 404: Not Found' in stderr
142 147
143 148 def test_clone_non_existing_path_git(self, rc_web_server, tmpdir):
144 149 clone_url = rc_web_server.repo_clone_url('trololo')
145 150 stdout, stderr = Command('/tmp').execute('git clone', clone_url)
146 151 assert 'not found' in stderr
147 152
148 153 def test_clone_existing_path_hg_not_in_database(
149 154 self, rc_web_server, tmpdir, fs_repo_only):
150 155
151 156 db_name = fs_repo_only('not-in-db-hg', repo_type='hg')
152 157 clone_url = rc_web_server.repo_clone_url(db_name)
153 158 stdout, stderr = Command('/tmp').execute(
154 159 'hg clone', clone_url, tmpdir.strpath)
155 160 assert 'HTTP Error 404: Not Found' in stderr
156 161
157 162 def test_clone_existing_path_git_not_in_database(
158 163 self, rc_web_server, tmpdir, fs_repo_only):
159 164 db_name = fs_repo_only('not-in-db-git', repo_type='git')
160 165 clone_url = rc_web_server.repo_clone_url(db_name)
161 166 stdout, stderr = Command('/tmp').execute(
162 167 'git clone', clone_url, tmpdir.strpath)
163 168 assert 'not found' in stderr
164 169
165 170 def test_clone_existing_path_hg_not_in_database_different_scm(
166 171 self, rc_web_server, tmpdir, fs_repo_only):
167 172 db_name = fs_repo_only('not-in-db-git', repo_type='git')
168 173 clone_url = rc_web_server.repo_clone_url(db_name)
169 174 stdout, stderr = Command('/tmp').execute(
170 175 'hg clone', clone_url, tmpdir.strpath)
171 176 assert 'HTTP Error 404: Not Found' in stderr
172 177
173 178 def test_clone_existing_path_git_not_in_database_different_scm(
174 179 self, rc_web_server, tmpdir, fs_repo_only):
175 180 db_name = fs_repo_only('not-in-db-hg', repo_type='hg')
176 181 clone_url = rc_web_server.repo_clone_url(db_name)
177 182 stdout, stderr = Command('/tmp').execute(
178 183 'git clone', clone_url, tmpdir.strpath)
179 184 assert 'not found' in stderr
180 185
181 186 def test_clone_non_existing_store_path_hg(self, rc_web_server, tmpdir, user_util):
182 187 repo = user_util.create_repo()
183 188 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
184 189
185 190 # Damage repo by removing it's folder
186 191 RepoModel()._delete_filesystem_repo(repo)
187 192
188 193 stdout, stderr = Command('/tmp').execute(
189 194 'hg clone', clone_url, tmpdir.strpath)
190 195 assert 'HTTP Error 404: Not Found' in stderr
191 196
192 197 def test_clone_non_existing_store_path_git(self, rc_web_server, tmpdir, user_util):
193 198 repo = user_util.create_repo(repo_type='git')
194 199 clone_url = rc_web_server.repo_clone_url(repo.repo_name)
195 200
196 201 # Damage repo by removing it's folder
197 202 RepoModel()._delete_filesystem_repo(repo)
198 203
199 204 stdout, stderr = Command('/tmp').execute(
200 205 'git clone', clone_url, tmpdir.strpath)
201 206 assert 'not found' in stderr
202 207
203 208 def test_push_new_file_hg(self, rc_web_server, tmpdir):
204 209 clone_url = rc_web_server.repo_clone_url(HG_REPO)
205 210 stdout, stderr = Command('/tmp').execute(
206 211 'hg clone', clone_url, tmpdir.strpath)
207 212
208 213 stdout, stderr = _add_files_and_push(
209 214 'hg', tmpdir.strpath, clone_url=clone_url)
210 215
211 216 assert 'pushing to' in stdout
212 217 assert 'size summary' in stdout
213 218
214 219 def test_push_new_file_git(self, rc_web_server, tmpdir):
215 220 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
216 221 stdout, stderr = Command('/tmp').execute(
217 222 'git clone', clone_url, tmpdir.strpath)
218 223
219 224 # commit some stuff into this repo
220 225 stdout, stderr = _add_files_and_push(
221 226 'git', tmpdir.strpath, clone_url=clone_url)
222 227
223 228 _check_proper_git_push(stdout, stderr)
224 229
225 230 def test_push_invalidates_cache_hg(self, rc_web_server, tmpdir):
226 231 key = CacheKey.query().filter(CacheKey.cache_key == HG_REPO).scalar()
227 232 if not key:
228 233 key = CacheKey(HG_REPO, HG_REPO)
229 234
230 235 key.cache_active = True
231 236 Session().add(key)
232 237 Session().commit()
233 238
234 239 clone_url = rc_web_server.repo_clone_url(HG_REPO)
235 240 stdout, stderr = Command('/tmp').execute(
236 241 'hg clone', clone_url, tmpdir.strpath)
237 242
238 243 stdout, stderr = _add_files_and_push(
239 244 'hg', tmpdir.strpath, clone_url=clone_url, files_no=1)
240 245
241 246 key = CacheKey.query().filter(CacheKey.cache_key == HG_REPO).one()
242 247 assert key.cache_active is False
243 248
244 249 def test_push_invalidates_cache_git(self, rc_web_server, tmpdir):
245 250 key = CacheKey.query().filter(CacheKey.cache_key == GIT_REPO).scalar()
246 251 if not key:
247 252 key = CacheKey(GIT_REPO, GIT_REPO)
248 253
249 254 key.cache_active = True
250 255 Session().add(key)
251 256 Session().commit()
252 257
253 258 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
254 259 stdout, stderr = Command('/tmp').execute(
255 260 'git clone', clone_url, tmpdir.strpath)
256 261
257 262 # commit some stuff into this repo
258 263 stdout, stderr = _add_files_and_push(
259 264 'git', tmpdir.strpath, clone_url=clone_url, files_no=1)
260 265 _check_proper_git_push(stdout, stderr)
261 266
262 267 key = CacheKey.query().filter(CacheKey.cache_key == GIT_REPO).one()
263 268
264 269 assert key.cache_active is False
265 270
266 271 def test_push_wrong_credentials_hg(self, rc_web_server, tmpdir):
267 272 clone_url = rc_web_server.repo_clone_url(HG_REPO)
268 273 stdout, stderr = Command('/tmp').execute(
269 274 'hg clone', clone_url, tmpdir.strpath)
270 275
271 276 push_url = rc_web_server.repo_clone_url(
272 277 HG_REPO, user='bad', passwd='name')
273 278 stdout, stderr = _add_files_and_push(
274 279 'hg', tmpdir.strpath, clone_url=push_url)
275 280
276 281 assert 'abort: authorization failed' in stderr
277 282
278 283 def test_push_wrong_credentials_git(self, rc_web_server, tmpdir):
279 284 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
280 285 stdout, stderr = Command('/tmp').execute(
281 286 'git clone', clone_url, tmpdir.strpath)
282 287
283 288 push_url = rc_web_server.repo_clone_url(
284 289 GIT_REPO, user='bad', passwd='name')
285 290 stdout, stderr = _add_files_and_push(
286 291 'git', tmpdir.strpath, clone_url=push_url)
287 292
288 293 assert 'fatal: Authentication failed' in stderr
289 294
290 295 def test_push_back_to_wrong_url_hg(self, rc_web_server, tmpdir):
291 296 clone_url = rc_web_server.repo_clone_url(HG_REPO)
292 297 stdout, stderr = Command('/tmp').execute(
293 298 'hg clone', clone_url, tmpdir.strpath)
294 299
295 300 stdout, stderr = _add_files_and_push(
296 301 'hg', tmpdir.strpath,
297 302 clone_url=rc_web_server.repo_clone_url('not-existing'))
298 303
299 304 assert 'HTTP Error 404: Not Found' in stderr
300 305
301 306 def test_push_back_to_wrong_url_git(self, rc_web_server, tmpdir):
302 307 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
303 308 stdout, stderr = Command('/tmp').execute(
304 309 'git clone', clone_url, tmpdir.strpath)
305 310
306 311 stdout, stderr = _add_files_and_push(
307 312 'git', tmpdir.strpath,
308 313 clone_url=rc_web_server.repo_clone_url('not-existing'))
309 314
310 315 assert 'not found' in stderr
311 316
312 317 def test_ip_restriction_hg(self, rc_web_server, tmpdir):
313 318 user_model = UserModel()
314 319 try:
315 320 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
316 321 Session().commit()
317 322 time.sleep(2)
318 323 clone_url = rc_web_server.repo_clone_url(HG_REPO)
319 324 stdout, stderr = Command('/tmp').execute(
320 325 'hg clone', clone_url, tmpdir.strpath)
321 326 assert 'abort: HTTP Error 403: Forbidden' in stderr
322 327 finally:
323 328 # release IP restrictions
324 329 for ip in UserIpMap.getAll():
325 330 UserIpMap.delete(ip.ip_id)
326 331 Session().commit()
327 332
328 333 time.sleep(2)
329 334
330 335 stdout, stderr = Command('/tmp').execute(
331 336 'hg clone', clone_url, tmpdir.strpath)
332 337 _check_proper_clone(stdout, stderr, 'hg')
333 338
334 339 def test_ip_restriction_git(self, rc_web_server, tmpdir):
335 340 user_model = UserModel()
336 341 try:
337 342 user_model.add_extra_ip(TEST_USER_ADMIN_LOGIN, '10.10.10.10/32')
338 343 Session().commit()
339 344 time.sleep(2)
340 345 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
341 346 stdout, stderr = Command('/tmp').execute(
342 347 'git clone', clone_url, tmpdir.strpath)
343 348 msg = "The requested URL returned error: 403"
344 349 assert msg in stderr
345 350 finally:
346 351 # release IP restrictions
347 352 for ip in UserIpMap.getAll():
348 353 UserIpMap.delete(ip.ip_id)
349 354 Session().commit()
350 355
351 356 time.sleep(2)
352 357
353 358 cmd = Command('/tmp')
354 359 stdout, stderr = cmd.execute('git clone', clone_url, tmpdir.strpath)
355 360 cmd.assert_returncode_success()
356 361 _check_proper_clone(stdout, stderr, 'git')
357 362
358 363 def test_clone_by_auth_token(
359 364 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
360 365 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
361 366 'egg:rhodecode-enterprise-ce#rhodecode'])
362 367
363 368 user = user_util.create_user()
364 369 token = user.auth_tokens[1]
365 370
366 371 clone_url = rc_web_server.repo_clone_url(
367 372 HG_REPO, user=user.username, passwd=token)
368 373
369 374 stdout, stderr = Command('/tmp').execute(
370 375 'hg clone', clone_url, tmpdir.strpath)
371 376 _check_proper_clone(stdout, stderr, 'hg')
372 377
373 378 def test_clone_by_auth_token_expired(
374 379 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
375 380 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
376 381 'egg:rhodecode-enterprise-ce#rhodecode'])
377 382
378 383 user = user_util.create_user()
379 384 auth_token = AuthTokenModel().create(
380 385 user.user_id, 'test-token', -10, AuthTokenModel.cls.ROLE_VCS)
381 386 token = auth_token.api_key
382 387
383 388 clone_url = rc_web_server.repo_clone_url(
384 389 HG_REPO, user=user.username, passwd=token)
385 390
386 391 stdout, stderr = Command('/tmp').execute(
387 392 'hg clone', clone_url, tmpdir.strpath)
388 393 assert 'abort: authorization failed' in stderr
389 394
390 395 def test_clone_by_auth_token_bad_role(
391 396 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
392 397 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
393 398 'egg:rhodecode-enterprise-ce#rhodecode'])
394 399
395 400 user = user_util.create_user()
396 401 auth_token = AuthTokenModel().create(
397 402 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_API)
398 403 token = auth_token.api_key
399 404
400 405 clone_url = rc_web_server.repo_clone_url(
401 406 HG_REPO, user=user.username, passwd=token)
402 407
403 408 stdout, stderr = Command('/tmp').execute(
404 409 'hg clone', clone_url, tmpdir.strpath)
405 410 assert 'abort: authorization failed' in stderr
406 411
407 412 def test_clone_by_auth_token_user_disabled(
408 413 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
409 414 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
410 415 'egg:rhodecode-enterprise-ce#rhodecode'])
411 416 user = user_util.create_user()
412 417 user.active = False
413 418 Session().add(user)
414 419 Session().commit()
415 420 token = user.auth_tokens[1]
416 421
417 422 clone_url = rc_web_server.repo_clone_url(
418 423 HG_REPO, user=user.username, passwd=token)
419 424
420 425 stdout, stderr = Command('/tmp').execute(
421 426 'hg clone', clone_url, tmpdir.strpath)
422 427 assert 'abort: authorization failed' in stderr
423 428
424 429 def test_clone_by_auth_token_with_scope(
425 430 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
426 431 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
427 432 'egg:rhodecode-enterprise-ce#rhodecode'])
428 433 user = user_util.create_user()
429 434 auth_token = AuthTokenModel().create(
430 435 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
431 436 token = auth_token.api_key
432 437
433 438 # manually set scope
434 439 auth_token.repo = Repository.get_by_repo_name(HG_REPO)
435 440 Session().add(auth_token)
436 441 Session().commit()
437 442
438 443 clone_url = rc_web_server.repo_clone_url(
439 444 HG_REPO, user=user.username, passwd=token)
440 445
441 446 stdout, stderr = Command('/tmp').execute(
442 447 'hg clone', clone_url, tmpdir.strpath)
443 448 _check_proper_clone(stdout, stderr, 'hg')
444 449
445 450 def test_clone_by_auth_token_with_wrong_scope(
446 451 self, rc_web_server, tmpdir, user_util, enable_auth_plugins):
447 452 enable_auth_plugins(['egg:rhodecode-enterprise-ce#token',
448 453 'egg:rhodecode-enterprise-ce#rhodecode'])
449 454 user = user_util.create_user()
450 455 auth_token = AuthTokenModel().create(
451 456 user.user_id, 'test-token', -1, AuthTokenModel.cls.ROLE_VCS)
452 457 token = auth_token.api_key
453 458
454 459 # manually set scope
455 460 auth_token.repo = Repository.get_by_repo_name(GIT_REPO)
456 461 Session().add(auth_token)
457 462 Session().commit()
458 463
459 464 clone_url = rc_web_server.repo_clone_url(
460 465 HG_REPO, user=user.username, passwd=token)
461 466
462 467 stdout, stderr = Command('/tmp').execute(
463 468 'hg clone', clone_url, tmpdir.strpath)
464 469 assert 'abort: authorization failed' in stderr
465 470
466 471
472 @pytest.mark.usefixtures("disable_locking")
473 class TestVCSOperationsSpecial(object):
474
467 475 def test_git_sets_default_branch_if_not_master(
468 backend_git, tmpdir, disable_locking, rc_web_server):
476 self, backend_git, tmpdir, rc_web_server):
469 477 empty_repo = backend_git.create_repo()
470 478 clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name)
471 479
472 480 cmd = Command(tmpdir.strpath)
473 481 cmd.execute('git clone', clone_url)
474 482
475 483 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
476 484 repo.in_memory_commit.add(FileNode('file', content=''))
477 485 repo.in_memory_commit.commit(
478 486 message='Commit on branch test',
479 487 author='Automatic test',
480 488 branch='test')
481 489
482 490 repo_cmd = Command(repo.path)
483 491 stdout, stderr = repo_cmd.execute('git push --verbose origin test')
484 492 _check_proper_git_push(
485 493 stdout, stderr, branch='test', should_set_default_branch=True)
486 494
487 495 stdout, stderr = cmd.execute(
488 496 'git clone', clone_url, empty_repo.repo_name + '-clone')
489 497 _check_proper_clone(stdout, stderr, 'git')
490 498
491 499 # Doing an explicit commit in order to get latest user logs on MySQL
492 500 Session().commit()
493 501
494
495 502 def test_git_fetches_from_remote_repository_with_annotated_tags(
496 backend_git, disable_locking, rc_web_server):
503 self, backend_git, rc_web_server):
497 504 # Note: This is a test specific to the git backend. It checks the
498 505 # integration of fetching from a remote repository which contains
499 506 # annotated tags.
500 507
501 508 # Dulwich shows this specific behavior only when
502 509 # operating against a remote repository.
503 510 source_repo = backend_git['annotated-tag']
504 511 target_vcs_repo = backend_git.create_repo().scm_instance()
505 512 target_vcs_repo.fetch(rc_web_server.repo_clone_url(source_repo.repo_name))
506 513
507
508 def test_git_push_shows_pull_request_refs(backend_git, rc_web_server, tmpdir):
514 def test_git_push_shows_pull_request_refs(self, backend_git, rc_web_server, tmpdir):
509 515 """
510 516 test if remote info about refs is visible
511 517 """
512 518 empty_repo = backend_git.create_repo()
513 519
514 520 clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name)
515 521
516 522 cmd = Command(tmpdir.strpath)
517 523 cmd.execute('git clone', clone_url)
518 524
519 525 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
520 526 repo.in_memory_commit.add(FileNode('readme.md', content='## Hello'))
521 527 repo.in_memory_commit.commit(
522 528 message='Commit on branch Master',
523 529 author='Automatic test',
524 530 branch='master')
525 531
526 532 repo_cmd = Command(repo.path)
527 533 stdout, stderr = repo_cmd.execute('git push --verbose origin master')
528 534 _check_proper_git_push(stdout, stderr, branch='master')
529 535
530 536 ref = '{}/{}/pull-request/new?branch=master'.format(
531 537 rc_web_server.host_url(), empty_repo.repo_name)
532 538 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stderr
533 539 assert 'remote: RhodeCode: push completed' in stderr
534 540
535 541 # push on the same branch
536 542 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
537 543 repo.in_memory_commit.add(FileNode('setup.py', content='print\n'))
538 544 repo.in_memory_commit.commit(
539 545 message='Commit2 on branch Master',
540 546 author='Automatic test2',
541 547 branch='master')
542 548
543 549 repo_cmd = Command(repo.path)
544 550 stdout, stderr = repo_cmd.execute('git push --verbose origin master')
545 551 _check_proper_git_push(stdout, stderr, branch='master')
546 552
547 553 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stderr
548 554 assert 'remote: RhodeCode: push completed' in stderr
549 555
550 556 # new Branch
551 557 repo = GitRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
552 558 repo.in_memory_commit.add(FileNode('feature1.py', content='## Hello world'))
553 559 repo.in_memory_commit.commit(
554 560 message='Commit on branch feature',
555 561 author='Automatic test',
556 562 branch='feature')
557 563
558 564 repo_cmd = Command(repo.path)
559 565 stdout, stderr = repo_cmd.execute('git push --verbose origin feature')
560 566 _check_proper_git_push(stdout, stderr, branch='feature')
561 567
562 568 ref = '{}/{}/pull-request/new?branch=feature'.format(
563 569 rc_web_server.host_url(), empty_repo.repo_name)
564 570 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stderr
565 571 assert 'remote: RhodeCode: push completed' in stderr
566 572
567
568 def test_hg_push_shows_pull_request_refs(backend_hg, rc_web_server, tmpdir):
573 def test_hg_push_shows_pull_request_refs(self, backend_hg, rc_web_server, tmpdir):
569 574 empty_repo = backend_hg.create_repo()
570 575
571 576 clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name)
572 577
573 578 cmd = Command(tmpdir.strpath)
574 579 cmd.execute('hg clone', clone_url)
575 580
576 581 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
577 582 repo.in_memory_commit.add(FileNode(u'readme.md', content=u'## Hello'))
578 583 repo.in_memory_commit.commit(
579 584 message=u'Commit on branch default',
580 585 author=u'Automatic test',
581 586 branch='default')
582 587
583 588 repo_cmd = Command(repo.path)
584 589 repo_cmd.execute('hg checkout default')
585 590
586 591 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
587 592 _check_proper_hg_push(stdout, stderr, branch='default')
588 593
589 594 ref = '{}/{}/pull-request/new?branch=default'.format(
590 595 rc_web_server.host_url(), empty_repo.repo_name)
591 596 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
592 597 assert 'remote: RhodeCode: push completed' in stdout
593 598
594 599 # push on the same branch
595 600 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
596 601 repo.in_memory_commit.add(FileNode(u'setup.py', content=u'print\n'))
597 602 repo.in_memory_commit.commit(
598 603 message=u'Commit2 on branch default',
599 604 author=u'Automatic test2',
600 605 branch=u'default')
601 606
602 607 repo_cmd = Command(repo.path)
603 608 repo_cmd.execute('hg checkout default')
604 609
605 610 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
606 611 _check_proper_hg_push(stdout, stderr, branch='default')
607 612
608 613 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
609 614 assert 'remote: RhodeCode: push completed' in stdout
610 615
611 616 # new Branch
612 617 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
613 618 repo.in_memory_commit.add(FileNode(u'feature1.py', content=u'## Hello world'))
614 619 repo.in_memory_commit.commit(
615 620 message=u'Commit on branch feature',
616 621 author=u'Automatic test',
617 622 branch=u'feature')
618 623
619 624 repo_cmd = Command(repo.path)
620 625 repo_cmd.execute('hg checkout feature')
621 626
622 627 stdout, stderr = repo_cmd.execute('hg push --new-branch --verbose', clone_url)
623 628 _check_proper_hg_push(stdout, stderr, branch='feature')
624 629
625 630 ref = '{}/{}/pull-request/new?branch=feature'.format(
626 631 rc_web_server.host_url(), empty_repo.repo_name)
627 632 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
628 633 assert 'remote: RhodeCode: push completed' in stdout
629 634
630
631 def test_hg_push_shows_pull_request_refs_book(backend_hg, rc_web_server, tmpdir):
635 def test_hg_push_shows_pull_request_refs_book(self, backend_hg, rc_web_server, tmpdir):
632 636 empty_repo = backend_hg.create_repo()
633 637
634 638 clone_url = rc_web_server.repo_clone_url(empty_repo.repo_name)
635 639
636 640 cmd = Command(tmpdir.strpath)
637 641 cmd.execute('hg clone', clone_url)
638 642
639 643 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
640 644 repo.in_memory_commit.add(FileNode(u'readme.md', content=u'## Hello'))
641 645 repo.in_memory_commit.commit(
642 646 message=u'Commit on branch default',
643 647 author=u'Automatic test',
644 648 branch='default')
645 649
646 650 repo_cmd = Command(repo.path)
647 651 repo_cmd.execute('hg checkout default')
648 652
649 653 stdout, stderr = repo_cmd.execute('hg push --verbose', clone_url)
650 654 _check_proper_hg_push(stdout, stderr, branch='default')
651 655
652 656 ref = '{}/{}/pull-request/new?branch=default'.format(
653 657 rc_web_server.host_url(), empty_repo.repo_name)
654 658 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
655 659 assert 'remote: RhodeCode: push completed' in stdout
656 660
657 661 # add bookmark
658 662 repo = MercurialRepository(os.path.join(tmpdir.strpath, empty_repo.repo_name))
659 663 repo.in_memory_commit.add(FileNode(u'setup.py', content=u'print\n'))
660 664 repo.in_memory_commit.commit(
661 665 message=u'Commit2 on branch default',
662 666 author=u'Automatic test2',
663 667 branch=u'default')
664 668
665 669 repo_cmd = Command(repo.path)
666 670 repo_cmd.execute('hg checkout default')
667 671 repo_cmd.execute('hg bookmark feature2')
668 672 stdout, stderr = repo_cmd.execute('hg push -B feature2 --verbose', clone_url)
669 673 _check_proper_hg_push(stdout, stderr, branch='default')
670 674
671 675 ref = '{}/{}/pull-request/new?branch=default'.format(
672 676 rc_web_server.host_url(), empty_repo.repo_name)
673 677 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
674 678 ref = '{}/{}/pull-request/new?bookmark=feature2'.format(
675 679 rc_web_server.host_url(), empty_repo.repo_name)
676 680 assert 'remote: RhodeCode: open pull request link: {}'.format(ref) in stdout
677 681 assert 'remote: RhodeCode: push completed' in stdout
678 682 assert 'exporting bookmark feature2' in stdout
@@ -1,141 +1,146 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 """
22 22 Test suite for making push/pull operations, on specially modified INI files
23 23
24 24 .. important::
25 25
26 26 You must have git >= 1.8.5 for tests to work fine. With 68b939b git started
27 27 to redirect things to stderr instead of stdout.
28 28 """
29 29
30 30 import pytest
31 31 import requests
32 32
33 33 from rhodecode import events
34 34 from rhodecode.model.db import Integration
35 35 from rhodecode.model.integration import IntegrationModel
36 36 from rhodecode.model.meta import Session
37 37
38 38 from rhodecode.tests import GIT_REPO, HG_REPO
39 39 from rhodecode.tests.other.vcs_operations import Command, _add_files_and_push
40 40 from rhodecode.integrations.types.webhook import WebhookIntegrationType
41 41
42 42
43 43 def check_connection():
44 44 try:
45 45 response = requests.get('http://httpbin.org')
46 46 return response.status_code == 200
47 47 except Exception as e:
48 48 print(e)
49 49
50 50 return False
51 51
52 52
53 53 connection_available = pytest.mark.skipif(
54 54 not check_connection(), reason="No outside internet connection available")
55 55
56 56
57 57 @pytest.fixture
58 58 def enable_webhook_push_integration(request):
59 59 integration = Integration()
60 60 integration.integration_type = WebhookIntegrationType.key
61 61 Session().add(integration)
62 62
63 63 settings = dict(
64 64 url='http://httpbin.org',
65 65 secret_token='secret',
66 66 username=None,
67 67 password=None,
68 68 custom_header_key=None,
69 69 custom_header_val=None,
70 70 method_type='get',
71 71 events=[events.RepoPushEvent.name],
72 72 log_data=True
73 73 )
74 74
75 75 IntegrationModel().update_integration(
76 76 integration,
77 77 name='IntegrationWebhookTest',
78 78 enabled=True,
79 79 settings=settings,
80 80 repo=None,
81 81 repo_group=None,
82 82 child_repos_only=False,
83 83 )
84 84 Session().commit()
85 85 integration_id = integration.integration_id
86 86
87 87 @request.addfinalizer
88 88 def cleanup():
89 89 integration = Integration.get(integration_id)
90 90 Session().delete(integration)
91 91 Session().commit()
92 92
93 93
94 @pytest.fixture(scope="session")
95 def vcs_server_config_override():
96 return ({'server:main': {'workers': 2}},)
97
98
94 99 @pytest.mark.usefixtures(
95 100 "disable_locking", "disable_anonymous_user",
96 101 "enable_webhook_push_integration")
97 102 class TestVCSOperationsOnCustomIniConfig(object):
98 103
99 104 def test_push_tag_with_commit_hg(self, rc_web_server, tmpdir):
100 105 clone_url = rc_web_server.repo_clone_url(HG_REPO)
101 106 stdout, stderr = Command('/tmp').execute(
102 107 'hg clone', clone_url, tmpdir.strpath)
103 108
104 109 push_url = rc_web_server.repo_clone_url(HG_REPO)
105 110 _add_files_and_push(
106 111 'hg', tmpdir.strpath, clone_url=push_url,
107 112 tags=[{'name': 'v1.0.0', 'commit': 'added tag v1.0.0'}])
108 113
109 114 rc_log = rc_web_server.get_rc_log()
110 115 assert 'ERROR' not in rc_log
111 116 assert "'name': u'v1.0.0'" in rc_log
112 117
113 118 def test_push_tag_with_commit_git(
114 119 self, rc_web_server, tmpdir):
115 120 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
116 121 stdout, stderr = Command('/tmp').execute(
117 122 'git clone', clone_url, tmpdir.strpath)
118 123
119 124 push_url = rc_web_server.repo_clone_url(GIT_REPO)
120 125 _add_files_and_push(
121 126 'git', tmpdir.strpath, clone_url=push_url,
122 127 tags=[{'name': 'v1.0.0', 'commit': 'added tag v1.0.0'}])
123 128
124 129 rc_log = rc_web_server.get_rc_log()
125 130 assert 'ERROR' not in rc_log
126 131 assert "'name': u'v1.0.0'" in rc_log
127 132
128 133 def test_push_tag_with_no_commit_git(
129 134 self, rc_web_server, tmpdir):
130 135 clone_url = rc_web_server.repo_clone_url(GIT_REPO)
131 136 stdout, stderr = Command('/tmp').execute(
132 137 'git clone', clone_url, tmpdir.strpath)
133 138
134 139 push_url = rc_web_server.repo_clone_url(GIT_REPO)
135 140 _add_files_and_push(
136 141 'git', tmpdir.strpath, clone_url=push_url,
137 142 tags=[{'name': 'v1.0.0', 'commit': 'added tag v1.0.0'}])
138 143
139 144 rc_log = rc_web_server.get_rc_log()
140 145 assert 'ERROR' not in rc_log
141 146 assert "'name': u'v1.0.0'" in rc_log
@@ -1,400 +1,410 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2010-2017 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import os
22 22 import json
23 23 import platform
24 24 import socket
25 25 import tempfile
26 26
27 27 import subprocess32
28 28 import time
29 29 from urllib2 import urlopen, URLError
30 30
31 31 import configobj
32 32 import pytest
33 33
34 34 import pyramid.paster
35 35
36 36 from rhodecode.lib.pyramid_utils import get_app_config
37 37 from rhodecode.tests.fixture import TestINI
38 38 import rhodecode
39 39 from rhodecode.tests.other.vcs_operations.conftest import get_host_url, get_port
40 40
41 41 VCSSERVER_LOG = os.path.join(tempfile.gettempdir(), 'rc-vcsserver.log')
42 42
43 43
44 44 def _parse_json(value):
45 45 return json.loads(value) if value else None
46 46
47 47
48 48 def pytest_addoption(parser):
49 49 parser.addoption(
50 50 '--test-loglevel', dest='test_loglevel',
51 51 help="Set default Logging level for tests, warn (default), info, debug")
52 52 group = parser.getgroup('pylons')
53 53 group.addoption(
54 54 '--with-pylons', dest='pyramid_config',
55 55 help="Set up a Pylons environment with the specified config file.")
56 56 group.addoption(
57 57 '--ini-config-override', action='store', type=_parse_json,
58 58 default=None, dest='pyramid_config_override', help=(
59 59 "Overrides the .ini file settings. Should be specified in JSON"
60 60 " format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
61 61 )
62 62 )
63 63 parser.addini(
64 64 'pyramid_config',
65 65 "Set up a Pyramid environment with the specified config file.")
66 66
67 67 vcsgroup = parser.getgroup('vcs')
68 68 vcsgroup.addoption(
69 69 '--without-vcsserver', dest='with_vcsserver', action='store_false',
70 70 help="Do not start the VCSServer in a background process.")
71 71 vcsgroup.addoption(
72 72 '--with-vcsserver-http', dest='vcsserver_config_http',
73 73 help="Start the HTTP VCSServer with the specified config file.")
74 74 vcsgroup.addoption(
75 75 '--vcsserver-protocol', dest='vcsserver_protocol',
76 76 help="Start the VCSServer with HTTP protocol support.")
77 77 vcsgroup.addoption(
78 78 '--vcsserver-config-override', action='store', type=_parse_json,
79 79 default=None, dest='vcsserver_config_override', help=(
80 80 "Overrides the .ini file settings for the VCSServer. "
81 81 "Should be specified in JSON "
82 82 "format, e.g. '{\"section\": {\"parameter\": \"value\", ...}}'"
83 83 )
84 84 )
85 85 vcsgroup.addoption(
86 86 '--vcsserver-port', action='store', type=int,
87 87 default=None, help=(
88 88 "Allows to set the port of the vcsserver. Useful when testing "
89 89 "against an already running server and random ports cause "
90 90 "trouble."))
91 91 parser.addini(
92 92 'vcsserver_config_http',
93 93 "Start the HTTP VCSServer with the specified config file.")
94 94 parser.addini(
95 95 'vcsserver_protocol',
96 96 "Start the VCSServer with HTTP protocol support.")
97 97
98 98
99 @pytest.fixture(scope="session")
100 def vcs_server_config_override(request):
101 """
102 Allows injecting the overrides by specifying this inside test class
103 """
104
105 return ()
106
107
99 108 @pytest.fixture(scope='session')
100 def vcsserver(request, vcsserver_port, vcsserver_factory):
109 def vcsserver(request, vcsserver_port, vcsserver_factory, vcs_server_config_override):
101 110 """
102 111 Session scope VCSServer.
103 112
104 113 Tests wich need the VCSServer have to rely on this fixture in order
105 114 to ensure it will be running.
106 115
107 116 For specific needs, the fixture vcsserver_factory can be used. It allows to
108 117 adjust the configuration file for the test run.
109 118
110 119 Command line args:
111 120
112 121 --without-vcsserver: Allows to switch this fixture off. You have to
113 122 manually start the server.
114 123
115 124 --vcsserver-port: Will expect the VCSServer to listen on this port.
116 125 """
117 126
118 127 if not request.config.getoption('with_vcsserver'):
119 128 return None
120 129
121 130 use_http = _use_vcs_http_server(request.config)
122 131 return vcsserver_factory(
123 request, use_http=use_http, vcsserver_port=vcsserver_port)
132 request, use_http=use_http, vcsserver_port=vcsserver_port,
133 overrides=vcs_server_config_override)
124 134
125 135
126 136 @pytest.fixture(scope='session')
127 137 def vcsserver_factory(tmpdir_factory):
128 138 """
129 139 Use this if you need a running vcsserver with a special configuration.
130 140 """
131 141
132 142 def factory(request, use_http=True, overrides=(), vcsserver_port=None):
133 143
134 144 if vcsserver_port is None:
135 145 vcsserver_port = get_available_port()
136 146
137 147 overrides = list(overrides)
138 148 if use_http:
139 149 overrides.append({'server:main': {'port': vcsserver_port}})
140 150 else:
141 151 overrides.append({'DEFAULT': {'port': vcsserver_port}})
142 152
143 153 if is_cygwin():
144 154 platform_override = {'DEFAULT': {
145 155 'beaker.cache.repo_object.type': 'nocache'}}
146 156 overrides.append(platform_override)
147 157
148 158 option_name = 'vcsserver_config_http' if use_http else ''
149 159 override_option_name = 'vcsserver_config_override'
150 160 config_file = get_config(
151 161 request.config, option_name=option_name,
152 162 override_option_name=override_option_name, overrides=overrides,
153 163 basetemp=tmpdir_factory.getbasetemp().strpath,
154 164 prefix='test_vcs_')
155 165
156 166 print("Using the VCSServer configuration:{}".format(config_file))
157 167 ServerClass = HttpVCSServer if use_http else None
158 168 server = ServerClass(config_file)
159 169 server.start()
160 170
161 171 @request.addfinalizer
162 172 def cleanup():
163 173 server.shutdown()
164 174
165 175 server.wait_until_ready()
166 176 return server
167 177
168 178 return factory
169 179
170 180
171 181 def is_cygwin():
172 182 return 'cygwin' in platform.system().lower()
173 183
174 184
175 185 def _use_vcs_http_server(config):
176 186 protocol_option = 'vcsserver_protocol'
177 187 protocol = (
178 188 config.getoption(protocol_option) or
179 189 config.getini(protocol_option) or
180 190 'http')
181 191 return protocol == 'http'
182 192
183 193
184 194 def _use_log_level(config):
185 195 level = config.getoption('test_loglevel') or 'warn'
186 196 return level.upper()
187 197
188 198
189 199 class VCSServer(object):
190 200 """
191 201 Represents a running VCSServer instance.
192 202 """
193 203
194 204 _args = []
195 205
196 206 def start(self):
197 207 print("Starting the VCSServer: {}".format(self._args))
198 208 self.process = subprocess32.Popen(self._args)
199 209
200 210 def wait_until_ready(self, timeout=30):
201 211 raise NotImplementedError()
202 212
203 213 def shutdown(self):
204 214 self.process.kill()
205 215
206 216
207 217 class HttpVCSServer(VCSServer):
208 218 """
209 219 Represents a running VCSServer instance.
210 220 """
211 221 def __init__(self, config_file):
212 222 self.config_file = config_file
213 223 config_data = configobj.ConfigObj(config_file)
214 224 self._config = config_data['server:main']
215 225
216 args = ['gunicorn', '--workers', '1', '--paste', config_file]
226 args = ['gunicorn', '--paste', config_file]
217 227 self._args = args
218 228
219 229 @property
220 230 def http_url(self):
221 231 template = 'http://{host}:{port}/'
222 232 return template.format(**self._config)
223 233
224 234 def start(self):
225 235 env = os.environ.copy()
226 236 host_url = 'http://' + get_host_url(self.config_file)
227 237
228 238 rc_log = list(VCSSERVER_LOG.partition('.log'))
229 239 rc_log.insert(1, get_port(self.config_file))
230 240 rc_log = ''.join(rc_log)
231 241
232 242 server_out = open(rc_log, 'w')
233 243
234 244 command = ' '.join(self._args)
235 print('Starting rhodecode-vcsserver: {}'.format(host_url))
236 print('Command: {}'.format(command))
237 print('Logfile: {}'.format(rc_log))
245 print('rhodecode-vcsserver starting at: {}'.format(host_url))
246 print('rhodecode-vcsserver command: {}'.format(command))
247 print('rhodecode-vcsserver logfile: {}'.format(rc_log))
238 248 self.process = subprocess32.Popen(
239 249 self._args, bufsize=0, env=env, stdout=server_out, stderr=server_out)
240 250
241 251 def wait_until_ready(self, timeout=30):
242 252 host = self._config['host']
243 253 port = self._config['port']
244 254 status_url = 'http://{host}:{port}/status'.format(host=host, port=port)
245 255 start = time.time()
246 256
247 257 while time.time() - start < timeout:
248 258 try:
249 259 urlopen(status_url)
250 260 break
251 261 except URLError:
252 262 time.sleep(0.2)
253 263 else:
254 264 pytest.exit(
255 265 "Starting the VCSServer failed or took more than {} "
256 266 "seconds. cmd: `{}`".format(timeout, ' '.join(self._args)))
257 267
258 268 def shutdown(self):
259 269 self.process.kill()
260 270
261 271
262 272 @pytest.fixture(scope='session')
263 273 def ini_config(request, tmpdir_factory, rcserver_port, vcsserver_port):
264 274 option_name = 'pyramid_config'
265 275 log_level = _use_log_level(request.config)
266 276
267 277 overrides = [
268 278 {'server:main': {'port': rcserver_port}},
269 279 {'app:main': {
270 280 'vcs.server': 'localhost:%s' % vcsserver_port,
271 281 # johbo: We will always start the VCSServer on our own based on the
272 282 # fixtures of the test cases. For the test run it must always be
273 283 # off in the INI file.
274 284 'vcs.start_server': 'false',
275 285 }},
276 286
277 287 {'handler_console': {
278 288 'class ': 'StreamHandler',
279 289 'args ': '(sys.stderr,)',
280 290 'level': log_level,
281 291 }},
282 292
283 293 ]
284 294 if _use_vcs_http_server(request.config):
285 295 overrides.append({
286 296 'app:main': {
287 297 'vcs.server.protocol': 'http',
288 298 'vcs.scm_app_implementation': 'http',
289 299 'vcs.hooks.protocol': 'http',
290 300 }
291 301 })
292 302
293 303 filename = get_config(
294 304 request.config, option_name=option_name,
295 305 override_option_name='{}_override'.format(option_name),
296 306 overrides=overrides,
297 307 basetemp=tmpdir_factory.getbasetemp().strpath,
298 308 prefix='test_rce_')
299 309 return filename
300 310
301 311
302 312 @pytest.fixture(scope='session')
303 313 def ini_settings(ini_config):
304 314 ini_path = ini_config
305 315 return get_app_config(ini_path)
306 316
307 317
308 318 @pytest.fixture(scope='session')
309 319 def rcserver_port(request):
310 320 port = get_available_port()
311 321 print('Using rcserver port {}'.format(port))
312 322 return port
313 323
314 324
315 325 @pytest.fixture(scope='session')
316 326 def vcsserver_port(request):
317 327 port = request.config.getoption('--vcsserver-port')
318 328 if port is None:
319 329 port = get_available_port()
320 330 print('Using vcsserver port {}'.format(port))
321 331 return port
322 332
323 333
324 334 def get_available_port():
325 335 family = socket.AF_INET
326 336 socktype = socket.SOCK_STREAM
327 337 host = '127.0.0.1'
328 338
329 339 mysocket = socket.socket(family, socktype)
330 340 mysocket.bind((host, 0))
331 341 port = mysocket.getsockname()[1]
332 342 mysocket.close()
333 343 del mysocket
334 344 return port
335 345
336 346
337 347 @pytest.fixture(scope='session')
338 348 def available_port_factory():
339 349 """
340 350 Returns a callable which returns free port numbers.
341 351 """
342 352 return get_available_port
343 353
344 354
345 355 @pytest.fixture
346 356 def available_port(available_port_factory):
347 357 """
348 358 Gives you one free port for the current test.
349 359
350 360 Uses "available_port_factory" to retrieve the port.
351 361 """
352 362 return available_port_factory()
353 363
354 364
355 365 @pytest.fixture(scope='session')
356 366 def testini_factory(tmpdir_factory, ini_config):
357 367 """
358 368 Factory to create an INI file based on TestINI.
359 369
360 370 It will make sure to place the INI file in the correct directory.
361 371 """
362 372 basetemp = tmpdir_factory.getbasetemp().strpath
363 373 return TestIniFactory(basetemp, ini_config)
364 374
365 375
366 376 class TestIniFactory(object):
367 377
368 378 def __init__(self, basetemp, template_ini):
369 379 self._basetemp = basetemp
370 380 self._template_ini = template_ini
371 381
372 382 def __call__(self, ini_params, new_file_prefix='test'):
373 383 ini_file = TestINI(
374 384 self._template_ini, ini_params=ini_params,
375 385 new_file_prefix=new_file_prefix, dir=self._basetemp)
376 386 result = ini_file.create()
377 387 return result
378 388
379 389
380 390 def get_config(
381 391 config, option_name, override_option_name, overrides=None,
382 392 basetemp=None, prefix='test'):
383 393 """
384 394 Find a configuration file and apply overrides for the given `prefix`.
385 395 """
386 396 config_file = (
387 397 config.getoption(option_name) or config.getini(option_name))
388 398 if not config_file:
389 399 pytest.exit(
390 400 "Configuration error, could not extract {}.".format(option_name))
391 401
392 402 overrides = overrides or []
393 403 config_override = config.getoption(override_option_name)
394 404 if config_override:
395 405 overrides.append(config_override)
396 406 temp_ini_file = TestINI(
397 407 config_file, ini_params=overrides, new_file_prefix=prefix,
398 408 dir=basetemp)
399 409
400 410 return temp_ini_file.create()
General Comments 0
You need to be logged in to leave comments. Login now