diff --git a/lib/vend/exception.rb b/lib/vend/exception.rb index 1714d47..c11d344 100644 --- a/lib/vend/exception.rb +++ b/lib/vend/exception.rb @@ -8,6 +8,7 @@ def initialize(headers) class BadRequest < HttpError; end class Unauthorized < HttpError; end + class PaymentRequired < HttpError; end class Forbidden < HttpError; end class NotFound < HttpError; end class MethodNotAllowed < HttpError; end @@ -25,6 +26,7 @@ module HttpErrors ERRORS = { 400 => Vend::BadRequest, 401 => Vend::Unauthorized, + 402 => Vend::PaymentRequired, 403 => Vend::Forbidden, 404 => Vend::NotFound, 405 => Vend::MethodNotAllowed, @@ -40,8 +42,21 @@ module HttpErrors }.freeze def throw_http_exception!(code, env) - return unless ERRORS.keys.include? code + return if (200..299).include?(code.to_i) + + response_headers = extract_headers_for_error(env) + error_class = error_class_by(code) + + raise error_class.new(response_headers), env.body + end + + def error_class_by(code) + ERRORS.fetch(code, ::Vend::HttpError) + end + + def extract_headers_for_error(env) response_headers = {} + unless env.body.empty? response_headers = begin Oj.load(env.body, symbol_keys: true) @@ -49,10 +64,12 @@ def throw_http_exception!(code, env) {} end end + unless env[:response_headers] && env[:response_headers]['X-Retry-After'].nil? response_headers[:retry_after] = env[:response_headers]['X-Retry-After'].to_i end - raise ERRORS[code].new(response_headers), env.body + + response_headers end end end diff --git a/spec/vend/unit/exception_spec.rb b/spec/vend/unit/exception_spec.rb index 617c84b..01ac7db 100644 --- a/spec/vend/unit/exception_spec.rb +++ b/spec/vend/unit/exception_spec.rb @@ -1,6 +1,5 @@ RSpec.describe Vend::HttpErrors do let(:dummy_class) { Class.new { extend Vend::HttpErrors } } - let(:code) { 200 } let(:env) { double } let(:body) { {} } let(:headers) { {} } @@ -14,18 +13,73 @@ expect(Vend::HttpErrors::ERRORS).not_to be_nil end - context 'invalid response status' do - context 'when we get a 404' do + context 'when received a successful response' do + it 'does not throw an exception' do + (200..299).each do |code| + expect { dummy_class.throw_http_exception!(code, env) }.not_to raise_exception + end + end + end + + context 'when response code is 0' do + subject { dummy_class.throw_http_exception!(0, env) } + + let(:body) { 'Request timeout' } + + it 'throws the default exception' do + expect { subject }.to raise_exception(Vend::HttpError, body) + end + end + + context 'when response code is not successful' do + context 'when response code is mapped to a known exception class' do + let(:unknown_error_codes) { [*100..199] + [*300..599] - Vend::HttpErrors::ERRORS.keys } + + let(:body) { 'Something web wrong' } + + it 'throws the default exception' do + expect(unknown_error_codes.count).to be_positive + + unknown_error_codes.each do |code| + expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::HttpError, body) + end + end + end + + context 'when received code 402' do + let(:code) { 402 } + let(:body) do + <<~HTML + + 402 Payment Required + +

402 Payment Required

+
openresty
+ + + HTML + end + + it 'throws a corresponding exception' do + expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::PaymentRequired, body) + end + end + + context 'when received code 404' do let(:code) { 404 } - it 'should throw an exception' do - expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::HttpErrors::ERRORS[code]) + it 'throws a corresponding exception' do + expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::NotFound) end end - it 'should have a valid error' do - Vend::HttpErrors::ERRORS.keys.each do |code| - expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::HttpErrors::ERRORS[code]) + context 'when received a known code' do + let(:body) { '{ "error": "something went wrong" }' } + + it 'throws a corresponding exception class' do + Vend::HttpErrors::ERRORS.keys.each do |code| + expect { dummy_class.throw_http_exception!(code, env) }.to raise_exception(Vend::HttpErrors::ERRORS[code], body) + end end end @@ -56,12 +110,4 @@ end end end - - context 'valid response status' do - let(:code) { 200 } - - it 'should not throw an exception' do - expect { dummy_class.throw_http_exception!(code, env) }.to_not raise_exception - end - end end