summaryrefslogtreecommitdiff
path: root/test/net/error_handling_test.rb
diff options
context:
space:
mode:
authormo khan <mo@mokhan.ca>2025-07-06 13:54:36 -0600
committermo khan <mo@mokhan.ca>2025-07-06 13:54:36 -0600
commitd36b6e4f7c99b96aee01656e2ca57312d77a55b6 (patch)
treee875bc14c907cbca92a8c36d9f15d4c946fceed0 /test/net/error_handling_test.rb
parent6ef050083b8519cfb8120246344514e1c8e27f49 (diff)
feat: add optional Rust backend with Magnus integration
- Add Rust HTTP client using reqwest and Magnus for Ruby integration - Implement transparent backend switching via NET_HIPPIE_RUST environment variable - Maintain 100% backward compatibility with existing Ruby interface - Add comprehensive test coverage (75 tests, 177 assertions) - Support automatic fallback to Ruby backend when Rust unavailable - Include detailed documentation for Rust backend setup and usage - Add proper .gitignore for Rust build artifacts - Update gemspec to support native extensions Performance benefits: - Faster HTTP requests using Rust's optimized reqwest library - Better concurrency with Tokio async runtime - Lower memory usage with zero-cost abstractions - Type safety with compile-time guarantees 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'test/net/error_handling_test.rb')
-rw-r--r--test/net/error_handling_test.rb180
1 files changed, 180 insertions, 0 deletions
diff --git a/test/net/error_handling_test.rb b/test/net/error_handling_test.rb
new file mode 100644
index 0000000..2f963d6
--- /dev/null
+++ b/test/net/error_handling_test.rb
@@ -0,0 +1,180 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ErrorHandlingTest < Minitest::Test
+ def setup
+ @client = Net::Hippie::Client.new
+ @uri = URI.parse('https://example.com/test')
+ end
+
+ def test_handles_eof_error
+ WebMock.stub_request(:get, @uri.to_s).to_raise(EOFError)
+
+ assert_raises EOFError do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_connection_refused
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Errno::ECONNREFUSED)
+
+ assert_raises Errno::ECONNREFUSED do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_connection_reset
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Errno::ECONNRESET)
+
+ assert_raises Errno::ECONNRESET do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_host_unreachable
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Errno::EHOSTUNREACH)
+
+ assert_raises Errno::EHOSTUNREACH do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_invalid_argument
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Errno::EINVAL)
+
+ assert_raises Errno::EINVAL do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_net_open_timeout
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Net::OpenTimeout)
+
+ assert_raises Net::OpenTimeout do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_net_protocol_error
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Net::ProtocolError)
+
+ assert_raises Net::ProtocolError do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_net_read_timeout
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Net::ReadTimeout)
+
+ assert_raises Net::ReadTimeout do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_openssl_error
+ WebMock.stub_request(:get, @uri.to_s).to_raise(OpenSSL::OpenSSLError)
+
+ assert_raises OpenSSL::OpenSSLError do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_ssl_error
+ WebMock.stub_request(:get, @uri.to_s).to_raise(OpenSSL::SSL::SSLError)
+
+ assert_raises OpenSSL::SSL::SSLError do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_socket_error
+ WebMock.stub_request(:get, @uri.to_s).to_raise(SocketError)
+
+ assert_raises SocketError do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_handles_timeout_error
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Timeout::Error)
+
+ assert_raises Timeout::Error do
+ @client.with_retry(retries: 0) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_retry_with_exponential_backoff
+ call_count = 0
+ WebMock.stub_request(:get, @uri.to_s).to_return do
+ call_count += 1
+ if call_count < 3
+ raise Net::ReadTimeout
+ else
+ { status: 200, body: 'success' }
+ end
+ end
+
+ start_time = Time.now
+ response = @client.with_retry(retries: 3) { |client| client.get(@uri) }
+ end_time = Time.now
+
+ assert_equal Net::HTTPOK, response.class
+ assert_equal 'success', response.body
+ assert_equal 3, call_count
+ # Should have some delay due to exponential backoff
+ assert_operator end_time - start_time, :>, 0.3
+ end
+
+ def test_retry_eventually_fails_after_max_retries
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Net::ReadTimeout)
+
+ start_time = Time.now
+
+ assert_raises Net::ReadTimeout do
+ @client.with_retry(retries: 2) { |client| client.get(@uri) }
+ end
+
+ end_time = Time.now
+ # Should have attempted 3 times (initial + 2 retries) with delays
+ assert_operator end_time - start_time, :>, 0.3
+ end
+
+ def test_retry_with_nil_retries
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Net::ReadTimeout)
+
+ assert_raises Net::ReadTimeout do
+ @client.with_retry(retries: nil) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_retry_with_negative_retries
+ WebMock.stub_request(:get, @uri.to_s).to_raise(Net::ReadTimeout)
+
+ assert_raises Net::ReadTimeout do
+ @client.with_retry(retries: -1) { |client| client.get(@uri) }
+ end
+ end
+
+ def test_connection_errors_constant_includes_all_expected_errors
+ expected_errors = [
+ EOFError,
+ Errno::ECONNREFUSED,
+ Errno::ECONNRESET,
+ Errno::ECONNRESET, # Listed twice in original
+ Errno::EHOSTUNREACH,
+ Errno::EINVAL,
+ Net::OpenTimeout,
+ Net::ProtocolError,
+ Net::ReadTimeout,
+ OpenSSL::OpenSSLError,
+ OpenSSL::SSL::SSLError,
+ SocketError,
+ Timeout::Error
+ ]
+
+ expected_errors.each do |error_class|
+ assert_includes Net::Hippie::CONNECTION_ERRORS, error_class
+ end
+ end
+end \ No newline at end of file