##// END OF EJS Templates
tests: Use settings dict from fixture instead of grabbing it from the WSGI app stack.
Martin Bornhold -
r603:4f5ebd99 default
parent child Browse files
Show More
@@ -1,305 +1,304 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
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 _get_repository_name(self, environ):
45 def _get_repository_name(self, environ):
46 return HG_REPO
46 return HG_REPO
47
47
48 def _get_action(self, environ):
48 def _get_action(self, environ):
49 return "pull"
49 return "pull"
50
50
51 def _create_wsgi_app(self, repo_path, repo_name, config):
51 def _create_wsgi_app(self, repo_path, repo_name, config):
52 def fake_app(environ, start_response):
52 def fake_app(environ, start_response):
53 start_response('200 OK', [])
53 start_response('200 OK', [])
54 return self.stub_response_body
54 return self.stub_response_body
55 return fake_app
55 return fake_app
56
56
57 def _create_config(self, extras, repo_name):
57 def _create_config(self, extras, repo_name):
58 return None
58 return None
59
59
60
60
61 @pytest.fixture
61 @pytest.fixture
62 def vcscontroller(pylonsapp, config_stub):
62 def vcscontroller(pylonsapp, config_stub):
63 config_stub.testing_securitypolicy()
63 config_stub.testing_securitypolicy()
64 config_stub.include('rhodecode.authentication')
64 config_stub.include('rhodecode.authentication')
65
65
66 set_anonymous_access(True)
66 set_anonymous_access(True)
67 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
67 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
68 app = HttpsFixup(controller, pylonsapp.config)
68 app = HttpsFixup(controller, pylonsapp.config)
69 app = webtest.app.TestApp(app)
69 app = webtest.app.TestApp(app)
70
70
71 _remove_default_user_from_query_cache()
71 _remove_default_user_from_query_cache()
72
72
73 # Sanity checks that things are set up correctly
73 # Sanity checks that things are set up correctly
74 app.get('/' + HG_REPO, status=200)
74 app.get('/' + HG_REPO, status=200)
75
75
76 app.controller = controller
76 app.controller = controller
77 return app
77 return app
78
78
79
79
80 def _remove_default_user_from_query_cache():
80 def _remove_default_user_from_query_cache():
81 user = User.get_default_user(cache=True)
81 user = User.get_default_user(cache=True)
82 query = Session().query(User).filter(User.username == user.username)
82 query = Session().query(User).filter(User.username == user.username)
83 query = query.options(FromCache(
83 query = query.options(FromCache(
84 "sql_cache_short", "get_user_%s" % _hash_key(user.username)))
84 "sql_cache_short", "get_user_%s" % _hash_key(user.username)))
85 query.invalidate()
85 query.invalidate()
86 Session().expire(user)
86 Session().expire(user)
87
87
88
88
89 @pytest.fixture
89 @pytest.fixture
90 def disable_anonymous_user(request, pylonsapp):
90 def disable_anonymous_user(request, pylonsapp):
91 set_anonymous_access(False)
91 set_anonymous_access(False)
92
92
93 @request.addfinalizer
93 @request.addfinalizer
94 def cleanup():
94 def cleanup():
95 set_anonymous_access(True)
95 set_anonymous_access(True)
96
96
97
97
98 def test_handles_exceptions_during_permissions_checks(
98 def test_handles_exceptions_during_permissions_checks(
99 vcscontroller, disable_anonymous_user):
99 vcscontroller, disable_anonymous_user):
100 user_and_pass = '%s:%s' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
100 user_and_pass = '%s:%s' % (TEST_USER_ADMIN_LOGIN, TEST_USER_ADMIN_PASS)
101 auth_password = base64.encodestring(user_and_pass).strip()
101 auth_password = base64.encodestring(user_and_pass).strip()
102 extra_environ = {
102 extra_environ = {
103 'AUTH_TYPE': 'Basic',
103 'AUTH_TYPE': 'Basic',
104 'HTTP_AUTHORIZATION': 'Basic %s' % auth_password,
104 'HTTP_AUTHORIZATION': 'Basic %s' % auth_password,
105 'REMOTE_USER': TEST_USER_ADMIN_LOGIN,
105 'REMOTE_USER': TEST_USER_ADMIN_LOGIN,
106 }
106 }
107
107
108 # Verify that things are hooked up correctly
108 # Verify that things are hooked up correctly
109 vcscontroller.get('/', status=200, extra_environ=extra_environ)
109 vcscontroller.get('/', status=200, extra_environ=extra_environ)
110
110
111 # Simulate trouble during permission checks
111 # Simulate trouble during permission checks
112 with mock.patch('rhodecode.model.db.User.get_by_username',
112 with mock.patch('rhodecode.model.db.User.get_by_username',
113 side_effect=Exception) as get_user:
113 side_effect=Exception) as get_user:
114 # Verify that a correct 500 is returned and check that the expected
114 # Verify that a correct 500 is returned and check that the expected
115 # code path was hit.
115 # code path was hit.
116 vcscontroller.get('/', status=500, extra_environ=extra_environ)
116 vcscontroller.get('/', status=500, extra_environ=extra_environ)
117 assert get_user.called
117 assert get_user.called
118
118
119
119
120 def test_returns_forbidden_if_no_anonymous_access(
120 def test_returns_forbidden_if_no_anonymous_access(
121 vcscontroller, disable_anonymous_user):
121 vcscontroller, disable_anonymous_user):
122 vcscontroller.get('/', status=401)
122 vcscontroller.get('/', status=401)
123
123
124
124
125 class StubFailVCSController(simplevcs.SimpleVCS):
125 class StubFailVCSController(simplevcs.SimpleVCS):
126 def _handle_request(self, environ, start_response):
126 def _handle_request(self, environ, start_response):
127 raise Exception("BOOM")
127 raise Exception("BOOM")
128
128
129
129
130 @pytest.fixture(scope='module')
130 @pytest.fixture(scope='module')
131 def fail_controller(pylonsapp):
131 def fail_controller(pylonsapp):
132 controller = StubFailVCSController(pylonsapp, pylonsapp.config, None)
132 controller = StubFailVCSController(pylonsapp, pylonsapp.config, None)
133 controller = HttpsFixup(controller, pylonsapp.config)
133 controller = HttpsFixup(controller, pylonsapp.config)
134 controller = webtest.app.TestApp(controller)
134 controller = webtest.app.TestApp(controller)
135 return controller
135 return controller
136
136
137
137
138 def test_handles_exceptions_as_internal_server_error(fail_controller):
138 def test_handles_exceptions_as_internal_server_error(fail_controller):
139 fail_controller.get('/', status=500)
139 fail_controller.get('/', status=500)
140
140
141
141
142 def test_provides_traceback_for_appenlight(fail_controller):
142 def test_provides_traceback_for_appenlight(fail_controller):
143 response = fail_controller.get(
143 response = fail_controller.get(
144 '/', status=500, extra_environ={'appenlight.client': 'fake'})
144 '/', status=500, extra_environ={'appenlight.client': 'fake'})
145 assert 'appenlight.__traceback' in response.request.environ
145 assert 'appenlight.__traceback' in response.request.environ
146
146
147
147
148 def test_provides_utils_scm_app_as_scm_app_by_default(pylonsapp):
148 def test_provides_utils_scm_app_as_scm_app_by_default(pylonsapp):
149 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
149 controller = StubVCSController(pylonsapp, pylonsapp.config, None)
150 assert controller.scm_app is scm_app
150 assert controller.scm_app is scm_app
151
151
152
152
153 def test_allows_to_override_scm_app_via_config(pylonsapp):
153 def test_allows_to_override_scm_app_via_config(pylonsapp):
154 config = pylonsapp.config.copy()
154 config = pylonsapp.config.copy()
155 config['vcs.scm_app_implementation'] = (
155 config['vcs.scm_app_implementation'] = (
156 'rhodecode.tests.lib.middleware.mock_scm_app')
156 'rhodecode.tests.lib.middleware.mock_scm_app')
157 controller = StubVCSController(pylonsapp, config, None)
157 controller = StubVCSController(pylonsapp, config, None)
158 assert controller.scm_app is mock_scm_app
158 assert controller.scm_app is mock_scm_app
159
159
160
160
161 @pytest.mark.parametrize('query_string, expected', [
161 @pytest.mark.parametrize('query_string, expected', [
162 ('cmd=stub_command', True),
162 ('cmd=stub_command', True),
163 ('cmd=listkeys', False),
163 ('cmd=listkeys', False),
164 ])
164 ])
165 def test_should_check_locking(query_string, expected):
165 def test_should_check_locking(query_string, expected):
166 result = simplevcs._should_check_locking(query_string)
166 result = simplevcs._should_check_locking(query_string)
167 assert result == expected
167 assert result == expected
168
168
169
169
170 @mock.patch.multiple(
170 @mock.patch.multiple(
171 'Pyro4.config', SERVERTYPE='multiplex', POLLTIMEOUT=0.01)
171 'Pyro4.config', SERVERTYPE='multiplex', POLLTIMEOUT=0.01)
172 class TestGenerateVcsResponse:
172 class TestGenerateVcsResponse:
173
173
174 def test_ensures_that_start_response_is_called_early_enough(self):
174 def test_ensures_that_start_response_is_called_early_enough(self):
175 self.call_controller_with_response_body(iter(['a', 'b']))
175 self.call_controller_with_response_body(iter(['a', 'b']))
176 assert self.start_response.called
176 assert self.start_response.called
177
177
178 def test_invalidates_cache_after_body_is_consumed(self):
178 def test_invalidates_cache_after_body_is_consumed(self):
179 result = self.call_controller_with_response_body(iter(['a', 'b']))
179 result = self.call_controller_with_response_body(iter(['a', 'b']))
180 assert not self.was_cache_invalidated()
180 assert not self.was_cache_invalidated()
181 # Consume the result
181 # Consume the result
182 list(result)
182 list(result)
183 assert self.was_cache_invalidated()
183 assert self.was_cache_invalidated()
184
184
185 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
185 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
186 def test_handles_locking_exception(self, http_locked_rc):
186 def test_handles_locking_exception(self, http_locked_rc):
187 result = self.call_controller_with_response_body(
187 result = self.call_controller_with_response_body(
188 self.raise_result_iter(vcs_kind='repo_locked'))
188 self.raise_result_iter(vcs_kind='repo_locked'))
189 assert not http_locked_rc.called
189 assert not http_locked_rc.called
190 # Consume the result
190 # Consume the result
191 list(result)
191 list(result)
192 assert http_locked_rc.called
192 assert http_locked_rc.called
193
193
194 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPRequirementError')
194 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPRequirementError')
195 def test_handles_requirement_exception(self, http_requirement):
195 def test_handles_requirement_exception(self, http_requirement):
196 result = self.call_controller_with_response_body(
196 result = self.call_controller_with_response_body(
197 self.raise_result_iter(vcs_kind='requirement'))
197 self.raise_result_iter(vcs_kind='requirement'))
198 assert not http_requirement.called
198 assert not http_requirement.called
199 # Consume the result
199 # Consume the result
200 list(result)
200 list(result)
201 assert http_requirement.called
201 assert http_requirement.called
202
202
203 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
203 @mock.patch('rhodecode.lib.middleware.simplevcs.HTTPLockedRC')
204 def test_handles_locking_exception_in_app_call(self, http_locked_rc):
204 def test_handles_locking_exception_in_app_call(self, http_locked_rc):
205 app_factory_patcher = mock.patch.object(
205 app_factory_patcher = mock.patch.object(
206 StubVCSController, '_create_wsgi_app')
206 StubVCSController, '_create_wsgi_app')
207 with app_factory_patcher as app_factory:
207 with app_factory_patcher as app_factory:
208 app_factory().side_effect = self.vcs_exception()
208 app_factory().side_effect = self.vcs_exception()
209 result = self.call_controller_with_response_body(['a'])
209 result = self.call_controller_with_response_body(['a'])
210 list(result)
210 list(result)
211 assert http_locked_rc.called
211 assert http_locked_rc.called
212
212
213 def test_raises_unknown_exceptions(self):
213 def test_raises_unknown_exceptions(self):
214 result = self.call_controller_with_response_body(
214 result = self.call_controller_with_response_body(
215 self.raise_result_iter(vcs_kind='unknown'))
215 self.raise_result_iter(vcs_kind='unknown'))
216 with pytest.raises(Exception):
216 with pytest.raises(Exception):
217 list(result)
217 list(result)
218
218
219 def test_prepare_callback_daemon_is_called(self):
219 def test_prepare_callback_daemon_is_called(self):
220 def side_effect(extras):
220 def side_effect(extras):
221 return DummyHooksCallbackDaemon(), extras
221 return DummyHooksCallbackDaemon(), extras
222
222
223 prepare_patcher = mock.patch.object(
223 prepare_patcher = mock.patch.object(
224 StubVCSController, '_prepare_callback_daemon')
224 StubVCSController, '_prepare_callback_daemon')
225 with prepare_patcher as prepare_mock:
225 with prepare_patcher as prepare_mock:
226 prepare_mock.side_effect = side_effect
226 prepare_mock.side_effect = side_effect
227 self.call_controller_with_response_body(iter(['a', 'b']))
227 self.call_controller_with_response_body(iter(['a', 'b']))
228 assert prepare_mock.called
228 assert prepare_mock.called
229 assert prepare_mock.call_count == 1
229 assert prepare_mock.call_count == 1
230
230
231 def call_controller_with_response_body(self, response_body):
231 def call_controller_with_response_body(self, response_body):
232 settings = {
232 settings = {
233 'base_path': 'fake_base_path',
233 'base_path': 'fake_base_path',
234 'vcs.hooks.protocol': 'http',
234 'vcs.hooks.protocol': 'http',
235 'vcs.hooks.direct_calls': False,
235 'vcs.hooks.direct_calls': False,
236 }
236 }
237 controller = StubVCSController(None, settings, None)
237 controller = StubVCSController(None, settings, None)
238 controller._invalidate_cache = mock.Mock()
238 controller._invalidate_cache = mock.Mock()
239 controller.stub_response_body = response_body
239 controller.stub_response_body = response_body
240 self.start_response = mock.Mock()
240 self.start_response = mock.Mock()
241 result = controller._generate_vcs_response(
241 result = controller._generate_vcs_response(
242 environ={}, start_response=self.start_response,
242 environ={}, start_response=self.start_response,
243 repo_path='fake_repo_path',
243 repo_path='fake_repo_path',
244 repo_name='fake_repo_name',
244 repo_name='fake_repo_name',
245 extras={}, action='push')
245 extras={}, action='push')
246 self.controller = controller
246 self.controller = controller
247 return result
247 return result
248
248
249 def raise_result_iter(self, vcs_kind='repo_locked'):
249 def raise_result_iter(self, vcs_kind='repo_locked'):
250 """
250 """
251 Simulates an exception due to a vcs raised exception if kind vcs_kind
251 Simulates an exception due to a vcs raised exception if kind vcs_kind
252 """
252 """
253 raise self.vcs_exception(vcs_kind=vcs_kind)
253 raise self.vcs_exception(vcs_kind=vcs_kind)
254 yield "never_reached"
254 yield "never_reached"
255
255
256 def vcs_exception(self, vcs_kind='repo_locked'):
256 def vcs_exception(self, vcs_kind='repo_locked'):
257 locked_exception = Exception('TEST_MESSAGE')
257 locked_exception = Exception('TEST_MESSAGE')
258 locked_exception._vcs_kind = vcs_kind
258 locked_exception._vcs_kind = vcs_kind
259 return locked_exception
259 return locked_exception
260
260
261 def was_cache_invalidated(self):
261 def was_cache_invalidated(self):
262 return self.controller._invalidate_cache.called
262 return self.controller._invalidate_cache.called
263
263
264
264
265 class TestInitializeGenerator:
265 class TestInitializeGenerator:
266
266
267 def test_drains_first_element(self):
267 def test_drains_first_element(self):
268 gen = self.factory(['__init__', 1, 2])
268 gen = self.factory(['__init__', 1, 2])
269 result = list(gen)
269 result = list(gen)
270 assert result == [1, 2]
270 assert result == [1, 2]
271
271
272 @pytest.mark.parametrize('values', [
272 @pytest.mark.parametrize('values', [
273 [],
273 [],
274 [1, 2],
274 [1, 2],
275 ])
275 ])
276 def test_raises_value_error(self, values):
276 def test_raises_value_error(self, values):
277 with pytest.raises(ValueError):
277 with pytest.raises(ValueError):
278 self.factory(values)
278 self.factory(values)
279
279
280 @simplevcs.initialize_generator
280 @simplevcs.initialize_generator
281 def factory(self, iterable):
281 def factory(self, iterable):
282 for elem in iterable:
282 for elem in iterable:
283 yield elem
283 yield elem
284
284
285
285
286 class TestPrepareHooksDaemon(object):
286 class TestPrepareHooksDaemon(object):
287 def test_calls_imported_prepare_callback_daemon(self, pylonsapp):
287 def test_calls_imported_prepare_callback_daemon(self, app_settings):
288 settings = pylonsapp.application.config
289 expected_extras = {'extra1': 'value1'}
288 expected_extras = {'extra1': 'value1'}
290 daemon = DummyHooksCallbackDaemon()
289 daemon = DummyHooksCallbackDaemon()
291
290
292 controller = StubVCSController(None, settings, None)
291 controller = StubVCSController(None, app_settings, None)
293 prepare_patcher = mock.patch.object(
292 prepare_patcher = mock.patch.object(
294 simplevcs, 'prepare_callback_daemon',
293 simplevcs, 'prepare_callback_daemon',
295 return_value=(daemon, expected_extras))
294 return_value=(daemon, expected_extras))
296 with prepare_patcher as prepare_mock:
295 with prepare_patcher as prepare_mock:
297 callback_daemon, extras = controller._prepare_callback_daemon(
296 callback_daemon, extras = controller._prepare_callback_daemon(
298 expected_extras.copy())
297 expected_extras.copy())
299 prepare_mock.assert_called_once_with(
298 prepare_mock.assert_called_once_with(
300 expected_extras,
299 expected_extras,
301 protocol=settings['vcs.hooks.protocol'],
300 protocol=app_settings['vcs.hooks.protocol'],
302 use_direct_calls=settings['vcs.hooks.direct_calls'])
301 use_direct_calls=app_settings['vcs.hooks.direct_calls'])
303
302
304 assert callback_daemon == daemon
303 assert callback_daemon == daemon
305 assert extras == extras
304 assert extras == extras
General Comments 0
You need to be logged in to leave comments. Login now