# RhodeCode VCSServer provides access to different vcs backends via network. # Copyright (C) 2014-2024 RhodeCode GmbH # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA import inspect import sys import traceback import pytest from mercurial.error import LookupError from mock import Mock, patch from vcsserver import exceptions, hgcompat from vcsserver.remote import hg_remote class TestDiff: def test_raising_safe_exception_when_lookup_failed(self): factory = Mock() hg_remote_instance = hg_remote.HgRemote(factory) with patch('mercurial.patch.diff') as diff_mock: diff_mock.side_effect = LookupError(b'deadbeef', b'index', b'message') with pytest.raises(Exception) as exc_info: hg_remote_instance.diff( wire={}, commit_id_1='deadbeef', commit_id_2='deadbee1', file_filter=None, opt_git=True, opt_ignorews=True, context=3) assert type(exc_info.value) == Exception assert exc_info.value._vcs_kind == 'lookup' class TestReraiseSafeExceptions: original_traceback = None def test_method_decorated_with_reraise_safe_exceptions(self): factory = Mock() hg_remote_instance = hg_remote.HgRemote(factory) methods = inspect.getmembers(hg_remote_instance, predicate=inspect.ismethod) decorator = hg_remote.reraise_safe_exceptions(None) for method_name, method in methods: if not method_name.startswith('_') and method_name not in ['vcsserver_invalidate_cache']: assert method.__func__.__code__ == decorator.__code__ @pytest.mark.parametrize('side_effect, expected_type', [ (hgcompat.Abort(b'failed-abort'), 'abort'), (hgcompat.InterventionRequired(b'intervention-required'), 'abort'), (hgcompat.RepoLookupError(), 'lookup'), (hgcompat.LookupError(b'deadbeef', b'index', b'message'), 'lookup'), (hgcompat.RepoError(), 'error'), (hgcompat.RequirementError(), 'requirement'), ]) def test_safe_exceptions_reraised(self, side_effect, expected_type): @hg_remote.reraise_safe_exceptions def fake_method(): raise side_effect with pytest.raises(Exception) as exc_info: fake_method() assert type(exc_info.value) == Exception assert exc_info.value._vcs_kind == expected_type def test_keeps_original_traceback(self): @hg_remote.reraise_safe_exceptions def fake_method(): try: raise hgcompat.Abort(b'test-abort') except: self.original_traceback = traceback.format_tb(sys.exc_info()[2]) raise new_traceback = None try: fake_method() except Exception: new_traceback = traceback.format_tb(sys.exc_info()[2]) new_traceback_tail = new_traceback[-len(self.original_traceback):] assert new_traceback_tail == self.original_traceback def test_maps_unknown_exceptions_to_unhandled(self): @hg_remote.reraise_safe_exceptions def stub_method(): raise ValueError('stub') with pytest.raises(Exception) as exc_info: stub_method() assert exc_info.value._vcs_kind == 'unhandled' def test_does_not_map_known_exceptions(self): @hg_remote.reraise_safe_exceptions def stub_method(): raise exceptions.LookupException()('stub') with pytest.raises(Exception) as exc_info: stub_method() assert exc_info.value._vcs_kind == 'lookup'