From a55c7a259488c9f8727d3b14d77271f73ded0640 Mon Sep 17 00:00:00 2001 From: Scott Nelson Date: Sun, 1 May 2016 20:16:25 -0400 Subject: [PATCH] Include Rack::Response::Helpers in responses --- lib/airborne.rb | 1 + lib/airborne/base.rb | 34 +++++++++------- lib/airborne/response.rb | 19 +++++++++ lib/airborne/rest_client_requester.rb | 37 +++++++++-------- spec/airborne/client_requester_spec.rb | 24 +++++------ spec/airborne/rack/rack_sinatra_spec.rb | 15 ++++--- spec/airborne/response_spec.rb | 53 +++++++++++++++++++++++++ 7 files changed, 130 insertions(+), 53 deletions(-) create mode 100644 lib/airborne/response.rb create mode 100644 spec/airborne/response_spec.rb diff --git a/lib/airborne.rb b/lib/airborne.rb index 719b443..78e7e99 100644 --- a/lib/airborne.rb +++ b/lib/airborne.rb @@ -16,6 +16,7 @@ config.add_setting :rack_app config.add_setting :requester_type config.add_setting :requester_module + config.add_setting :rest_client_options, default: {} config.before do |example| config.match_expected = example.metadata[:match_expected].nil? ? Airborne.configuration.match_expected_default? : example.metadata[:match_expected] diff --git a/lib/airborne/base.rb b/lib/airborne/base.rb index 7f900eb..18b8bab 100644 --- a/lib/airborne/base.rb +++ b/lib/airborne/base.rb @@ -1,13 +1,15 @@ require 'json' require 'active_support' require 'active_support/core_ext/hash/indifferent_access' +require 'active_support/core_ext/module/delegation' +require 'airborne/response' module Airborne class InvalidJsonError < StandardError; end include RequestExpectations - attr_reader :response, :headers, :body + attr_reader :headers, :body def self.configure RSpec.configure do |config| @@ -15,20 +17,24 @@ def self.configure end end - def self.included(base) - if !Airborne.configuration.requester_module.nil? - base.send(:include, Airborne.configuration.requester_module) - elsif !Airborne.configuration.rack_app.nil? - base.send(:include, RackTestRequester) - else - base.send(:include, RestClientRequester) - end - end - def self.configuration RSpec.configuration end + def requester + Class.new(self.class) do + if !Airborne.configuration.requester_module.nil? + include Airborne.configuration.requester_module + elsif !Airborne.configuration.rack_app.nil? + include RackTestRequester + else + include RestClientRequester + end + end.new + end + + delegate :make_request, to: :requester + def get(url, headers = nil) @response = make_request(:get, url, headers: headers) end @@ -58,15 +64,15 @@ def options(url, headers = nil) end def response - @response + Response.new(@response) end def headers - HashWithIndifferentAccess.new(response.headers) + HashWithIndifferentAccess.new(@response.headers) end def body - response.body + @response.body end def json_body diff --git a/lib/airborne/response.rb b/lib/airborne/response.rb new file mode 100644 index 0000000..1685f06 --- /dev/null +++ b/lib/airborne/response.rb @@ -0,0 +1,19 @@ +require 'rack/response' + +module Airborne + class Response < Struct.new(:raw) + include Rack::Response::Helpers + + alias_method :success?, :successful? + alias_method :missing?, :not_found? + alias_method :error?, :server_error? + + def status + raw.code + end + + def method_missing(name, *args, &block) + raw.send(name, *args, &block) + end + end +end diff --git a/lib/airborne/rest_client_requester.rb b/lib/airborne/rest_client_requester.rb index 3ebe68f..fb9a098 100644 --- a/lib/airborne/rest_client_requester.rb +++ b/lib/airborne/rest_client_requester.rb @@ -3,29 +3,34 @@ module Airborne module RestClientRequester def make_request(method, url, options = {}) - headers = base_headers.merge(options[:headers] || {}) - res = if method == :post || method == :patch || method == :put - begin - request_body = options[:body].nil? ? '' : options[:body] - request_body = request_body.to_json if options[:body].is_a?(Hash) - RestClient.send(method, get_url(url), request_body, headers) - rescue RestClient::Exception => e - e.response - end - else - begin - RestClient.send(method, get_url(url), headers) - rescue RestClient::Exception => e - e.response - end + params = base_params.merge( + method: method, + url: get_url(url), + headers: base_headers.merge(options[:headers] || {})) + + if include_body?(method) + request_body = options[:body].nil? ? '' : options[:body] + request_body = request_body.to_json if options[:body].is_a?(Hash) + params[:payload] = request_body end - res + + RestClient::Request.execute(params) + rescue RestClient::Exception => e + e.response end private + def include_body?(method) + [:post, :patch, :put].include?(method) + end + def base_headers { content_type: :json }.merge(Airborne.configuration.headers || {}) end + + def base_params + Airborne.configuration.rest_client_options || {} + end end end diff --git a/spec/airborne/client_requester_spec.rb b/spec/airborne/client_requester_spec.rb index 3023801..fa5302c 100644 --- a/spec/airborne/client_requester_spec.rb +++ b/spec/airborne/client_requester_spec.rb @@ -1,36 +1,30 @@ require 'spec_helper' describe 'client requester' do - before do - allow(RestClient).to receive(:send) - RSpec::Mocks.space.proxy_for(self).remove_stub_if_present(:get) - end - after do - allow(RestClient).to receive(:send).and_call_original Airborne.configure { |config| config.headers = {} } end it 'should set :content_type to :json by default' do - get '/foo' + stub_request(:get, 'http://www.example.com/foo') + .with(headers: { 'Content-Type' => 'application/json' }) - expect(RestClient).to have_received(:send) - .with(:get, 'http://www.example.com/foo', { content_type: :json }) + get '/foo' end it 'should override headers with option[:headers]' do - get '/foo', { content_type: 'application/x-www-form-urlencoded' } + stub_request(:get, 'http://www.example.com/foo') + .with(headers: { 'Content-Type' => 'application/x-www-form-urlencoded' }) - expect(RestClient).to have_received(:send) - .with(:get, 'http://www.example.com/foo', { content_type: 'application/x-www-form-urlencoded' }) + get '/foo', { content_type: 'application/x-www-form-urlencoded' } end it 'should override headers with airborne config headers' do Airborne.configure { |config| config.headers = { content_type: 'text/plain' } } - get '/foo' + stub_request(:get, 'http://www.example.com/foo') + .with(headers: { 'Content-Type' => 'text/plain' }) - expect(RestClient).to have_received(:send) - .with(:get, 'http://www.example.com/foo', { content_type: 'text/plain' }) + get '/foo' end end diff --git a/spec/airborne/rack/rack_sinatra_spec.rb b/spec/airborne/rack/rack_sinatra_spec.rb index f32235e..2507c3f 100644 --- a/spec/airborne/rack/rack_sinatra_spec.rb +++ b/spec/airborne/rack/rack_sinatra_spec.rb @@ -11,11 +11,12 @@ class SampleApp < Sinatra::Application end end -Airborne.configure do |config| - config.rack_app = SampleApp -end +TestResponse = Struct.new(:body, :headers) describe 'rack app' do + before { Airborne.configuration.rack_app = SampleApp } + after { Airborne.configuration.rack_app = nil } + it 'should allow requests against a sinatra app' do get '/' expect_json_types(foo: :string) @@ -27,17 +28,15 @@ class SampleApp < Sinatra::Application end it 'Should set json_body even when not using the airborne http requests' do - Response = Struct.new(:body, :headers) - @response = Response.new({ foo: 'bar' }.to_json) + @response = TestResponse.new({ foo: 'bar' }.to_json) expect(json_body).to eq(foo: 'bar') end it 'Should work with consecutive requests' do - Response = Struct.new(:body, :headers) - @response = Response.new({ foo: 'bar' }.to_json) + @response = TestResponse.new({ foo: 'bar' }.to_json) expect(json_body).to eq(foo: 'bar') - @response = Response.new({ foo: 'boo' }.to_json) + @response = TestResponse.new({ foo: 'boo' }.to_json) expect(json_body).to eq(foo: 'boo') end end diff --git a/spec/airborne/response_spec.rb b/spec/airborne/response_spec.rb new file mode 100644 index 0000000..8b08d96 --- /dev/null +++ b/spec/airborne/response_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe 'response' do + let(:headers) { {} } + let(:status) { 200 } + + before { Airborne.configuration.rest_client_options = { max_redirects: 0 } } + after { Airborne.configuration.rest_client_options = {} } + + before do + mock_get('simple_get', headers, status) + get '/simple_get' + end + + context 'with 200 status' do + let(:status) { 200 } + + it { expect(response.status).to eq 200 } + it { expect(response).to be_successful } + it { expect(response).to be_success } + it { expect(response).to be_ok } + end + + context 'with 404 status' do + let(:status) { 404 } + + it { expect(response.status).to eq 404 } + it { expect(response).to_not be_success } + it { expect(response).to_not be_ok } + it { expect(response).to be_client_error } + it { expect(response).to be_not_found } + it { expect(response).to be_missing } + end + + context 'with 302 status' do + let(:status) { 302 } + let(:headers) { { 'Location' => '/' } } + + it { expect(response.status).to eq 302 } + it { expect(response).to be_redirection } + it { expect(response).to be_redirect } + end + + context 'with 500 status' do + let(:status) { 500 } + + it { expect(response.status).to eq 500 } + it { expect(response).to_not be_success } + it { expect(response).to_not be_ok } + it { expect(response).to be_server_error } + it { expect(response).to be_error } + end +end