From 64fbd7301caf8df6162a0f4f13c83b204300278c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0t=C4=9Bp=C3=A1n=20Tomsa?= Date: Wed, 14 Aug 2024 13:10:53 +0200 Subject: [PATCH] Catch Timeout on test connection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The --test-connection command only used to catch ConnectionError, including ConnectionTimeout, but not other timeouts such as ReadTimeoutError. Re-used the existing REQUEST_FAILED_EXCEPTIONS list to process all timeouts in the same way. * Card ID: CCT-506 Signed-off-by: Štěpán Tomsa --- insights/client/connection.py | 2 +- .../client/connection/test_test_connection.py | 158 ++++++++++++++++++ 2 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 insights/tests/client/connection/test_test_connection.py diff --git a/insights/client/connection.py b/insights/client/connection.py index eaffa2112d..0a77e5d224 100644 --- a/insights/client/connection.py +++ b/insights/client/connection.py @@ -413,7 +413,7 @@ def test_connection(self, rc=0): logger.info("Connectivity tests completed with some errors") print("See %s for more details." % self.config.logging_file) rc = 1 - except requests.ConnectionError as exc: + except REQUEST_FAILED_EXCEPTIONS as exc: print(exc) logger.error('Connectivity test failed! ' 'Please check your network configuration') diff --git a/insights/tests/client/connection/test_test_connection.py b/insights/tests/client/connection/test_test_connection.py new file mode 100644 index 0000000000..09d67e7a51 --- /dev/null +++ b/insights/tests/client/connection/test_test_connection.py @@ -0,0 +1,158 @@ +import pytest +import requests +from mock import mock + +from insights.client.config import InsightsConfig +from insights.client.connection import InsightsConnection + +LOGGING_FILE = "logging-file.log" + +EXPECTED_EXCEPTIONS = [ + requests.exceptions.SSLError, + requests.exceptions.ConnectTimeout, + requests.exceptions.ReadTimeout, +] +parametrize_exceptions = pytest.mark.parametrize( + ["exception"], + [(exception,) for exception in EXPECTED_EXCEPTIONS], +) + + +class UnexpectedException(Exception): + pass + + +@pytest.fixture +@mock.patch("insights.client.connection.InsightsConnection._init_session") +@mock.patch("insights.client.connection.InsightsConnection.get_proxies") +def insights_connection(get_proxies, init_session): + config = InsightsConfig(base_url="www.example.com", logging_file=LOGGING_FILE) + + connection = InsightsConnection(config) + connection.proxies = None + + return connection + + +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_test_urls_no_catch(test_urls, insights_connection): + """The connection test is stopped in case of an unknown exception.""" + test_urls.side_effect = UnexpectedException + + try: + insights_connection.test_connection() + except UnexpectedException: + pass + + test_urls.assert_called_once() + + +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_test_urls_success(test_urls, insights_connection): + """The connection test performs several API calls in case of no error.""" + insights_connection.test_connection() + assert len(test_urls.mock_calls) > 1 + + +@parametrize_exceptions +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_test_urls_fail(test_urls, exception, insights_connection): + """The connection test is stopped after the first API call failure.""" + test_urls.side_effect = exception + + insights_connection.test_connection() + test_urls.assert_called_once() + + +@mock.patch("insights.client.connection.logger") +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_error_log_no_catch(test_urls, logger, insights_connection): + """The connection test doesn't log any ERROR in case of an unknown exception.""" + test_urls.side_effect = UnexpectedException + + try: + insights_connection.test_connection() + except UnexpectedException: + pass + + logger.error.assert_not_called() + + +@mock.patch("insights.client.connection.logger") +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_error_log_success(test_urls, logger, insights_connection): + """The connection test doesn't log any ERROR if all API calls succeed.""" + insights_connection.test_connection() + + logger.error.assert_not_called() + + +@parametrize_exceptions +@mock.patch("insights.client.connection.logger") +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_error_log_fail( + test_urls, logger, exception, insights_connection +): + """An error message is logged on the ERROR level.""" + test_urls.side_effect = exception + + insights_connection.test_connection() + + logger.error.assert_called_once_with( + "Connectivity test failed! Please check your network configuration" + ) + + +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_print_no_catch(test_urls, insights_connection, capsys): + """The connection test doesn't print anything in case of an unknown exception.""" + test_urls.side_effect = UnexpectedException + + try: + insights_connection.test_connection() + except UnexpectedException: + pass + + out, err = capsys.readouterr() + assert not out + assert not err + + +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_print_success(test_urls, insights_connection, capsys): + """The connection test prints a message pointing to a log file if all API calls succeed.""" + insights_connection.test_connection() + + out, err = capsys.readouterr() + assert out == "See {0} for more details.\n".format( + LOGGING_FILE, + ) + assert not err + + +@parametrize_exceptions +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_print_fail(test_urls, exception, insights_connection, capsys): + """The connection test prints a message pointing to a log file if an API call fails.""" + message = "request exception" + test_urls.side_effect = exception(message) + + insights_connection.test_connection() + out, err = capsys.readouterr() + assert out == "{0}\nAdditional information may be in {1}\n".format( + message, + LOGGING_FILE, + ) + assert not err + + +@mock.patch("insights.client.connection.InsightsConnection._test_urls") +def test_test_connection_no_catch(test_urls, insights_connection): + """The connection test doesn't catch and recreate an unknown exception.""" + expected = UnexpectedException() + test_urls.side_effect = expected + + with pytest.raises(UnexpectedException) as caught: + insights_connection.test_connection() + + assert caught.value is expected