summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/net/connection_test.rb108
-rw-r--r--test/net/content_type_mapper_test.rb102
-rw-r--r--test/net/error_handling_test.rb180
-rw-r--r--test/net/timeout_test.rb127
4 files changed, 517 insertions, 0 deletions
diff --git a/test/net/connection_test.rb b/test/net/connection_test.rb
new file mode 100644
index 0000000..380f680
--- /dev/null
+++ b/test/net/connection_test.rb
@@ -0,0 +1,108 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class ConnectionTest < Minitest::Test
+ def test_initialize_with_http_scheme
+ connection = Net::Hippie::Connection.new('http', 'example.com', 80)
+ backend = connection.instance_variable_get(:@backend)
+ refute backend.instance_variable_get(:@http).use_ssl?
+ end
+
+ def test_initialize_with_https_scheme
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443)
+ backend = connection.instance_variable_get(:@backend)
+ assert backend.instance_variable_get(:@http).use_ssl?
+ end
+
+ def test_initialize_with_custom_timeouts
+ options = { read_timeout: 30, open_timeout: 15 }
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443, options)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal 30, http.read_timeout
+ assert_equal 15, http.open_timeout
+ end
+
+ def test_initialize_with_custom_verify_mode
+ options = { verify_mode: OpenSSL::SSL::VERIFY_NONE }
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443, options)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal OpenSSL::SSL::VERIFY_NONE, http.verify_mode
+ end
+
+ def test_initialize_with_client_certificate
+ private_key = OpenSSL::PKey::RSA.new(2048)
+ certificate = OpenSSL::X509::Certificate.new
+ certificate.not_after = certificate.not_before = Time.now
+ certificate.public_key = private_key.public_key
+ certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+
+ options = {
+ certificate: certificate.to_pem,
+ key: private_key.export
+ }
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443, options)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal certificate.to_pem, http.cert.to_pem
+ assert_equal private_key.export, http.key.export
+ end
+
+ def test_initialize_with_client_certificate_and_passphrase
+ private_key = OpenSSL::PKey::RSA.new(2048)
+ passphrase = 'test_passphrase'
+ certificate = OpenSSL::X509::Certificate.new
+ certificate.not_after = certificate.not_before = Time.now
+ certificate.public_key = private_key.public_key
+ certificate.sign(private_key, OpenSSL::Digest::SHA256.new)
+
+ options = {
+ certificate: certificate.to_pem,
+ key: private_key.export(OpenSSL::Cipher.new('AES-256-CBC'), passphrase),
+ passphrase: passphrase
+ }
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443, options)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal certificate.to_pem, http.cert.to_pem
+ assert_equal private_key.export, http.key.export
+ end
+
+ def test_run_executes_request
+ WebMock.stub_request(:get, 'https://example.com/test')
+ .to_return(status: 200, body: 'success')
+
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443)
+ request = Net::HTTP::Get.new('/test')
+ response = connection.run(request)
+
+ assert_equal Net::HTTPOK, response.class
+ assert_equal 'success', response.body
+ end
+
+ def test_build_url_for_absolute_path
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443)
+ url = connection.build_url_for('https://other.com/path')
+ assert_equal 'https://other.com/path', url
+ end
+
+ def test_build_url_for_relative_path_https
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443)
+ url = connection.build_url_for('/api/v1/users')
+ assert_equal 'https://example.com/api/v1/users', url
+ end
+
+ def test_build_url_for_relative_path_http
+ connection = Net::Hippie::Connection.new('http', 'example.com', 80)
+ url = connection.build_url_for('/api/v1/users')
+ assert_equal 'http://example.com/api/v1/users', url
+ end
+
+ def test_build_url_for_http_url
+ connection = Net::Hippie::Connection.new('https', 'example.com', 443)
+ url = connection.build_url_for('http://other.com/path')
+ assert_equal 'http://other.com/path', url
+ end
+end \ No newline at end of file
diff --git a/test/net/content_type_mapper_test.rb b/test/net/content_type_mapper_test.rb
index 0d8ed64..3802362 100644
--- a/test/net/content_type_mapper_test.rb
+++ b/test/net/content_type_mapper_test.rb
@@ -24,4 +24,106 @@ class ContentTypeMapperTest < Minitest::Test
result = subject.map_from(headers, body)
assert_equal body, result
end
+
+ def test_returns_string_body_unchanged
+ subject = Net::Hippie::ContentTypeMapper.new
+ headers = { 'Content-Type' => 'application/json' }
+ body = '{"already": "json"}'
+ result = subject.map_from(headers, body)
+ assert_equal body, result
+ end
+
+ def test_returns_json_for_various_json_content_types
+ subject = Net::Hippie::ContentTypeMapper.new
+ body = { message: 'test' }
+ expected = JSON.generate(body)
+
+ json_types = [
+ 'application/json',
+ 'application/json; charset=utf-8',
+ 'application/json; charset=iso-8859-1',
+ 'application/vnd.api+json',
+ 'text/json'
+ ]
+
+ json_types.each do |content_type|
+ headers = { 'Content-Type' => content_type }
+ result = subject.map_from(headers, body)
+ assert_equal expected, result, "Failed for content type: #{content_type}"
+ end
+ end
+
+ def test_returns_hash_body_for_non_json_content_types
+ subject = Net::Hippie::ContentTypeMapper.new
+ body = { message: 'test' }
+
+ non_json_types = [
+ 'text/plain',
+ 'text/html',
+ 'application/xml',
+ 'application/octet-stream',
+ 'multipart/form-data'
+ ]
+
+ non_json_types.each do |content_type|
+ headers = { 'Content-Type' => content_type }
+ result = subject.map_from(headers, body)
+ assert_equal body, result, "Failed for content type: #{content_type}"
+ end
+ end
+
+ def test_handles_nil_content_type
+ subject = Net::Hippie::ContentTypeMapper.new
+ headers = {}
+ body = { message: 'test' }
+ result = subject.map_from(headers, body)
+ assert_equal body, result
+ end
+
+ def test_handles_empty_content_type
+ subject = Net::Hippie::ContentTypeMapper.new
+ headers = { 'Content-Type' => '' }
+ body = { message: 'test' }
+ result = subject.map_from(headers, body)
+ assert_equal body, result
+ end
+
+ def test_handles_case_insensitive_content_type_headers
+ subject = Net::Hippie::ContentTypeMapper.new
+ body = { message: 'test' }
+
+ # Test various case combinations - current implementation only handles exact 'Content-Type'
+ # This test documents the current behavior
+ headers_variations = [
+ { 'content-type' => 'application/json' },
+ { 'Content-type' => 'application/json' },
+ { 'CONTENT-TYPE' => 'application/json' }
+ ]
+
+ headers_variations.each do |headers|
+ result = subject.map_from(headers, body)
+ # Current implementation doesn't handle case-insensitive headers
+ # so these should return the original body, not JSON
+ assert_equal body, result
+ end
+ end
+
+ def test_handles_complex_json_objects
+ subject = Net::Hippie::ContentTypeMapper.new
+ headers = { 'Content-Type' => 'application/json' }
+ body = {
+ string: 'test',
+ number: 123,
+ boolean: true,
+ nil_value: nil,
+ array: [1, 2, 3],
+ nested: { key: 'value' }
+ }
+ result = subject.map_from(headers, body)
+ assert_equal JSON.generate(body), result
+ # Verify it's valid JSON by parsing it back
+ parsed = JSON.parse(result)
+ expected_parsed = JSON.parse(JSON.generate(body))
+ assert_equal expected_parsed, parsed
+ end
end
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
diff --git a/test/net/timeout_test.rb b/test/net/timeout_test.rb
new file mode 100644
index 0000000..a085b54
--- /dev/null
+++ b/test/net/timeout_test.rb
@@ -0,0 +1,127 @@
+# frozen_string_literal: true
+
+require 'test_helper'
+
+class TimeoutTest < Minitest::Test
+ def setup
+ @uri = URI.parse('https://example.com/test')
+ end
+
+ def test_custom_read_timeout
+ client = Net::Hippie::Client.new(read_timeout: 5)
+ connection = client.send(:connection_for, @uri)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal 5, http.read_timeout
+ end
+
+ def test_custom_open_timeout
+ client = Net::Hippie::Client.new(open_timeout: 8)
+ connection = client.send(:connection_for, @uri)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal 8, http.open_timeout
+ end
+
+ def test_default_timeouts
+ client = Net::Hippie::Client.new
+ connection = client.send(:connection_for, @uri)
+ backend = connection.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal 10, http.read_timeout
+ assert_equal 10, http.open_timeout
+ end
+
+ def test_read_timeout_triggers_retry
+ WebMock.stub_request(:get, @uri.to_s)
+ .to_timeout.then
+ .to_return(status: 200, body: 'success')
+
+ client = Net::Hippie::Client.new
+ response = client.with_retry(retries: 1) { |c| c.get(@uri) }
+
+ assert_equal Net::HTTPOK, response.class
+ assert_equal 'success', response.body
+ end
+
+ def test_open_timeout_triggers_retry
+ WebMock.stub_request(:get, @uri.to_s)
+ .to_raise(Net::OpenTimeout).then
+ .to_return(status: 200, body: 'success')
+
+ client = Net::Hippie::Client.new
+ response = client.with_retry(retries: 1) { |c| c.get(@uri) }
+
+ assert_equal Net::HTTPOK, response.class
+ assert_equal 'success', response.body
+ end
+
+ def test_timeout_with_zero_retries
+ WebMock.stub_request(:get, @uri.to_s).to_timeout
+
+ client = Net::Hippie::Client.new
+ # WebMock.to_timeout raises different timeout errors, so check for any timeout error
+ assert_raises(*Net::Hippie::CONNECTION_ERRORS.select { |e| e.name.include?('Timeout') }) do
+ client.with_retry(retries: 0) { |c| c.get(@uri) }
+ end
+ end
+
+ def test_multiple_timeout_types_in_sequence
+ call_count = 0
+ WebMock.stub_request(:get, @uri.to_s).to_return do
+ call_count += 1
+ case call_count
+ when 1
+ raise Net::OpenTimeout
+ when 2
+ raise Net::ReadTimeout
+ when 3
+ raise Timeout::Error
+ else
+ { status: 200, body: 'success' }
+ end
+ end
+
+ client = Net::Hippie::Client.new
+ response = client.with_retry(retries: 4) { |c| c.get(@uri) }
+
+ assert_equal Net::HTTPOK, response.class
+ assert_equal 'success', response.body
+ assert_equal 4, call_count
+ end
+
+ def test_timeout_settings_per_connection
+ uri1 = URI.parse('https://example1.com/test')
+ uri2 = URI.parse('https://example2.com/test')
+
+ client = Net::Hippie::Client.new(read_timeout: 15, open_timeout: 20)
+
+ connection1 = client.send(:connection_for, uri1)
+ connection2 = client.send(:connection_for, uri2)
+
+ backend1 = connection1.instance_variable_get(:@backend)
+ backend2 = connection2.instance_variable_get(:@backend)
+ http1 = backend1.instance_variable_get(:@http)
+ http2 = backend2.instance_variable_get(:@http)
+
+ assert_equal 15, http1.read_timeout
+ assert_equal 20, http1.open_timeout
+ assert_equal 15, http2.read_timeout
+ assert_equal 20, http2.open_timeout
+ end
+
+ def test_timeout_preserves_connection_pooling
+ client = Net::Hippie::Client.new(read_timeout: 25)
+
+ # First call should create connection
+ connection1 = client.send(:connection_for, @uri)
+ # Second call should reuse same connection
+ connection2 = client.send(:connection_for, @uri)
+
+ assert_same connection1, connection2
+
+ backend = connection1.instance_variable_get(:@backend)
+ http = backend.instance_variable_get(:@http)
+ assert_equal 25, http.read_timeout
+ end
+end \ No newline at end of file