From 34d16285dbcc574c90b273a89f16cb5fb9f4222a Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Sun, 22 Sep 2024 18:22:04 +0200 Subject: [PATCH 1/4] Exclude em-http-request on Ruby 3.4 --- .../em_http_request_adapter.rb | 2 + .../em_http_request/em_http_request_spec.rb | 682 +++++++++--------- spec/spec_helper.rb | 2 +- 3 files changed, 345 insertions(+), 341 deletions(-) diff --git a/lib/webmock/http_lib_adapters/em_http_request_adapter.rb b/lib/webmock/http_lib_adapters/em_http_request_adapter.rb index ec0b89f5..b4f55dd1 100644 --- a/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +++ b/lib/webmock/http_lib_adapters/em_http_request_adapter.rb @@ -1,5 +1,7 @@ # frozen_string_literal: true +return if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') && Gem.loaded_specs['em-http-request'].version <= Gem::Version.new('1.1.7') + begin require 'em-http-request' rescue LoadError diff --git a/spec/acceptance/em_http_request/em_http_request_spec.rb b/spec/acceptance/em_http_request/em_http_request_spec.rb index 04b4320a..c45af8dd 100644 --- a/spec/acceptance/em_http_request/em_http_request_spec.rb +++ b/spec/acceptance/em_http_request/em_http_request_spec.rb @@ -3,77 +3,169 @@ require 'acceptance/webmock_shared' require 'ostruct' -unless RUBY_PLATFORM =~ /java/ - require 'acceptance/em_http_request/em_http_request_spec_helper' +require 'acceptance/em_http_request/em_http_request_spec_helper' - describe "EM::HttpRequest" do - include EMHttpRequestSpecHelper +describe "EM::HttpRequest" do + include EMHttpRequestSpecHelper - include_context "with WebMock", :no_status_message + before(:all) do + skip 'em-http-request is not supported on JRuby' if RUBY_PLATFORM =~ /java/ + skip 'em-http-request <= 1.1.7 is not supported on Ruby >= 3.4' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') && Gem.loaded_specs['em-http-request'].version <= Gem::Version.new('1.1.7') + end - #functionality only supported for em-http-request 1.x - if defined?(EventMachine::HttpConnection) - context 'when a real request is made and redirects are followed', net_connect: true do - before { WebMock.allow_net_connect! } + include_context "with WebMock", :no_status_message - # This url redirects to the https URL. - let(:http_url) { "http://raw.github.com:80/gist/fb555cb593f3349d53af/6921dd638337d3f6a51b0e02e7f30e3c414f70d6/vcr_gist" } - let(:https_url) { http_url.gsub('http', 'https').gsub('80', '443') } + #functionality only supported for em-http-request 1.x + if defined?(EventMachine::HttpConnection) + context 'when a real request is made and redirects are followed', net_connect: true do + before { WebMock.allow_net_connect! } - def make_request - EM.run do - request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1) - request.callback { EM.stop } + # This url redirects to the https URL. + let(:http_url) { "http://raw.github.com:80/gist/fb555cb593f3349d53af/6921dd638337d3f6a51b0e02e7f30e3c414f70d6/vcr_gist" } + let(:https_url) { http_url.gsub('http', 'https').gsub('80', '443') } + + def make_request + EM.run do + request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1) + request.callback { EM.stop } + end + end + + it "invokes the globally_stub_request hook with both requests" do + urls = [] + WebMock.globally_stub_request { |r| urls << r.uri.to_s; nil } + + make_request + + expect(urls).to eq([http_url, https_url]) + end + + it 'invokes the after_request hook with both requests' do + urls = [] + WebMock.after_request { |req, res| urls << req.uri.to_s } + + make_request + + expect(urls).to eq([http_url, https_url]) + end + end + + describe "with middleware" do + + it "should work with request middleware" do + stub_request(:get, "www.example.com").with(body: 'bar') + + middleware = Class.new do + def request(client, head, body) + [{}, 'bar'] end end - it "invokes the globally_stub_request hook with both requests" do - urls = [] - WebMock.globally_stub_request { |r| urls << r.uri.to_s; nil } + EM.run do + conn = EventMachine::HttpRequest.new('http://www.example.com/') - make_request + conn.use middleware - expect(urls).to eq([http_url, https_url]) + http = conn.get(body: 'foo') + + http.callback do + expect(WebMock).to have_requested(:get, "www.example.com").with(body: 'bar') + EM.stop + end end + end + + it "only calls request middleware once" do + stub_request(:get, "www.example.com") - it 'invokes the after_request hook with both requests' do - urls = [] - WebMock.after_request { |req, res| urls << req.uri.to_s } + middleware = Class.new do + def self.called! + @called = called + 1 + end + + def self.called + @called || 0 + end - make_request + def request(client, head, body) + self.class.called! + [head, body] + end + end + + EM.run do + conn = EventMachine::HttpRequest.new('http://www.example.com/') + conn.use middleware + http = conn.get + http.callback do + expect(middleware.called).to eq(1) + EM.stop + end + end + end - expect(urls).to eq([http_url, https_url]) + let(:response_middleware) do + Class.new do + def response(resp) + resp.response = 'bar' + end end end - describe "with middleware" do + it "should work with response middleware" do + stub_request(:get, "www.example.com").to_return(body: 'foo') - it "should work with request middleware" do - stub_request(:get, "www.example.com").with(body: 'bar') + EM.run do + conn = EventMachine::HttpRequest.new('http://www.example.com/') - middleware = Class.new do - def request(client, head, body) - [{}, 'bar'] - end + conn.use response_middleware + + http = conn.get + + http.callback do + expect(http.response).to eq('bar') + EM.stop end + end + end + + let(:webmock_server_url) { "http://#{WebMockServer.instance.host_with_port}/" } + + shared_examples_for "em-http-request middleware/after_request hook integration" do + it 'yields the original raw body to the after_request hook even if a response middleware modifies the body' do + yielded_response_body = nil + ::WebMock.after_request do |request, response| + yielded_response_body = response.body + end + + EM::HttpRequest.use response_middleware EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') + http = EventMachine::HttpRequest.new(webmock_server_url).get + http.callback { EM.stop } + end - conn.use middleware + expect(yielded_response_body).to eq("hello world") + end + end - http = conn.get(body: 'foo') + context 'making a real request', net_connect: true do + before { WebMock.allow_net_connect! } + include_examples "em-http-request middleware/after_request hook integration" + it "doesn't modify headers" do + EM.run do + conn = EventMachine::HttpRequest.new(webmock_server_url) + http = conn.post(head: { 'content-length' => '4' }, body: 'test') + expect(conn).to receive(:send_data).with(/POST \/ HTTP\/1.1\r\nContent-Length: 4\r\nConnection: close\r\nHost: localhost:\d+\r\nUser-Agent: EventMachine HttpClient\r\nAccept-Encoding: gzip, compressed\r\n\r\n/).and_call_original + expect(conn).to receive(:send_data).with('test') http.callback do - expect(WebMock).to have_requested(:get, "www.example.com").with(body: 'bar') EM.stop end end end it "only calls request middleware once" do - stub_request(:get, "www.example.com") - middleware = Class.new do def self.called! @called = called + 1 @@ -90,7 +182,7 @@ def request(client, head, body) end EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') + conn = EventMachine::HttpRequest.new(webmock_server_url) conn.use middleware http = conn.get http.callback do @@ -99,376 +191,286 @@ def request(client, head, body) end end end + end - let(:response_middleware) do - Class.new do - def response(resp) - resp.response = 'bar' - end - end - end - - it "should work with response middleware" do - stub_request(:get, "www.example.com").to_return(body: 'foo') - - EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') - - conn.use response_middleware + context 'when the request is stubbed' do + before { stub_request(:get, webmock_server_url).to_return(body: 'hello world') } + include_examples "em-http-request middleware/after_request hook integration" + end + end - http = conn.get + it 'should trigger error callbacks asynchronously' do + stub_request(:get, 'www.example.com').to_timeout + called = false - http.callback do - expect(http.response).to eq('bar') - EM.stop - end - end + EM.run do + conn = EventMachine::HttpRequest.new('http://www.example.com/') + http = conn.get + http.errback do + called = true + EM.stop end + expect(called).to eq(false) + end - let(:webmock_server_url) { "http://#{WebMockServer.instance.host_with_port}/" } - - shared_examples_for "em-http-request middleware/after_request hook integration" do - it 'yields the original raw body to the after_request hook even if a response middleware modifies the body' do - yielded_response_body = nil - ::WebMock.after_request do |request, response| - yielded_response_body = response.body - end + expect(called).to eq(true) + end - EM::HttpRequest.use response_middleware + # not pretty, but it works + if defined?(EventMachine::Synchrony) + describe "with synchrony" do + let(:webmock_em_http) { File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb")) } - EM.run do - http = EventMachine::HttpRequest.new(webmock_server_url).get - http.callback { EM.stop } - end - - expect(yielded_response_body).to eq("hello world") - end + before(:each) do + # need to reload the webmock em-http adapter after we require synchrony + WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! + $".delete webmock_em_http + $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + require 'em-synchrony' + require 'em-synchrony/em-http' + require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) end - context 'making a real request', net_connect: true do - before { WebMock.allow_net_connect! } - include_examples "em-http-request middleware/after_request hook integration" - - it "doesn't modify headers" do + it "should work" do + stub_request(:post, /.*.testserver.com*/).to_return(status: 200, body: 'ok') + expect { EM.run do - conn = EventMachine::HttpRequest.new(webmock_server_url) - http = conn.post(head: { 'content-length' => '4' }, body: 'test') - expect(conn).to receive(:send_data).with(/POST \/ HTTP\/1.1\r\nContent-Length: 4\r\nConnection: close\r\nHost: localhost:\d+\r\nUser-Agent: EventMachine HttpClient\r\nAccept-Encoding: gzip, compressed\r\n\r\n/).and_call_original - expect(conn).to receive(:send_data).with('test') - http.callback do + fiber = Fiber.new do + EM::HttpRequest.new("http://www.testserver.com").post body: "foo=bar&baz=bang", timeout: 60 EM.stop end + fiber.resume end - end - - it "only calls request middleware once" do - middleware = Class.new do - def self.called! - @called = called + 1 - end - - def self.called - @called || 0 - end - - def request(client, head, body) - self.class.called! - [head, body] - end - end - - EM.run do - conn = EventMachine::HttpRequest.new(webmock_server_url) - conn.use middleware - http = conn.get - http.callback do - expect(middleware.called).to eq(1) - EM.stop - end - end - end + }.not_to raise_error end - context 'when the request is stubbed' do - before { stub_request(:get, webmock_server_url).to_return(body: 'hello world') } - include_examples "em-http-request middleware/after_request hook integration" + after(:each) do + EM.send(:remove_const, :Synchrony) + EM.send(:remove_const, :HTTPMethods) + WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! + $".reject! {|path| path.include? "em-http-request"} + $".delete webmock_em_http + $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + require 'em-http-request' + require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) end end + end + end - it 'should trigger error callbacks asynchronously' do - stub_request(:get, 'www.example.com').to_timeout - called = false - - EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') - http = conn.get - http.errback do - called = true - EM.stop - end - expect(called).to eq(false) - end + it "should work with streaming" do + stub_request(:get, "www.example.com").to_return(body: "abc") + response = "" + EM.run { + http = EventMachine::HttpRequest.new('http://www.example.com/').get + http.stream { |chunk| response = chunk; EM.stop } + } + expect(response).to eq("abc") + end - expect(called).to eq(true) - end + it "should work with responses that use chunked transfer encoding" do + stub_request(:get, "www.example.com").to_return(body: "abc", headers: { 'Transfer-Encoding' => 'chunked' }) + expect(http_request(:get, "http://www.example.com").body).to eq("abc") + end - # not pretty, but it works - if defined?(EventMachine::Synchrony) - describe "with synchrony" do - let(:webmock_em_http) { File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb")) } - - before(:each) do - # need to reload the webmock em-http adapter after we require synchrony - WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! - $".delete webmock_em_http - $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) - require 'em-synchrony' - require 'em-synchrony/em-http' - require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) - end + it "should work with optional query params" do + stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") + expect(http_request(:get, "http://www.example.com/?x=3", query: {"a" => ["b", "c"]}).body).to eq("abc") + end - it "should work" do - stub_request(:post, /.*.testserver.com*/).to_return(status: 200, body: 'ok') - expect { - EM.run do - fiber = Fiber.new do - EM::HttpRequest.new("http://www.testserver.com").post body: "foo=bar&baz=bang", timeout: 60 - EM.stop - end - fiber.resume - end - }.not_to raise_error - end + it "should work with optional query params declared as string" do + stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") + expect(http_request(:get, "http://www.example.com/?x=3", query: "a[]=b&a[]=c").body).to eq("abc") + end - after(:each) do - EM.send(:remove_const, :Synchrony) - EM.send(:remove_const, :HTTPMethods) - WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! - $".reject! {|path| path.include? "em-http-request"} - $".delete webmock_em_http - $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) - require 'em-http-request' - require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) - end - end - end - end + it "should work when the body is passed as a Hash" do + stub_request(:post, "www.example.com").with(body: {a: "1", b: "2"}).to_return(body: "ok") + expect(http_request(:post, "http://www.example.com", body: {a: "1", b: "2"}).body).to eq("ok") + end - it "should work with streaming" do - stub_request(:get, "www.example.com").to_return(body: "abc") - response = "" - EM.run { - http = EventMachine::HttpRequest.new('http://www.example.com/').get - http.stream { |chunk| response = chunk; EM.stop } - } - expect(response).to eq("abc") + if defined?(EventMachine::HttpConnection) + it "should work when a file is passed as body" do + stub_request(:post, "www.example.com").with(body: File.read(__FILE__)).to_return(body: "ok") + expect(http_request(:post, "http://www.example.com", file: __FILE__).body).to eq("ok") end + end - it "should work with responses that use chunked transfer encoding" do - stub_request(:get, "www.example.com").to_return(body: "abc", headers: { 'Transfer-Encoding' => 'chunked' }) - expect(http_request(:get, "http://www.example.com").body).to eq("abc") - end + it "should work with UTF-8 strings" do + body = "Привет, Мир!" + stub_request(:post, "www.example.com").to_return(body: body) + expect(http_request(:post, "http://www.example.com").body.bytesize).to eq(body.bytesize) + end - it "should work with optional query params" do - stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") - expect(http_request(:get, "http://www.example.com/?x=3", query: {"a" => ["b", "c"]}).body).to eq("abc") - end + it "should work with multiple requests to the same connection" do + stub_request(:get, "www.example.com/foo").to_return(body: "bar") + stub_request(:get, "www.example.com/baz").to_return(body: "wombat") + err1 = nil + err2 = nil + body1 = nil + body2 = nil + i = 0 + + EM.run do + conn = EM::HttpRequest.new("http://www.example.com") + conn.get(path: "/foo").callback do |resp| + body1 = resp.response + i += 1; EM.stop if i == 2 + end.errback do |resp| + err1 = resp.error + i += 1; EM.stop if i == 2 + end - it "should work with optional query params declared as string" do - stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") - expect(http_request(:get, "http://www.example.com/?x=3", query: "a[]=b&a[]=c").body).to eq("abc") + conn.get(path: "/baz").callback do |resp| + body2 = resp.response + i += 1; EM.stop if i == 2 + end.errback do |resp| + err2 = resp.error + i += 1; EM.stop if i == 2 + end end - it "should work when the body is passed as a Hash" do - stub_request(:post, "www.example.com").with(body: {a: "1", b: "2"}).to_return(body: "ok") - expect(http_request(:post, "http://www.example.com", body: {a: "1", b: "2"}).body).to eq("ok") - end + expect(err1).to be(nil) + expect(err2).to be(nil) + expect(body1).to eq("bar") + expect(body2).to eq("wombat") + end - if defined?(EventMachine::HttpConnection) - it "should work when a file is passed as body" do - stub_request(:post, "www.example.com").with(body: File.read(__FILE__)).to_return(body: "ok") - expect(http_request(:post, "http://www.example.com", file: __FILE__).body).to eq("ok") + it "should work with multiple requests to the same connection when the first request times out" do + stub_request(:get, "www.example.com/foo").to_timeout.then.to_return(status: 200, body: "wombat") + err = nil + body = nil + + EM.run do + conn = EM::HttpRequest.new("http://www.example.com") + conn.get(path: "/foo").callback do |resp| + err = :success_from_timeout + EM.stop + end.errback do |resp| + conn.get(path: "/foo").callback do |retry_resp| + expect(retry_resp.response_header.status).to eq(200) + body = retry_resp.response + EM.stop + end.errback do |retry_resp| + err = retry_resp.error + EM.stop + end end end - it "should work with UTF-8 strings" do - body = "Привет, Мир!" - stub_request(:post, "www.example.com").to_return(body: body) - expect(http_request(:post, "http://www.example.com").body.bytesize).to eq(body.bytesize) - end - - it "should work with multiple requests to the same connection" do - stub_request(:get, "www.example.com/foo").to_return(body: "bar") - stub_request(:get, "www.example.com/baz").to_return(body: "wombat") - err1 = nil - err2 = nil - body1 = nil - body2 = nil - i = 0 - - EM.run do - conn = EM::HttpRequest.new("http://www.example.com") - conn.get(path: "/foo").callback do |resp| - body1 = resp.response - i += 1; EM.stop if i == 2 - end.errback do |resp| - err1 = resp.error - i += 1; EM.stop if i == 2 - end + expect(err).to be(nil) + expect(body).to eq("wombat") + end - conn.get(path: "/baz").callback do |resp| - body2 = resp.response - i += 1; EM.stop if i == 2 - end.errback do |resp| - err2 = resp.error - i += 1; EM.stop if i == 2 - end - end + describe "mocking EM::HttpClient API" do + let(:uri) { "http://www.example.com/" } - expect(err1).to be(nil) - expect(err2).to be(nil) - expect(body1).to eq("bar") - expect(body2).to eq("wombat") + before do + stub_request(:get, uri) + WebMock::HttpLibAdapters::EmHttpRequestAdapter.enable! end - it "should work with multiple requests to the same connection when the first request times out" do - stub_request(:get, "www.example.com/foo").to_timeout.then.to_return(status: 200, body: "wombat") - err = nil - body = nil - + def client(uri, options = {}) + client = nil EM.run do - conn = EM::HttpRequest.new("http://www.example.com") - conn.get(path: "/foo").callback do |resp| - err = :success_from_timeout - EM.stop - end.errback do |resp| - conn.get(path: "/foo").callback do |retry_resp| - expect(retry_resp.response_header.status).to eq(200) - body = retry_resp.response - EM.stop - end.errback do |retry_resp| - err = retry_resp.error - EM.stop - end - end + client = EventMachine::HttpRequest.new(uri).get(options) + client.callback { EM.stop } + client.errback { failed } end - - expect(err).to be(nil) - expect(body).to eq("wombat") + client end - describe "mocking EM::HttpClient API" do - let(:uri) { "http://www.example.com/" } - - before do - stub_request(:get, uri) - WebMock::HttpLibAdapters::EmHttpRequestAdapter.enable! - end + subject { client(uri) } - def client(uri, options = {}) - client = nil - EM.run do - client = EventMachine::HttpRequest.new(uri).get(options) - client.callback { EM.stop } - client.errback { failed } - end - client - end + it 'should support #uri' do + expect(subject.uri).to eq(Addressable::URI.parse(uri)) + end - subject { client(uri) } + it 'should support #last_effective_url' do + expect(subject.last_effective_url).to eq(Addressable::URI.parse(uri)) + end - it 'should support #uri' do - expect(subject.uri).to eq(Addressable::URI.parse(uri)) - end + context "with a query" do + let(:uri) { "http://www.example.com/?a=1&b=2" } + subject { client("http://www.example.com/?a=1", query: { 'b' => 2 }) } - it 'should support #last_effective_url' do - expect(subject.last_effective_url).to eq(Addressable::URI.parse(uri)) + it "#request_signature doesn't mutate the original uri" do + expect(subject.uri).to eq(Addressable::URI.parse("http://www.example.com/?a=1")) + signature = WebMock::RequestRegistry.instance.requested_signatures.hash.keys.first + expect(signature.uri).to eq(Addressable::URI.parse(uri)) end + end - context "with a query" do - let(:uri) { "http://www.example.com/?a=1&b=2" } - subject { client("http://www.example.com/?a=1", query: { 'b' => 2 }) } + describe 'get_response_cookie' do - it "#request_signature doesn't mutate the original uri" do - expect(subject.uri).to eq(Addressable::URI.parse("http://www.example.com/?a=1")) - signature = WebMock::RequestRegistry.instance.requested_signatures.hash.keys.first - expect(signature.uri).to eq(Addressable::URI.parse(uri)) - end + before(:each) do + stub_request(:get, "http://example.org/"). + to_return( + status: 200, + body: "", + headers: { 'Set-Cookie' => cookie_string } + ) end - describe 'get_response_cookie' do + describe 'success' do - before(:each) do - stub_request(:get, "http://example.org/"). - to_return( - status: 200, - body: "", - headers: { 'Set-Cookie' => cookie_string } - ) - end - - describe 'success' do + context 'with only one cookie' do - context 'with only one cookie' do + let(:cookie_name) { 'name_of_the_cookie' } + let(:cookie_value) { 'value_of_the_cookie' } + let(:cookie_string) { "#{cookie_name}=#{cookie_value}" } - let(:cookie_name) { 'name_of_the_cookie' } - let(:cookie_value) { 'value_of_the_cookie' } - let(:cookie_string) { "#{cookie_name}=#{cookie_value}" } - - it 'successfully gets the cookie' do - EM.run { - http = EventMachine::HttpRequest.new('http://example.org').get + it 'successfully gets the cookie' do + EM.run { + http = EventMachine::HttpRequest.new('http://example.org').get - http.errback { fail(http.error) } - http.callback { - expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) - EM.stop - } + http.errback { fail(http.error) } + http.callback { + expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) + EM.stop } - end + } end + end - context 'with several cookies' do + context 'with several cookies' do - let(:cookie_name) { 'name_of_the_cookie' } - let(:cookie_value) { 'value_of_the_cookie' } - let(:cookie_2_name) { 'name_of_the_2nd_cookie' } - let(:cookie_2_value) { 'value_of_the_2nd_cookie' } - let(:cookie_string) { %W(#{cookie_name}=#{cookie_value} #{cookie_2_name}=#{cookie_2_value}) } + let(:cookie_name) { 'name_of_the_cookie' } + let(:cookie_value) { 'value_of_the_cookie' } + let(:cookie_2_name) { 'name_of_the_2nd_cookie' } + let(:cookie_2_value) { 'value_of_the_2nd_cookie' } + let(:cookie_string) { %W(#{cookie_name}=#{cookie_value} #{cookie_2_name}=#{cookie_2_value}) } - it 'successfully gets both cookies' do - EM.run { - http = EventMachine::HttpRequest.new('http://example.org').get + it 'successfully gets both cookies' do + EM.run { + http = EventMachine::HttpRequest.new('http://example.org').get - http.errback { fail(http.error) } - http.callback { - expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) - expect(http.get_response_cookie(cookie_2_name)).to eq(cookie_2_value) - EM.stop - } + http.errback { fail(http.error) } + http.callback { + expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) + expect(http.get_response_cookie(cookie_2_name)).to eq(cookie_2_value) + EM.stop } - end + } end end + end - describe 'failure' do + describe 'failure' do - let(:cookie_string) { 'a=b' } + let(:cookie_string) { 'a=b' } - it 'returns nil when no cookie is found' do - EM.run { - http = EventMachine::HttpRequest.new('http://example.org').get + it 'returns nil when no cookie is found' do + EM.run { + http = EventMachine::HttpRequest.new('http://example.org').get - http.errback { fail(http.error) } - http.callback { - expect(http.get_response_cookie('not_found_cookie')).to eq(nil) - EM.stop - } + http.errback { fail(http.error) } + http.callback { + expect(http.get_response_cookie('not_found_cookie')).to eq(nil) + EM.stop } - end + } end end end - end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9c7bc842..9c7e1f23 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,9 +1,9 @@ require 'rubygems' require 'httpclient' + unless RUBY_PLATFORM =~ /java/ require 'curb' require 'patron' - require 'em-http' require 'typhoeus' end if RUBY_PLATFORM =~ /java/ From 9a957a26ad110350c949039747667ae06c7f4176 Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Sun, 22 Sep 2024 20:01:46 +0200 Subject: [PATCH 2/4] Do not speculate about em-http-request version --- lib/webmock/http_lib_adapters/em_http_request_adapter.rb | 2 +- spec/acceptance/em_http_request/em_http_request_spec.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/webmock/http_lib_adapters/em_http_request_adapter.rb b/lib/webmock/http_lib_adapters/em_http_request_adapter.rb index b4f55dd1..8a4eecb9 100644 --- a/lib/webmock/http_lib_adapters/em_http_request_adapter.rb +++ b/lib/webmock/http_lib_adapters/em_http_request_adapter.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -return if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') && Gem.loaded_specs['em-http-request'].version <= Gem::Version.new('1.1.7') +return if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') begin require 'em-http-request' diff --git a/spec/acceptance/em_http_request/em_http_request_spec.rb b/spec/acceptance/em_http_request/em_http_request_spec.rb index c45af8dd..ff5571f8 100644 --- a/spec/acceptance/em_http_request/em_http_request_spec.rb +++ b/spec/acceptance/em_http_request/em_http_request_spec.rb @@ -10,7 +10,7 @@ before(:all) do skip 'em-http-request is not supported on JRuby' if RUBY_PLATFORM =~ /java/ - skip 'em-http-request <= 1.1.7 is not supported on Ruby >= 3.4' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') && Gem.loaded_specs['em-http-request'].version <= Gem::Version.new('1.1.7') + skip 'em-http-request is not supported on Ruby >= 3.4' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') end include_context "with WebMock", :no_status_message From 957792464dd22fe293e862150413d75a24cf7bbe Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Sun, 22 Sep 2024 22:34:46 +0200 Subject: [PATCH 3/4] Revert JRuby change --- .../em_http_request/em_http_request_spec.rb | 684 +++++++++--------- 1 file changed, 343 insertions(+), 341 deletions(-) diff --git a/spec/acceptance/em_http_request/em_http_request_spec.rb b/spec/acceptance/em_http_request/em_http_request_spec.rb index ff5571f8..e7218116 100644 --- a/spec/acceptance/em_http_request/em_http_request_spec.rb +++ b/spec/acceptance/em_http_request/em_http_request_spec.rb @@ -3,169 +3,81 @@ require 'acceptance/webmock_shared' require 'ostruct' -require 'acceptance/em_http_request/em_http_request_spec_helper' +unless RUBY_PLATFORM =~ /java/ + require 'acceptance/em_http_request/em_http_request_spec_helper' -describe "EM::HttpRequest" do - include EMHttpRequestSpecHelper + describe "EM::HttpRequest" do + include EMHttpRequestSpecHelper - before(:all) do - skip 'em-http-request is not supported on JRuby' if RUBY_PLATFORM =~ /java/ - skip 'em-http-request is not supported on Ruby >= 3.4' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') - end - - include_context "with WebMock", :no_status_message - - #functionality only supported for em-http-request 1.x - if defined?(EventMachine::HttpConnection) - context 'when a real request is made and redirects are followed', net_connect: true do - before { WebMock.allow_net_connect! } - - # This url redirects to the https URL. - let(:http_url) { "http://raw.github.com:80/gist/fb555cb593f3349d53af/6921dd638337d3f6a51b0e02e7f30e3c414f70d6/vcr_gist" } - let(:https_url) { http_url.gsub('http', 'https').gsub('80', '443') } - - def make_request - EM.run do - request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1) - request.callback { EM.stop } - end - end - - it "invokes the globally_stub_request hook with both requests" do - urls = [] - WebMock.globally_stub_request { |r| urls << r.uri.to_s; nil } - - make_request - - expect(urls).to eq([http_url, https_url]) - end - - it 'invokes the after_request hook with both requests' do - urls = [] - WebMock.after_request { |req, res| urls << req.uri.to_s } - - make_request - - expect(urls).to eq([http_url, https_url]) - end + before(:all) do + skip 'em-http-request is not supported on Ruby >= 3.4' if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.4.0') end - describe "with middleware" do - - it "should work with request middleware" do - stub_request(:get, "www.example.com").with(body: 'bar') - - middleware = Class.new do - def request(client, head, body) - [{}, 'bar'] - end - end - - EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') + include_context "with WebMock", :no_status_message - conn.use middleware + #functionality only supported for em-http-request 1.x + if defined?(EventMachine::HttpConnection) + context 'when a real request is made and redirects are followed', net_connect: true do + before { WebMock.allow_net_connect! } - http = conn.get(body: 'foo') + # This url redirects to the https URL. + let(:http_url) { "http://raw.github.com:80/gist/fb555cb593f3349d53af/6921dd638337d3f6a51b0e02e7f30e3c414f70d6/vcr_gist" } + let(:https_url) { http_url.gsub('http', 'https').gsub('80', '443') } - http.callback do - expect(WebMock).to have_requested(:get, "www.example.com").with(body: 'bar') - EM.stop + def make_request + EM.run do + request = EM::HttpRequest.new(http_url, ssl: {verify_peer: true}).get(redirects: 1) + request.callback { EM.stop } end end - end - - it "only calls request middleware once" do - stub_request(:get, "www.example.com") - middleware = Class.new do - def self.called! - @called = called + 1 - end - - def self.called - @called || 0 - end - - def request(client, head, body) - self.class.called! - [head, body] - end - end + it "invokes the globally_stub_request hook with both requests" do + urls = [] + WebMock.globally_stub_request { |r| urls << r.uri.to_s; nil } - EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') - conn.use middleware - http = conn.get - http.callback do - expect(middleware.called).to eq(1) - EM.stop - end - end - end + make_request - let(:response_middleware) do - Class.new do - def response(resp) - resp.response = 'bar' - end + expect(urls).to eq([http_url, https_url]) end - end - it "should work with response middleware" do - stub_request(:get, "www.example.com").to_return(body: 'foo') + it 'invokes the after_request hook with both requests' do + urls = [] + WebMock.after_request { |req, res| urls << req.uri.to_s } - EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') + make_request - conn.use response_middleware - - http = conn.get - - http.callback do - expect(http.response).to eq('bar') - EM.stop - end + expect(urls).to eq([http_url, https_url]) end end - let(:webmock_server_url) { "http://#{WebMockServer.instance.host_with_port}/" } + describe "with middleware" do - shared_examples_for "em-http-request middleware/after_request hook integration" do - it 'yields the original raw body to the after_request hook even if a response middleware modifies the body' do - yielded_response_body = nil - ::WebMock.after_request do |request, response| - yielded_response_body = response.body - end + it "should work with request middleware" do + stub_request(:get, "www.example.com").with(body: 'bar') - EM::HttpRequest.use response_middleware + middleware = Class.new do + def request(client, head, body) + [{}, 'bar'] + end + end EM.run do - http = EventMachine::HttpRequest.new(webmock_server_url).get - http.callback { EM.stop } - end + conn = EventMachine::HttpRequest.new('http://www.example.com/') - expect(yielded_response_body).to eq("hello world") - end - end + conn.use middleware - context 'making a real request', net_connect: true do - before { WebMock.allow_net_connect! } - include_examples "em-http-request middleware/after_request hook integration" + http = conn.get(body: 'foo') - it "doesn't modify headers" do - EM.run do - conn = EventMachine::HttpRequest.new(webmock_server_url) - http = conn.post(head: { 'content-length' => '4' }, body: 'test') - expect(conn).to receive(:send_data).with(/POST \/ HTTP\/1.1\r\nContent-Length: 4\r\nConnection: close\r\nHost: localhost:\d+\r\nUser-Agent: EventMachine HttpClient\r\nAccept-Encoding: gzip, compressed\r\n\r\n/).and_call_original - expect(conn).to receive(:send_data).with('test') http.callback do + expect(WebMock).to have_requested(:get, "www.example.com").with(body: 'bar') EM.stop end end end it "only calls request middleware once" do + stub_request(:get, "www.example.com") + middleware = Class.new do def self.called! @called = called + 1 @@ -182,7 +94,7 @@ def request(client, head, body) end EM.run do - conn = EventMachine::HttpRequest.new(webmock_server_url) + conn = EventMachine::HttpRequest.new('http://www.example.com/') conn.use middleware http = conn.get http.callback do @@ -191,286 +103,376 @@ def request(client, head, body) end end end - end - context 'when the request is stubbed' do - before { stub_request(:get, webmock_server_url).to_return(body: 'hello world') } - include_examples "em-http-request middleware/after_request hook integration" - end - end + let(:response_middleware) do + Class.new do + def response(resp) + resp.response = 'bar' + end + end + end - it 'should trigger error callbacks asynchronously' do - stub_request(:get, 'www.example.com').to_timeout - called = false + it "should work with response middleware" do + stub_request(:get, "www.example.com").to_return(body: 'foo') - EM.run do - conn = EventMachine::HttpRequest.new('http://www.example.com/') - http = conn.get - http.errback do - called = true - EM.stop + EM.run do + conn = EventMachine::HttpRequest.new('http://www.example.com/') + + conn.use response_middleware + + http = conn.get + + http.callback do + expect(http.response).to eq('bar') + EM.stop + end + end end - expect(called).to eq(false) - end - expect(called).to eq(true) - end + let(:webmock_server_url) { "http://#{WebMockServer.instance.host_with_port}/" } - # not pretty, but it works - if defined?(EventMachine::Synchrony) - describe "with synchrony" do - let(:webmock_em_http) { File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb")) } + shared_examples_for "em-http-request middleware/after_request hook integration" do + it 'yields the original raw body to the after_request hook even if a response middleware modifies the body' do + yielded_response_body = nil + ::WebMock.after_request do |request, response| + yielded_response_body = response.body + end - before(:each) do - # need to reload the webmock em-http adapter after we require synchrony - WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! - $".delete webmock_em_http - $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) - require 'em-synchrony' - require 'em-synchrony/em-http' - require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + EM::HttpRequest.use response_middleware + + EM.run do + http = EventMachine::HttpRequest.new(webmock_server_url).get + http.callback { EM.stop } + end + + expect(yielded_response_body).to eq("hello world") + end end - it "should work" do - stub_request(:post, /.*.testserver.com*/).to_return(status: 200, body: 'ok') - expect { + context 'making a real request', net_connect: true do + before { WebMock.allow_net_connect! } + include_examples "em-http-request middleware/after_request hook integration" + + it "doesn't modify headers" do EM.run do - fiber = Fiber.new do - EM::HttpRequest.new("http://www.testserver.com").post body: "foo=bar&baz=bang", timeout: 60 + conn = EventMachine::HttpRequest.new(webmock_server_url) + http = conn.post(head: { 'content-length' => '4' }, body: 'test') + expect(conn).to receive(:send_data).with(/POST \/ HTTP\/1.1\r\nContent-Length: 4\r\nConnection: close\r\nHost: localhost:\d+\r\nUser-Agent: EventMachine HttpClient\r\nAccept-Encoding: gzip, compressed\r\n\r\n/).and_call_original + expect(conn).to receive(:send_data).with('test') + http.callback do EM.stop end - fiber.resume end - }.not_to raise_error + end + + it "only calls request middleware once" do + middleware = Class.new do + def self.called! + @called = called + 1 + end + + def self.called + @called || 0 + end + + def request(client, head, body) + self.class.called! + [head, body] + end + end + + EM.run do + conn = EventMachine::HttpRequest.new(webmock_server_url) + conn.use middleware + http = conn.get + http.callback do + expect(middleware.called).to eq(1) + EM.stop + end + end + end end - after(:each) do - EM.send(:remove_const, :Synchrony) - EM.send(:remove_const, :HTTPMethods) - WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! - $".reject! {|path| path.include? "em-http-request"} - $".delete webmock_em_http - $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) - require 'em-http-request' - require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + context 'when the request is stubbed' do + before { stub_request(:get, webmock_server_url).to_return(body: 'hello world') } + include_examples "em-http-request middleware/after_request hook integration" end end - end - end - it "should work with streaming" do - stub_request(:get, "www.example.com").to_return(body: "abc") - response = "" - EM.run { - http = EventMachine::HttpRequest.new('http://www.example.com/').get - http.stream { |chunk| response = chunk; EM.stop } - } - expect(response).to eq("abc") - end + it 'should trigger error callbacks asynchronously' do + stub_request(:get, 'www.example.com').to_timeout + called = false - it "should work with responses that use chunked transfer encoding" do - stub_request(:get, "www.example.com").to_return(body: "abc", headers: { 'Transfer-Encoding' => 'chunked' }) - expect(http_request(:get, "http://www.example.com").body).to eq("abc") - end + EM.run do + conn = EventMachine::HttpRequest.new('http://www.example.com/') + http = conn.get + http.errback do + called = true + EM.stop + end + expect(called).to eq(false) + end - it "should work with optional query params" do - stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") - expect(http_request(:get, "http://www.example.com/?x=3", query: {"a" => ["b", "c"]}).body).to eq("abc") - end + expect(called).to eq(true) + end - it "should work with optional query params declared as string" do - stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") - expect(http_request(:get, "http://www.example.com/?x=3", query: "a[]=b&a[]=c").body).to eq("abc") - end + # not pretty, but it works + if defined?(EventMachine::Synchrony) + describe "with synchrony" do + let(:webmock_em_http) { File.expand_path(File.join(File.dirname(__FILE__), "../lib/webmock/http_lib_adapters/em_http_request/em_http_request_1_x.rb")) } + + before(:each) do + # need to reload the webmock em-http adapter after we require synchrony + WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! + $".delete webmock_em_http + $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + require 'em-synchrony' + require 'em-synchrony/em-http' + require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + end - it "should work when the body is passed as a Hash" do - stub_request(:post, "www.example.com").with(body: {a: "1", b: "2"}).to_return(body: "ok") - expect(http_request(:post, "http://www.example.com", body: {a: "1", b: "2"}).body).to eq("ok") - end + it "should work" do + stub_request(:post, /.*.testserver.com*/).to_return(status: 200, body: 'ok') + expect { + EM.run do + fiber = Fiber.new do + EM::HttpRequest.new("http://www.testserver.com").post body: "foo=bar&baz=bang", timeout: 60 + EM.stop + end + fiber.resume + end + }.not_to raise_error + end - if defined?(EventMachine::HttpConnection) - it "should work when a file is passed as body" do - stub_request(:post, "www.example.com").with(body: File.read(__FILE__)).to_return(body: "ok") - expect(http_request(:post, "http://www.example.com", file: __FILE__).body).to eq("ok") + after(:each) do + EM.send(:remove_const, :Synchrony) + EM.send(:remove_const, :HTTPMethods) + WebMock::HttpLibAdapters::EmHttpRequestAdapter.disable! + $".reject! {|path| path.include? "em-http-request"} + $".delete webmock_em_http + $".delete File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + require 'em-http-request' + require File.expand_path(File.join(File.dirname(__FILE__), "../../../lib/webmock/http_lib_adapters/em_http_request_adapter.rb")) + end + end + end end - end - it "should work with UTF-8 strings" do - body = "Привет, Мир!" - stub_request(:post, "www.example.com").to_return(body: body) - expect(http_request(:post, "http://www.example.com").body.bytesize).to eq(body.bytesize) - end + it "should work with streaming" do + stub_request(:get, "www.example.com").to_return(body: "abc") + response = "" + EM.run { + http = EventMachine::HttpRequest.new('http://www.example.com/').get + http.stream { |chunk| response = chunk; EM.stop } + } + expect(response).to eq("abc") + end - it "should work with multiple requests to the same connection" do - stub_request(:get, "www.example.com/foo").to_return(body: "bar") - stub_request(:get, "www.example.com/baz").to_return(body: "wombat") - err1 = nil - err2 = nil - body1 = nil - body2 = nil - i = 0 - - EM.run do - conn = EM::HttpRequest.new("http://www.example.com") - conn.get(path: "/foo").callback do |resp| - body1 = resp.response - i += 1; EM.stop if i == 2 - end.errback do |resp| - err1 = resp.error - i += 1; EM.stop if i == 2 - end + it "should work with responses that use chunked transfer encoding" do + stub_request(:get, "www.example.com").to_return(body: "abc", headers: { 'Transfer-Encoding' => 'chunked' }) + expect(http_request(:get, "http://www.example.com").body).to eq("abc") + end - conn.get(path: "/baz").callback do |resp| - body2 = resp.response - i += 1; EM.stop if i == 2 - end.errback do |resp| - err2 = resp.error - i += 1; EM.stop if i == 2 - end + it "should work with optional query params" do + stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") + expect(http_request(:get, "http://www.example.com/?x=3", query: {"a" => ["b", "c"]}).body).to eq("abc") end - expect(err1).to be(nil) - expect(err2).to be(nil) - expect(body1).to eq("bar") - expect(body2).to eq("wombat") - end + it "should work with optional query params declared as string" do + stub_request(:get, "www.example.com/?x=3&a[]=b&a[]=c").to_return(body: "abc") + expect(http_request(:get, "http://www.example.com/?x=3", query: "a[]=b&a[]=c").body).to eq("abc") + end - it "should work with multiple requests to the same connection when the first request times out" do - stub_request(:get, "www.example.com/foo").to_timeout.then.to_return(status: 200, body: "wombat") - err = nil - body = nil - - EM.run do - conn = EM::HttpRequest.new("http://www.example.com") - conn.get(path: "/foo").callback do |resp| - err = :success_from_timeout - EM.stop - end.errback do |resp| - conn.get(path: "/foo").callback do |retry_resp| - expect(retry_resp.response_header.status).to eq(200) - body = retry_resp.response - EM.stop - end.errback do |retry_resp| - err = retry_resp.error - EM.stop - end + it "should work when the body is passed as a Hash" do + stub_request(:post, "www.example.com").with(body: {a: "1", b: "2"}).to_return(body: "ok") + expect(http_request(:post, "http://www.example.com", body: {a: "1", b: "2"}).body).to eq("ok") + end + + if defined?(EventMachine::HttpConnection) + it "should work when a file is passed as body" do + stub_request(:post, "www.example.com").with(body: File.read(__FILE__)).to_return(body: "ok") + expect(http_request(:post, "http://www.example.com", file: __FILE__).body).to eq("ok") end end - expect(err).to be(nil) - expect(body).to eq("wombat") - end + it "should work with UTF-8 strings" do + body = "Привет, Мир!" + stub_request(:post, "www.example.com").to_return(body: body) + expect(http_request(:post, "http://www.example.com").body.bytesize).to eq(body.bytesize) + end + + it "should work with multiple requests to the same connection" do + stub_request(:get, "www.example.com/foo").to_return(body: "bar") + stub_request(:get, "www.example.com/baz").to_return(body: "wombat") + err1 = nil + err2 = nil + body1 = nil + body2 = nil + i = 0 - describe "mocking EM::HttpClient API" do - let(:uri) { "http://www.example.com/" } + EM.run do + conn = EM::HttpRequest.new("http://www.example.com") + conn.get(path: "/foo").callback do |resp| + body1 = resp.response + i += 1; EM.stop if i == 2 + end.errback do |resp| + err1 = resp.error + i += 1; EM.stop if i == 2 + end + + conn.get(path: "/baz").callback do |resp| + body2 = resp.response + i += 1; EM.stop if i == 2 + end.errback do |resp| + err2 = resp.error + i += 1; EM.stop if i == 2 + end + end - before do - stub_request(:get, uri) - WebMock::HttpLibAdapters::EmHttpRequestAdapter.enable! + expect(err1).to be(nil) + expect(err2).to be(nil) + expect(body1).to eq("bar") + expect(body2).to eq("wombat") end - def client(uri, options = {}) - client = nil + it "should work with multiple requests to the same connection when the first request times out" do + stub_request(:get, "www.example.com/foo").to_timeout.then.to_return(status: 200, body: "wombat") + err = nil + body = nil + EM.run do - client = EventMachine::HttpRequest.new(uri).get(options) - client.callback { EM.stop } - client.errback { failed } + conn = EM::HttpRequest.new("http://www.example.com") + conn.get(path: "/foo").callback do |resp| + err = :success_from_timeout + EM.stop + end.errback do |resp| + conn.get(path: "/foo").callback do |retry_resp| + expect(retry_resp.response_header.status).to eq(200) + body = retry_resp.response + EM.stop + end.errback do |retry_resp| + err = retry_resp.error + EM.stop + end + end end - client + + expect(err).to be(nil) + expect(body).to eq("wombat") end - subject { client(uri) } + describe "mocking EM::HttpClient API" do + let(:uri) { "http://www.example.com/" } - it 'should support #uri' do - expect(subject.uri).to eq(Addressable::URI.parse(uri)) - end + before do + stub_request(:get, uri) + WebMock::HttpLibAdapters::EmHttpRequestAdapter.enable! + end - it 'should support #last_effective_url' do - expect(subject.last_effective_url).to eq(Addressable::URI.parse(uri)) - end + def client(uri, options = {}) + client = nil + EM.run do + client = EventMachine::HttpRequest.new(uri).get(options) + client.callback { EM.stop } + client.errback { failed } + end + client + end - context "with a query" do - let(:uri) { "http://www.example.com/?a=1&b=2" } - subject { client("http://www.example.com/?a=1", query: { 'b' => 2 }) } + subject { client(uri) } - it "#request_signature doesn't mutate the original uri" do - expect(subject.uri).to eq(Addressable::URI.parse("http://www.example.com/?a=1")) - signature = WebMock::RequestRegistry.instance.requested_signatures.hash.keys.first - expect(signature.uri).to eq(Addressable::URI.parse(uri)) + it 'should support #uri' do + expect(subject.uri).to eq(Addressable::URI.parse(uri)) end - end - describe 'get_response_cookie' do + it 'should support #last_effective_url' do + expect(subject.last_effective_url).to eq(Addressable::URI.parse(uri)) + end + + context "with a query" do + let(:uri) { "http://www.example.com/?a=1&b=2" } + subject { client("http://www.example.com/?a=1", query: { 'b' => 2 }) } - before(:each) do - stub_request(:get, "http://example.org/"). - to_return( - status: 200, - body: "", - headers: { 'Set-Cookie' => cookie_string } - ) + it "#request_signature doesn't mutate the original uri" do + expect(subject.uri).to eq(Addressable::URI.parse("http://www.example.com/?a=1")) + signature = WebMock::RequestRegistry.instance.requested_signatures.hash.keys.first + expect(signature.uri).to eq(Addressable::URI.parse(uri)) + end end - describe 'success' do + describe 'get_response_cookie' do - context 'with only one cookie' do + before(:each) do + stub_request(:get, "http://example.org/"). + to_return( + status: 200, + body: "", + headers: { 'Set-Cookie' => cookie_string } + ) + end - let(:cookie_name) { 'name_of_the_cookie' } - let(:cookie_value) { 'value_of_the_cookie' } - let(:cookie_string) { "#{cookie_name}=#{cookie_value}" } + describe 'success' do - it 'successfully gets the cookie' do - EM.run { - http = EventMachine::HttpRequest.new('http://example.org').get + context 'with only one cookie' do - http.errback { fail(http.error) } - http.callback { - expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) - EM.stop + let(:cookie_name) { 'name_of_the_cookie' } + let(:cookie_value) { 'value_of_the_cookie' } + let(:cookie_string) { "#{cookie_name}=#{cookie_value}" } + + it 'successfully gets the cookie' do + EM.run { + http = EventMachine::HttpRequest.new('http://example.org').get + + http.errback { fail(http.error) } + http.callback { + expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) + EM.stop + } } - } + end end - end - context 'with several cookies' do + context 'with several cookies' do - let(:cookie_name) { 'name_of_the_cookie' } - let(:cookie_value) { 'value_of_the_cookie' } - let(:cookie_2_name) { 'name_of_the_2nd_cookie' } - let(:cookie_2_value) { 'value_of_the_2nd_cookie' } - let(:cookie_string) { %W(#{cookie_name}=#{cookie_value} #{cookie_2_name}=#{cookie_2_value}) } + let(:cookie_name) { 'name_of_the_cookie' } + let(:cookie_value) { 'value_of_the_cookie' } + let(:cookie_2_name) { 'name_of_the_2nd_cookie' } + let(:cookie_2_value) { 'value_of_the_2nd_cookie' } + let(:cookie_string) { %W(#{cookie_name}=#{cookie_value} #{cookie_2_name}=#{cookie_2_value}) } - it 'successfully gets both cookies' do - EM.run { - http = EventMachine::HttpRequest.new('http://example.org').get + it 'successfully gets both cookies' do + EM.run { + http = EventMachine::HttpRequest.new('http://example.org').get - http.errback { fail(http.error) } - http.callback { - expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) - expect(http.get_response_cookie(cookie_2_name)).to eq(cookie_2_value) - EM.stop + http.errback { fail(http.error) } + http.callback { + expect(http.get_response_cookie(cookie_name)).to eq(cookie_value) + expect(http.get_response_cookie(cookie_2_name)).to eq(cookie_2_value) + EM.stop + } } - } + end end end - end - describe 'failure' do + describe 'failure' do - let(:cookie_string) { 'a=b' } + let(:cookie_string) { 'a=b' } - it 'returns nil when no cookie is found' do - EM.run { - http = EventMachine::HttpRequest.new('http://example.org').get + it 'returns nil when no cookie is found' do + EM.run { + http = EventMachine::HttpRequest.new('http://example.org').get - http.errback { fail(http.error) } - http.callback { - expect(http.get_response_cookie('not_found_cookie')).to eq(nil) - EM.stop + http.errback { fail(http.error) } + http.callback { + expect(http.get_response_cookie('not_found_cookie')).to eq(nil) + EM.stop + } } - } + end end end end + end end From 4fd657ac77527f687f6eea5e9da601fd464d100d Mon Sep 17 00:00:00 2001 From: Christian Schmidt Date: Sun, 22 Sep 2024 22:35:45 +0200 Subject: [PATCH 4/4] Whitespace --- spec/spec_helper.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9c7e1f23..97ff95f2 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,6 +1,5 @@ require 'rubygems' require 'httpclient' - unless RUBY_PLATFORM =~ /java/ require 'curb' require 'patron'