require 'test_helper' require 'openssl' require 'net/http' class NetworkConnectionRetriesTest < Minitest::Test class MyNewError < StandardError end include NetworkConnectionRetries def setup @logger = stubs(:logger) @requester = stubs(:requester) @ok = stub(:code => 200, :message => 'OK', :body => 'success') end def test_unrecoverable_exception raised = assert_raises(ActiveUtils::ConnectionError) do retry_exceptions do raise EOFError end end assert_equal "The remote server dropped the connection", raised.message end def test_econnreset_raises_correctly raised = assert_raises(ActiveUtils::ConnectionError) do retry_exceptions do raise Errno::ECONNRESET end end assert_equal "The remote server reset the connection", raised.message end def test_timeout_errors_raise_correctly exceptions = [Timeout::Error, Errno::ETIMEDOUT] if RUBY_VERSION >= '2.0.0' exceptions += [Net::ReadTimeout, Net::OpenTimeout] end exceptions.each do |exception| raised = assert_raises(ActiveUtils::ConnectionError) do retry_exceptions do raise exception end end assert_equal "The connection to the remote server timed out", raised.message end end def test_socket_error_raises_correctly raised = assert_raises(ActiveUtils::ConnectionError) do retry_exceptions do raise SocketError end end assert_equal "The connection to the remote server could not be established", raised.message end def test_ssl_errors_raise_correctly exceptions = [OpenSSL::SSL::SSLError] if RUBY_VERSION >= '2.1.0' exceptions += [OpenSSL::SSL::SSLErrorWaitWritable, OpenSSL::SSL::SSLErrorWaitReadable] end exceptions.each do |exception| raised = assert_raises(ActiveUtils::ConnectionError) do retry_exceptions do raise exception end end assert_equal "The SSL connection to the remote server could not be established", raised.message end end def test_invalid_response_error assert_raises(ActiveUtils::InvalidResponseError) do retry_exceptions do raise Zlib::BufError end end end def test_unrecoverable_exception_logged_if_logger_provided @logger.expects(:info).once assert_raises(ActiveUtils::ConnectionError) do retry_exceptions :logger => @logger do raise EOFError end end end def test_failure_then_success_with_recoverable_exception @requester.expects(:post).times(2).raises(Errno::ECONNREFUSED).then.returns(@ok) retry_exceptions do @requester.post end end def test_failure_limit_reached @requester.expects(:post).times(ActiveUtils::NetworkConnectionRetries::DEFAULT_RETRIES).raises(Errno::ECONNREFUSED) assert_raises(ActiveUtils::ConnectionError) do retry_exceptions do @requester.post end end end def test_failure_limit_reached_logs_final_error @logger.expects(:info).times(3) @requester.expects(:post).times(ActiveUtils::NetworkConnectionRetries::DEFAULT_RETRIES).raises(Errno::ECONNREFUSED) assert_raises(ActiveUtils::ConnectionError) do retry_exceptions(:logger => @logger) do @requester.post end end end def test_failure_then_success_with_retry_safe_enabled @requester.expects(:post).times(2).raises(EOFError).then.returns(@ok) retry_exceptions :retry_safe => true do @requester.post end end def test_failure_then_success_logs_success @logger.expects(:info).with(regexp_matches(/dropped/)) @logger.expects(:info).with(regexp_matches(/success/)) @requester.expects(:post).times(2).raises(EOFError).then.returns(@ok) retry_exceptions(:logger => @logger, :retry_safe => true) do @requester.post end end def test_mixture_of_failures_with_retry_safe_enabled @requester.expects(:post).times(3).raises(Errno::ECONNRESET). raises(Errno::ECONNREFUSED). raises(EOFError) assert_raises(ActiveUtils::ConnectionError) do retry_exceptions :retry_safe => true do @requester.post end end end def test_delay_between_retries @requester.expects(:post).times(2).raises(EOFError).then.returns(@ok) Kernel.expects(sleep: 0.5) retry_exceptions retry_safe: true, delay: 0.5 do @requester.post end end def test_no_delay_without_specified_option @requester.expects(:post).times(2).raises(EOFError).then.returns(@ok) Kernel.expects(sleep: 0.5).never retry_exceptions retry_safe: true do @requester.post end end def test_failure_with_ssl_certificate @requester.expects(:post).raises(OpenSSL::X509::CertificateError) assert_raises(ActiveUtils::ClientCertificateError) do retry_exceptions do @requester.post end end end def test_failure_with_ssl_certificate_logs_error_if_logger_specified @logger.expects(:error).once @requester.expects(:post).raises(OpenSSL::X509::CertificateError) assert_raises(ActiveUtils::ClientCertificateError) do retry_exceptions :logger => @logger do @requester.post end end end def test_failure_with_additional_exceptions_specified @requester.expects(:post).raises(MyNewError) assert_raises(ActiveUtils::ConnectionError) do retry_exceptions :connection_exceptions => {MyNewError => "my message"} do @requester.post end end end def test_failure_without_additional_exceptions_specified @requester.expects(:post).raises(MyNewError) assert_raises(MyNewError) do retry_exceptions do @requester.post end end end def test_retries_with_custom_error_specified @requester.expects(:post).times(2).raises(Errno::ETIMEDOUT).then.returns(@ok) retry_exceptions retriable_exceptions: { Errno::ETIMEDOUT => "The connection to the remote server timed out"} do @requester.post end end end