##// END OF EJS Templates
tests: Add a way to deactivate exception re-raising in tests.
Martin Bornhold -
r947:eb8b2333 default
parent child Browse files
Show More
@@ -1,104 +1,106 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
3 # Copyright (C) 2016-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
21
22 import logging
22 import logging
23
24 from pyramid import httpexceptions
23 from pyramid import httpexceptions
25 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
24 from pyramid.httpexceptions import HTTPError, HTTPInternalServerError
26 from pyramid.threadlocal import get_current_request
25 from pyramid.threadlocal import get_current_request
27
26
28 from rhodecode.lib.exceptions import VCSServerUnavailable
27 from rhodecode.lib.exceptions import VCSServerUnavailable
29 from rhodecode.lib.vcs.exceptions import VCSCommunicationError
28 from rhodecode.lib.vcs.exceptions import VCSCommunicationError
30
29
31
30
32 log = logging.getLogger(__name__)
31 log = logging.getLogger(__name__)
33
32
34
33
35 class PylonsErrorHandlingMiddleware(object):
34 class PylonsErrorHandlingMiddleware(object):
36 """
35 """
37 This middleware is wrapped around the old pylons application to catch
36 This middleware is wrapped around the old pylons application to catch
38 errors and invoke our error handling view to return a proper error page.
37 errors and invoke our error handling view to return a proper error page.
39 """
38 """
40 def __init__(self, app, error_view, reraise=False):
39 def __init__(self, app, error_view, reraise=False):
41 self.app = app
40 self.app = app
42 self.error_view = error_view
41 self.error_view = error_view
43 self.reraise = reraise
42 self._reraise = reraise
44
43
45 def __call__(self, environ, start_response):
44 def __call__(self, environ, start_response):
46 # We need to use the pyramid request here instead of creating a custom
45 # We need to use the pyramid request here instead of creating a custom
47 # instance from the environ because this request maybe passed to the
46 # instance from the environ because this request maybe passed to the
48 # error handler view which is a pyramid view and expects a pyramid
47 # error handler view which is a pyramid view and expects a pyramid
49 # request which has been processed by the pyramid router.
48 # request which has been processed by the pyramid router.
50 request = get_current_request()
49 request = get_current_request()
51
50
52 response = self.handle_request(request)
51 response = self.handle_request(request)
53 return response(environ, start_response)
52 return response(environ, start_response)
54
53
55 def is_vcs_response(self, response):
54 def is_vcs_response(self, response):
56 return 'X-RhodeCode-Backend' in response.headers
55 return 'X-RhodeCode-Backend' in response.headers
57
56
58 def is_http_error(self, response):
57 def is_http_error(self, response):
59 # webob type error responses
58 # webob type error responses
60 return (400 <= response.status_int <= 599)
59 return (400 <= response.status_int <= 599)
61
60
62 def is_error_handling_needed(self, response):
61 def is_error_handling_needed(self, response):
63 return (self.is_http_error(response) and not
62 return (self.is_http_error(response) and not
64 self.is_vcs_response(response))
63 self.is_vcs_response(response))
65
64
65 def reraise(self):
66 return self._reraise
67
66 def handle_request(self, request):
68 def handle_request(self, request):
67 """
69 """
68 Calls the underlying WSGI app (typically the old RhodeCode pylons app)
70 Calls the underlying WSGI app (typically the old RhodeCode pylons app)
69 and returns the response if no error happened. In case of an error it
71 and returns the response if no error happened. In case of an error it
70 invokes the error handling view to return a proper error page as
72 invokes the error handling view to return a proper error page as
71 response.
73 response.
72
74
73 - old webob type exceptions get converted to pyramid exceptions
75 - old webob type exceptions get converted to pyramid exceptions
74 - pyramid exceptions are passed to the error handler view
76 - pyramid exceptions are passed to the error handler view
75 """
77 """
76 try:
78 try:
77 response = request.get_response(self.app)
79 response = request.get_response(self.app)
78 if self.is_error_handling_needed(response):
80 if self.is_error_handling_needed(response):
79 response = webob_to_pyramid_http_response(response)
81 response = webob_to_pyramid_http_response(response)
80 return self.error_view(response, request)
82 return self.error_view(response, request)
81 except HTTPError as e: # pyramid type exceptions
83 except HTTPError as e: # pyramid type exceptions
82 return self.error_view(e, request)
84 return self.error_view(e, request)
83 except Exception as e:
85 except Exception as e:
84 log.exception(e)
86 log.exception(e)
85
87
86 if self.reraise:
88 if self.reraise():
87 raise
89 raise
88
90
89 if isinstance(e, VCSCommunicationError):
91 if isinstance(e, VCSCommunicationError):
90 return self.error_view(VCSServerUnavailable(), request)
92 return self.error_view(VCSServerUnavailable(), request)
91
93
92 return self.error_view(HTTPInternalServerError(), request)
94 return self.error_view(HTTPInternalServerError(), request)
93
95
94 return response
96 return response
95
97
96
98
97 def webob_to_pyramid_http_response(webob_response):
99 def webob_to_pyramid_http_response(webob_response):
98 ResponseClass = httpexceptions.status_map[webob_response.status_int]
100 ResponseClass = httpexceptions.status_map[webob_response.status_int]
99 pyramid_response = ResponseClass(webob_response.status)
101 pyramid_response = ResponseClass(webob_response.status)
100 pyramid_response.status = webob_response.status
102 pyramid_response.status = webob_response.status
101 pyramid_response.headers.update(webob_response.headers)
103 pyramid_response.headers.update(webob_response.headers)
102 if pyramid_response.headers['content-type'] == 'text/html':
104 if pyramid_response.headers['content-type'] == 'text/html':
103 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
105 pyramid_response.headers['content-type'] = 'text/html; charset=UTF-8'
104 return pyramid_response
106 return pyramid_response
@@ -1,47 +1,50 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 mock
21 import mock
22 import pytest
22 import pytest
23 import rhodecode
24 import rhodecode.lib.vcs.client as client
23 import rhodecode.lib.vcs.client as client
24 from rhodecode.lib.middleware.error_handling import (
25 PylonsErrorHandlingMiddleware)
26
25
27
26 @pytest.mark.usefixtures('autologin_user', 'app')
28 @pytest.mark.usefixtures('autologin_user', 'app')
27 def test_vcs_available_returns_summary_page(app, backend):
29 def test_vcs_available_returns_summary_page(app, backend):
28 url = '/{repo_name}'.format(repo_name=backend.repo.repo_name)
30 url = '/{repo_name}'.format(repo_name=backend.repo.repo_name)
29 response = app.get(url)
31 response = app.get(url)
30 assert response.status_code == 200
32 assert response.status_code == 200
31 assert 'Summary' in response.body
33 assert 'Summary' in response.body
32
34
33
35
34 @pytest.mark.usefixtures('autologin_user', 'app')
36 @pytest.mark.usefixtures('autologin_user', 'app')
35 def test_vcs_unavailable_returns_vcs_error_page(app, backend):
37 def test_vcs_unavailable_returns_vcs_error_page(app, backend):
36 url = '/{repo_name}'.format(repo_name=backend.repo.repo_name)
38 url = '/{repo_name}'.format(repo_name=backend.repo.repo_name)
37
39
38 try:
40 # Path the get proxy method to raise an exception instead of making a RPC
39 rhodecode.disable_error_handler = False
41 # call to the vcsserver.
40 with mock.patch.object(client, '_get_proxy_method') as p:
42 with mock.patch.object(client, '_get_proxy_method') as p:
41 p.side_effect = client.exceptions.PyroVCSCommunicationError()
43 p.side_effect = client.exceptions.PyroVCSCommunicationError()
44 # Patch pylons error handling middleware to not re-raise exceptions.
45 with mock.patch.object(PylonsErrorHandlingMiddleware, 'reraise') as r:
46 r.return_value = False
42 response = app.get(url, expect_errors=True)
47 response = app.get(url, expect_errors=True)
43 finally:
44 rhodecode.disable_error_handler = True
45
48
46 assert response.status_code == 502
49 assert response.status_code == 502
47 assert 'Could not connect to VCS Server' in response.body
50 assert 'Could not connect to VCS Server' in response.body
General Comments 0
You need to be logged in to leave comments. Login now