##// END OF EJS Templates
tests: Fix test that expects the pyro4 scm app as default.
Martin Bornhold -
r975:97ed4c4f default
parent child Browse files
Show More
@@ -1,461 +1,461 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import base64
21 import base64
22
22
23 import mock
23 import mock
24 import pytest
24 import pytest
25 import webtest.app
25 import webtest.app
26
26
27 from rhodecode.lib.caching_query import FromCache
27 from rhodecode.lib.caching_query import FromCache
28 from rhodecode.lib.hooks_daemon import DummyHooksCallbackDaemon
28 from rhodecode.lib.hooks_daemon import DummyHooksCallbackDaemon
29 from rhodecode.lib.middleware import simplevcs
29 from rhodecode.lib.middleware import simplevcs
30 from rhodecode.lib.middleware.https_fixup import HttpsFixup
30 from rhodecode.lib.middleware.https_fixup import HttpsFixup
31 from rhodecode.lib.middleware.utils import scm_app
31 from rhodecode.lib.middleware.utils import scm_app_http
32 from rhodecode.model.db import User, _hash_key
32 from rhodecode.model.db import User, _hash_key
33 from rhodecode.model.meta import Session
33 from rhodecode.model.meta import Session
34 from rhodecode.tests import (
34 from rhodecode.tests import (
35 HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
35 HG_REPO, TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
36 from rhodecode.tests.lib.middleware import mock_scm_app
36 from rhodecode.tests.lib.middleware import mock_scm_app
37 from rhodecode.tests.utils import set_anonymous_access
37 from rhodecode.tests.utils import set_anonymous_access
38
38
39
39
40 class StubVCSController(simplevcs.SimpleVCS):
40 class StubVCSController(simplevcs.SimpleVCS):
41
41
42 SCM = 'hg'
42 SCM = 'hg'
43 stub_response_body = tuple()
43 stub_response_body = tuple()
44
44
45 def __init__(self, *args, **kwargs):
45 def __init__(self, *args, **kwargs):
46 super(StubVCSController, self).__init__(*args, **kwargs)
46 super(StubVCSController, self).__init__(*args, **kwargs)
47 self._action = 'pull'
47 self._action = 'pull'
48 self._name = HG_REPO
48 self._name = HG_REPO
49 self.set_repo_names(None)
49 self.set_repo_names(None)
50
50
51 def _get_repository_name(self, environ):
51 def _get_repository_name(self, environ):
52 return self._name
52 return self._name
53
53
54 def _get_action(self, environ):
54 def _get_action(self, environ):
55 return self._action
55 return self._action
56
56
57 def _create_wsgi_app(self, repo_path, repo_name, config):
57 def _create_wsgi_app(self, repo_path, repo_name, config):
58 def fake_app(environ, start_response):
58 def fake_app(environ, start_response):
59 start_response('200 OK', [])
59 start_response('200 OK', [])
60 return self.stub_response_body
60 return self.stub_response_body
61 return fake_app
61 return fake_app
62
62
63 def _create_config(self, extras, repo_name):
63 def _create_config(self, extras, repo_name):
64 return None
64 return None
65
65
66
66
67 @pytest.fixture
67 @pytest.fixture
68 def vcscontroller(pylonsapp, config_stub):
68 def vcscontroller(pylonsapp, config_stub):
69 config_stub.testing_securitypolicy()
69 config_stub.testing_securitypolicy()
70 config_stub.include('rhodecode.authentication')
70 config_stub.include('rhodecode.authentication')
71
71
72 set_anonymous_access(True)
72 set_anonymous_access(True)
73 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
73 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
74 app = HttpsFixup(controller, pylonsapp.config)
74 app = HttpsFixup(controller, pylonsapp.config)
75 app = webtest.app.TestApp(app)
75 app = webtest.app.TestApp(app)
76
76
77 _remove_default_user_from_query_cache()
77 _remove_default_user_from_query_cache()
78
78
79 # Sanity checks that things are set up correctly
79 # Sanity checks that things are set up correctly
80 app.get('/' + HG_REPO, status=200)
80 app.get('/' + HG_REPO, status=200)
81
81
82 app.controller = controller
82 app.controller = controller
83 return app
83 return app
84
84
85
85
86 def _remove_default_user_from_query_cache():
86 def _remove_default_user_from_query_cache():
87 user = User.get_default_user(cache=True)
87 user = User.get_default_user(cache=True)
88 query = Session().query(User).filter(User.username == user.username)
88 query = Session().query(User).filter(User.username == user.username)
89 query = query.options(FromCache(
89 query = query.options(FromCache(
90 "sql_cache_short", "get_user_%s" % _hash_key(user.username)))
90 "sql_cache_short", "get_user_%s" % _hash_key(user.username)))
91 query.invalidate()
91 query.invalidate()
92 Session().expire(user)
92 Session().expire(user)
93
93
94
94
95 @pytest.fixture
95 @pytest.fixture
96 def disable_anonymous_user(request, pylonsapp):
96 def disable_anonymous_user(request, pylonsapp):
97 set_anonymous_access(False)
97 set_anonymous_access(False)
98
98
99 @request.addfinalizer
99 @request.addfinalizer
100 def cleanup():
100 def cleanup():
101 set_anonymous_access(True)
101 set_anonymous_access(True)
102
102
103
103
104 def test_handles_exceptions_during_permissions_checks(
104 def test_handles_exceptions_during_permissions_checks(
105 vcscontroller, disable_anonymous_user):
105 vcscontroller, disable_anonymous_user):
106 user_and_pass = '%s:%s' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
106 user_and_pass = '%s:%s' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
107 auth_password = base64.encodestring(user_and_pass).strip()
107 auth_password = base64.encodestring(user_and_pass).strip()
108 extra_environ = {
108 extra_environ = {
109 'AUTH_TYPE': 'Basic',
109 'AUTH_TYPE': 'Basic',
110 'HTTP_AUTHORIZATION': 'Basic %s' % auth_password,
110 'HTTP_AUTHORIZATION': 'Basic %s' % auth_password,
111 'REMOTE_USER': TEST_USER_ADMIN_LOGIN,
111 'REMOTE_USER': TEST_USER_ADMIN_LOGIN,
112 }
112 }
113
113
114 # Verify that things are hooked up correctly
114 # Verify that things are hooked up correctly
115 vcscontroller.get('/', status=200, extra_environ=extra_environ)
115 vcscontroller.get('/', status=200, extra_environ=extra_environ)
116
116
117 # Simulate trouble during permission checks
117 # Simulate trouble during permission checks
118 with mock.patch('rhodecode.model.db.User.get_by_username',
118 with mock.patch('rhodecode.model.db.User.get_by_username',
119 side_effect=Exception) as get_user:
119 side_effect=Exception) as get_user:
120 # Verify that a correct 500 is returned and check that the expected
120 # Verify that a correct 500 is returned and check that the expected
121 # code path was hit.
121 # code path was hit.
122 vcscontroller.get('/', status=500, extra_environ=extra_environ)
122 vcscontroller.get('/', status=500, extra_environ=extra_environ)
123 assert get_user.called
123 assert get_user.called
124
124
125
125
126 def test_returns_forbidden_if_no_anonymous_access(
126 def test_returns_forbidden_if_no_anonymous_access(
127 vcscontroller, disable_anonymous_user):
127 vcscontroller, disable_anonymous_user):
128 vcscontroller.get('/', status=401)
128 vcscontroller.get('/', status=401)
129
129
130
130
131 class StubFailVCSController(simplevcs.SimpleVCS):
131 class StubFailVCSController(simplevcs.SimpleVCS):
132 def _handle_request(self, environ, start_response):
132 def _handle_request(self, environ, start_response):
133 raise Exception("BOOM")
133 raise Exception("BOOM")
134
134
135
135
136 @pytest.fixture(scope='module')
136 @pytest.fixture(scope='module')
137 def fail_controller(pylonsapp):
137 def fail_controller(pylonsapp):
138 controller = StubFailVCSController(pylonsapp, pylonsapp.config, None)
138 controller = StubFailVCSController(pylonsapp, pylonsapp.config, None)
139 controller = HttpsFixup(controller, pylonsapp.config)
139 controller = HttpsFixup(controller, pylonsapp.config)
140 controller = webtest.app.TestApp(controller)
140 controller = webtest.app.TestApp(controller)
141 return controller
141 return controller
142
142
143
143
144 def test_handles_exceptions_as_internal_server_error(fail_controller):
144 def test_handles_exceptions_as_internal_server_error(fail_controller):
145 fail_controller.get('/', status=500)
145 fail_controller.get('/', status=500)
146
146
147
147
148 def test_provides_traceback_for_appenlight(fail_controller):
148 def test_provides_traceback_for_appenlight(fail_controller):
149 response = fail_controller.get(
149 response = fail_controller.get(
150 '/', status=500, extra_environ={'appenlight.client': 'fake'})
150 '/', status=500, extra_environ={'appenlight.client': 'fake'})
151 assert 'appenlight.__traceback' in response.request.environ
151 assert 'appenlight.__traceback' in response.request.environ
152
152
153
153
154 def test_provides_utils_scm_app_as_scm_app_by_default(pylonsapp):
154 def test_provides_utils_scm_app_as_scm_app_by_default(pylonsapp):
155 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
155 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
156 assert controller.scm_app is scm_app
156 assert controller.scm_app is scm_app_http
157
157
158
158
159 def test_allows_to_override_scm_app_via_config(pylonsapp):
159 def test_allows_to_override_scm_app_via_config(pylonsapp):
160 config = pylonsapp.config.copy()
160 config = pylonsapp.config.copy()
161 config['vcs.scm_app_implementation'] = (
161 config['vcs.scm_app_implementation'] = (
162 'rhodecode.tests.lib.middleware.mock_scm_app')
162 'rhodecode.tests.lib.middleware.mock_scm_app')
163 controller = StubVCSController(pylonsapp, config, None)
163 controller = StubVCSController(pylonsapp, config, None)
164 assert controller.scm_app is mock_scm_app
164 assert controller.scm_app is mock_scm_app
165
165
166
166
167 @pytest.mark.parametrize('query_string, expected', [
167 @pytest.mark.parametrize('query_string, expected', [
168 ('cmd=stub_command', True),
168 ('cmd=stub_command', True),
169 ('cmd=listkeys', False),
169 ('cmd=listkeys', False),
170 ])
170 ])
171 def test_should_check_locking(query_string, expected):
171 def test_should_check_locking(query_string, expected):
172 result = simplevcs._should_check_locking(query_string)
172 result = simplevcs._should_check_locking(query_string)
173 assert result == expected
173 assert result == expected
174
174
175
175
176 class TestShadowRepoRegularExpression(object):
176 class TestShadowRepoRegularExpression(object):
177 pr_segment = 'pull-request'
177 pr_segment = 'pull-request'
178 shadow_segment = 'repository'
178 shadow_segment = 'repository'
179
179
180 @pytest.mark.parametrize('url, expected', [
180 @pytest.mark.parametrize('url, expected', [
181 # repo with/without groups
181 # repo with/without groups
182 ('My-Repo/{pr_segment}/1/{shadow_segment}', True),
182 ('My-Repo/{pr_segment}/1/{shadow_segment}', True),
183 ('Group/My-Repo/{pr_segment}/2/{shadow_segment}', True),
183 ('Group/My-Repo/{pr_segment}/2/{shadow_segment}', True),
184 ('Group/Sub-Group/My-Repo/{pr_segment}/3/{shadow_segment}', True),
184 ('Group/Sub-Group/My-Repo/{pr_segment}/3/{shadow_segment}', True),
185 ('Group/Sub-Group1/Sub-Group2/My-Repo/{pr_segment}/3/{shadow_segment}', True),
185 ('Group/Sub-Group1/Sub-Group2/My-Repo/{pr_segment}/3/{shadow_segment}', True),
186
186
187 # pull request ID
187 # pull request ID
188 ('MyRepo/{pr_segment}/1/{shadow_segment}', True),
188 ('MyRepo/{pr_segment}/1/{shadow_segment}', True),
189 ('MyRepo/{pr_segment}/1234567890/{shadow_segment}', True),
189 ('MyRepo/{pr_segment}/1234567890/{shadow_segment}', True),
190 ('MyRepo/{pr_segment}/-1/{shadow_segment}', False),
190 ('MyRepo/{pr_segment}/-1/{shadow_segment}', False),
191 ('MyRepo/{pr_segment}/invalid/{shadow_segment}', False),
191 ('MyRepo/{pr_segment}/invalid/{shadow_segment}', False),
192
192
193 # unicode
193 # unicode
194 (u'Sp€çîál-Repö/{pr_segment}/1/{shadow_segment}', True),
194 (u'Sp€çîál-Repö/{pr_segment}/1/{shadow_segment}', True),
195 (u'Sp€çîál-Gröüp/Sp€çîál-Repö/{pr_segment}/1/{shadow_segment}', True),
195 (u'Sp€çîál-Gröüp/Sp€çîál-Repö/{pr_segment}/1/{shadow_segment}', True),
196
196
197 # trailing/leading slash
197 # trailing/leading slash
198 ('/My-Repo/{pr_segment}/1/{shadow_segment}', False),
198 ('/My-Repo/{pr_segment}/1/{shadow_segment}', False),
199 ('My-Repo/{pr_segment}/1/{shadow_segment}/', False),
199 ('My-Repo/{pr_segment}/1/{shadow_segment}/', False),
200 ('/My-Repo/{pr_segment}/1/{shadow_segment}/', False),
200 ('/My-Repo/{pr_segment}/1/{shadow_segment}/', False),
201
201
202 # misc
202 # misc
203 ('My-Repo/{pr_segment}/1/{shadow_segment}/extra', False),
203 ('My-Repo/{pr_segment}/1/{shadow_segment}/extra', False),
204 ('My-Repo/{pr_segment}/1/{shadow_segment}extra', False),
204 ('My-Repo/{pr_segment}/1/{shadow_segment}extra', False),
205 ])
205 ])
206 def test_shadow_repo_regular_expression(self, url, expected):
206 def test_shadow_repo_regular_expression(self, url, expected):
207 from rhodecode.lib.middleware.simplevcs import SimpleVCS
207 from rhodecode.lib.middleware.simplevcs import SimpleVCS
208 url = url.format(
208 url = url.format(
209 pr_segment=self.pr_segment,
209 pr_segment=self.pr_segment,
210 shadow_segment=self.shadow_segment)
210 shadow_segment=self.shadow_segment)
211 match_obj = SimpleVCS.shadow_repo_re.match(url)
211 match_obj = SimpleVCS.shadow_repo_re.match(url)
212 assert (match_obj is not None) == expected
212 assert (match_obj is not None) == expected
213
213
214
214
215 @pytest.mark.backends('git', 'hg')
215 @pytest.mark.backends('git', 'hg')
216 class TestShadowRepoExposure(object):
216 class TestShadowRepoExposure(object):
217
217
218 def test_pull_on_shadow_repo_propagates_to_wsgi_app(self, pylonsapp):
218 def test_pull_on_shadow_repo_propagates_to_wsgi_app(self, pylonsapp):
219 """
219 """
220 Check that a pull action to a shadow repo is propagated to the
220 Check that a pull action to a shadow repo is propagated to the
221 underlying wsgi app.
221 underlying wsgi app.
222 """
222 """
223 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
223 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
224 controller._check_ssl = mock.Mock()
224 controller._check_ssl = mock.Mock()
225 controller.is_shadow_repo = True
225 controller.is_shadow_repo = True
226 controller._action = 'pull'
226 controller._action = 'pull'
227 controller.stub_response_body = 'dummy body value'
227 controller.stub_response_body = 'dummy body value'
228 environ_stub = {
228 environ_stub = {
229 'HTTP_HOST': 'test.example.com',
229 'HTTP_HOST': 'test.example.com',
230 'REQUEST_METHOD': 'GET',
230 'REQUEST_METHOD': 'GET',
231 'wsgi.url_scheme': 'http',
231 'wsgi.url_scheme': 'http',
232 }
232 }
233
233
234 response = controller(environ_stub, mock.Mock())
234 response = controller(environ_stub, mock.Mock())
235 response_body = ''.join(response)
235 response_body = ''.join(response)
236
236
237 # Assert that we got the response from the wsgi app.
237 # Assert that we got the response from the wsgi app.
238 assert response_body == controller.stub_response_body
238 assert response_body == controller.stub_response_body
239
239
240 def test_push_on_shadow_repo_raises(self, pylonsapp):
240 def test_push_on_shadow_repo_raises(self, pylonsapp):
241 """
241 """
242 Check that a push action to a shadow repo is aborted.
242 Check that a push action to a shadow repo is aborted.
243 """
243 """
244 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
244 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
245 controller._check_ssl = mock.Mock()
245 controller._check_ssl = mock.Mock()
246 controller.is_shadow_repo = True
246 controller.is_shadow_repo = True
247 controller._action = 'push'
247 controller._action = 'push'
248 controller.stub_response_body = 'dummy body value'
248 controller.stub_response_body = 'dummy body value'
249 environ_stub = {
249 environ_stub = {
250 'HTTP_HOST': 'test.example.com',
250 'HTTP_HOST': 'test.example.com',
251 'REQUEST_METHOD': 'GET',
251 'REQUEST_METHOD': 'GET',
252 'wsgi.url_scheme': 'http',
252 'wsgi.url_scheme': 'http',
253 }
253 }
254
254
255 response = controller(environ_stub, mock.Mock())
255 response = controller(environ_stub, mock.Mock())
256 response_body = ''.join(response)
256 response_body = ''.join(response)
257
257
258 assert response_body != controller.stub_response_body
258 assert response_body != controller.stub_response_body
259 # Assert that a 406 error is returned.
259 # Assert that a 406 error is returned.
260 assert '406 Not Acceptable' in response_body
260 assert '406 Not Acceptable' in response_body
261
261
262 def test_set_repo_names_no_shadow(self, pylonsapp):
262 def test_set_repo_names_no_shadow(self, pylonsapp):
263 """
263 """
264 Check that the set_repo_names method sets all names to the one returned
264 Check that the set_repo_names method sets all names to the one returned
265 by the _get_repository_name method on a request to a non shadow repo.
265 by the _get_repository_name method on a request to a non shadow repo.
266 """
266 """
267 environ_stub = {}
267 environ_stub = {}
268 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
268 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
269 controller._name = 'RepoGroup/MyRepo'
269 controller._name = 'RepoGroup/MyRepo'
270 controller.set_repo_names(environ_stub)
270 controller.set_repo_names(environ_stub)
271 assert not controller.is_shadow_repo
271 assert not controller.is_shadow_repo
272 assert (controller.url_repo_name ==
272 assert (controller.url_repo_name ==
273 controller.acl_repo_name ==
273 controller.acl_repo_name ==
274 controller.vcs_repo_name ==
274 controller.vcs_repo_name ==
275 controller._get_repository_name(environ_stub))
275 controller._get_repository_name(environ_stub))
276
276
277 def test_set_repo_names_with_shadow(self, pylonsapp, pr_util):
277 def test_set_repo_names_with_shadow(self, pylonsapp, pr_util):
278 """
278 """
279 Check that the set_repo_names method sets correct names on a request
279 Check that the set_repo_names method sets correct names on a request
280 to a shadow repo.
280 to a shadow repo.
281 """
281 """
282 from rhodecode.model.pull_request import PullRequestModel
282 from rhodecode.model.pull_request import PullRequestModel
283
283
284 pull_request = pr_util.create_pull_request()
284 pull_request = pr_util.create_pull_request()
285 shadow_url = '{target}/{pr_segment}/{pr_id}/{shadow_segment}'.format(
285 shadow_url = '{target}/{pr_segment}/{pr_id}/{shadow_segment}'.format(
286 target=pull_request.target_repo.repo_name,
286 target=pull_request.target_repo.repo_name,
287 pr_id=pull_request.pull_request_id,
287 pr_id=pull_request.pull_request_id,
288 pr_segment=TestShadowRepoRegularExpression.pr_segment,
288 pr_segment=TestShadowRepoRegularExpression.pr_segment,
289 shadow_segment=TestShadowRepoRegularExpression.shadow_segment)
289 shadow_segment=TestShadowRepoRegularExpression.shadow_segment)
290 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
290 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
291 controller._name = shadow_url
291 controller._name = shadow_url
292 controller.set_repo_names({})
292 controller.set_repo_names({})
293
293
294 # Get file system path to shadow repo for assertions.
294 # Get file system path to shadow repo for assertions.
295 workspace_id = PullRequestModel()._workspace_id(pull_request)
295 workspace_id = PullRequestModel()._workspace_id(pull_request)
296 target_vcs = pull_request.target_repo.scm_instance()
296 target_vcs = pull_request.target_repo.scm_instance()
297 vcs_repo_name = target_vcs._get_shadow_repository_path(
297 vcs_repo_name = target_vcs._get_shadow_repository_path(
298 workspace_id)
298 workspace_id)
299
299
300 assert controller.vcs_repo_name == vcs_repo_name
300 assert controller.vcs_repo_name == vcs_repo_name
301 assert controller.url_repo_name == shadow_url
301 assert controller.url_repo_name == shadow_url
302 assert controller.acl_repo_name == pull_request.target_repo.repo_name
302 assert controller.acl_repo_name == pull_request.target_repo.repo_name
303 assert controller.is_shadow_repo
303 assert controller.is_shadow_repo
304
304
305 def test_set_repo_names_with_shadow_but_missing_pr(
305 def test_set_repo_names_with_shadow_but_missing_pr(
306 self, pylonsapp, pr_util):
306 self, pylonsapp, pr_util):
307 """
307 """
308 Checks that the set_repo_names method enforces matching target repos
308 Checks that the set_repo_names method enforces matching target repos
309 and pull request IDs.
309 and pull request IDs.
310 """
310 """
311 pull_request = pr_util.create_pull_request()
311 pull_request = pr_util.create_pull_request()
312 shadow_url = '{target}/{pr_segment}/{pr_id}/{shadow_segment}'.format(
312 shadow_url = '{target}/{pr_segment}/{pr_id}/{shadow_segment}'.format(
313 target=pull_request.target_repo.repo_name,
313 target=pull_request.target_repo.repo_name,
314 pr_id=999999999,
314 pr_id=999999999,
315 pr_segment=TestShadowRepoRegularExpression.pr_segment,
315 pr_segment=TestShadowRepoRegularExpression.pr_segment,
316 shadow_segment=TestShadowRepoRegularExpression.shadow_segment)
316 shadow_segment=TestShadowRepoRegularExpression.shadow_segment)
317 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
317 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
318 controller._name = shadow_url
318 controller._name = shadow_url
319 controller.set_repo_names({})
319 controller.set_repo_names({})
320
320
321 assert not controller.is_shadow_repo
321 assert not controller.is_shadow_repo
322 assert (controller.url_repo_name ==
322 assert (controller.url_repo_name ==
323 controller.acl_repo_name ==
323 controller.acl_repo_name ==
324 controller.vcs_repo_name)
324 controller.vcs_repo_name)
325
325
326
326
327 @pytest.mark.usefixtures('db')
327 @pytest.mark.usefixtures('db')
328 @mock.patch.multiple(
328 @mock.patch.multiple(
329 'Pyro4.config', SERVERTYPE='multiplex', POLLTIMEOUT=0.01)
329 'Pyro4.config', SERVERTYPE='multiplex', POLLTIMEOUT=0.01)
330 class TestGenerateVcsResponse:
330 class TestGenerateVcsResponse:
331
331
332 def test_ensures_that_start_response_is_called_early_enough(self):
332 def test_ensures_that_start_response_is_called_early_enough(self):
333 self.call_controller_with_response_body(iter(['a', 'b']))
333 self.call_controller_with_response_body(iter(['a', 'b']))
334 assert self.start_response.called
334 assert self.start_response.called
335
335
336 def test_invalidates_cache_after_body_is_consumed(self):
336 def test_invalidates_cache_after_body_is_consumed(self):
337 result = self.call_controller_with_response_body(iter(['a', 'b']))
337 result = self.call_controller_with_response_body(iter(['a', 'b']))
338 assert not self.was_cache_invalidated()
338 assert not self.was_cache_invalidated()
339 # Consume the result
339 # Consume the result
340 list(result)
340 list(result)
341 assert self.was_cache_invalidated()
341 assert self.was_cache_invalidated()
342
342
343 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
343 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
344 def test_handles_locking_exception(self, http_locked_rc):
344 def test_handles_locking_exception(self, http_locked_rc):
345 result = self.call_controller_with_response_body(
345 result = self.call_controller_with_response_body(
346 self.raise_result_iter(vcs_kind='repo_locked'))
346 self.raise_result_iter(vcs_kind='repo_locked'))
347 assert not http_locked_rc.called
347 assert not http_locked_rc.called
348 # Consume the result
348 # Consume the result
349 list(result)
349 list(result)
350 assert http_locked_rc.called
350 assert http_locked_rc.called
351
351
352 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPRequirementError')
352 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPRequirementError')
353 def test_handles_requirement_exception(self, http_requirement):
353 def test_handles_requirement_exception(self, http_requirement):
354 result = self.call_controller_with_response_body(
354 result = self.call_controller_with_response_body(
355 self.raise_result_iter(vcs_kind='requirement'))
355 self.raise_result_iter(vcs_kind='requirement'))
356 assert not http_requirement.called
356 assert not http_requirement.called
357 # Consume the result
357 # Consume the result
358 list(result)
358 list(result)
359 assert http_requirement.called
359 assert http_requirement.called
360
360
361 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
361 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
362 def test_handles_locking_exception_in_app_call(self, http_locked_rc):
362 def test_handles_locking_exception_in_app_call(self, http_locked_rc):
363 app_factory_patcher = mock.patch.object(
363 app_factory_patcher = mock.patch.object(
364 StubVCSController, '_create_wsgi_app')
364 StubVCSController, '_create_wsgi_app')
365 with app_factory_patcher as app_factory:
365 with app_factory_patcher as app_factory:
366 app_factory().side_effect = self.vcs_exception()
366 app_factory().side_effect = self.vcs_exception()
367 result = self.call_controller_with_response_body(['a'])
367 result = self.call_controller_with_response_body(['a'])
368 list(result)
368 list(result)
369 assert http_locked_rc.called
369 assert http_locked_rc.called
370
370
371 def test_raises_unknown_exceptions(self):
371 def test_raises_unknown_exceptions(self):
372 result = self.call_controller_with_response_body(
372 result = self.call_controller_with_response_body(
373 self.raise_result_iter(vcs_kind='unknown'))
373 self.raise_result_iter(vcs_kind='unknown'))
374 with pytest.raises(Exception):
374 with pytest.raises(Exception):
375 list(result)
375 list(result)
376
376
377 def test_prepare_callback_daemon_is_called(self):
377 def test_prepare_callback_daemon_is_called(self):
378 def side_effect(extras):
378 def side_effect(extras):
379 return DummyHooksCallbackDaemon(), extras
379 return DummyHooksCallbackDaemon(), extras
380
380
381 prepare_patcher = mock.patch.object(
381 prepare_patcher = mock.patch.object(
382 StubVCSController, '_prepare_callback_daemon')
382 StubVCSController, '_prepare_callback_daemon')
383 with prepare_patcher as prepare_mock:
383 with prepare_patcher as prepare_mock:
384 prepare_mock.side_effect = side_effect
384 prepare_mock.side_effect = side_effect
385 self.call_controller_with_response_body(iter(['a', 'b']))
385 self.call_controller_with_response_body(iter(['a', 'b']))
386 assert prepare_mock.called
386 assert prepare_mock.called
387 assert prepare_mock.call_count == 1
387 assert prepare_mock.call_count == 1
388
388
389 def call_controller_with_response_body(self, response_body):
389 def call_controller_with_response_body(self, response_body):
390 settings = {
390 settings = {
391 'base_path': 'fake_base_path',
391 'base_path': 'fake_base_path',
392 'vcs.hooks.protocol': 'http',
392 'vcs.hooks.protocol': 'http',
393 'vcs.hooks.direct_calls': False,
393 'vcs.hooks.direct_calls': False,
394 }
394 }
395 controller = StubVCSController(None, settings, None)
395 controller = StubVCSController(None, settings, None)
396 controller._invalidate_cache = mock.Mock()
396 controller._invalidate_cache = mock.Mock()
397 controller.stub_response_body = response_body
397 controller.stub_response_body = response_body
398 self.start_response = mock.Mock()
398 self.start_response = mock.Mock()
399 result = controller._generate_vcs_response(
399 result = controller._generate_vcs_response(
400 environ={}, start_response=self.start_response,
400 environ={}, start_response=self.start_response,
401 repo_path='fake_repo_path',
401 repo_path='fake_repo_path',
402 extras={}, action='push')
402 extras={}, action='push')
403 self.controller = controller
403 self.controller = controller
404 return result
404 return result
405
405
406 def raise_result_iter(self, vcs_kind='repo_locked'):
406 def raise_result_iter(self, vcs_kind='repo_locked'):
407 """
407 """
408 Simulates an exception due to a vcs raised exception if kind vcs_kind
408 Simulates an exception due to a vcs raised exception if kind vcs_kind
409 """
409 """
410 raise self.vcs_exception(vcs_kind=vcs_kind)
410 raise self.vcs_exception(vcs_kind=vcs_kind)
411 yield "never_reached"
411 yield "never_reached"
412
412
413 def vcs_exception(self, vcs_kind='repo_locked'):
413 def vcs_exception(self, vcs_kind='repo_locked'):
414 locked_exception = Exception('TEST_MESSAGE')
414 locked_exception = Exception('TEST_MESSAGE')
415 locked_exception._vcs_kind = vcs_kind
415 locked_exception._vcs_kind = vcs_kind
416 return locked_exception
416 return locked_exception
417
417
418 def was_cache_invalidated(self):
418 def was_cache_invalidated(self):
419 return self.controller._invalidate_cache.called
419 return self.controller._invalidate_cache.called
420
420
421
421
422 class TestInitializeGenerator:
422 class TestInitializeGenerator:
423
423
424 def test_drains_first_element(self):
424 def test_drains_first_element(self):
425 gen = self.factory(['__init__', 1, 2])
425 gen = self.factory(['__init__', 1, 2])
426 result = list(gen)
426 result = list(gen)
427 assert result == [1, 2]
427 assert result == [1, 2]
428
428
429 @pytest.mark.parametrize('values', [
429 @pytest.mark.parametrize('values', [
430 [],
430 [],
431 [1, 2],
431 [1, 2],
432 ])
432 ])
433 def test_raises_value_error(self, values):
433 def test_raises_value_error(self, values):
434 with pytest.raises(ValueError):
434 with pytest.raises(ValueError):
435 self.factory(values)
435 self.factory(values)
436
436
437 @simplevcs.initialize_generator
437 @simplevcs.initialize_generator
438 def factory(self, iterable):
438 def factory(self, iterable):
439 for elem in iterable:
439 for elem in iterable:
440 yield elem
440 yield elem
441
441
442
442
443 class TestPrepareHooksDaemon(object):
443 class TestPrepareHooksDaemon(object):
444 def test_calls_imported_prepare_callback_daemon(self, app_settings):
444 def test_calls_imported_prepare_callback_daemon(self, app_settings):
445 expected_extras = {'extra1': 'value1'}
445 expected_extras = {'extra1': 'value1'}
446 daemon = DummyHooksCallbackDaemon()
446 daemon = DummyHooksCallbackDaemon()
447
447
448 controller = StubVCSController(None, app_settings, None)
448 controller = StubVCSController(None, app_settings, None)
449 prepare_patcher = mock.patch.object(
449 prepare_patcher = mock.patch.object(
450 simplevcs, 'prepare_callback_daemon',
450 simplevcs, 'prepare_callback_daemon',
451 return_value=(daemon, expected_extras))
451 return_value=(daemon, expected_extras))
452 with prepare_patcher as prepare_mock:
452 with prepare_patcher as prepare_mock:
453 callback_daemon, extras = controller._prepare_callback_daemon(
453 callback_daemon, extras = controller._prepare_callback_daemon(
454 expected_extras.copy())
454 expected_extras.copy())
455 prepare_mock.assert_called_once_with(
455 prepare_mock.assert_called_once_with(
456 expected_extras,
456 expected_extras,
457 protocol=app_settings['vcs.hooks.protocol'],
457 protocol=app_settings['vcs.hooks.protocol'],
458 use_direct_calls=app_settings['vcs.hooks.direct_calls'])
458 use_direct_calls=app_settings['vcs.hooks.direct_calls'])
459
459
460 assert callback_daemon == daemon
460 assert callback_daemon == daemon
461 assert extras == extras
461 assert extras == extras
General Comments 0
You need to be logged in to leave comments. Login now