require 'spec_helper' describe MinatoRubyApiClient::ApiClient do [:config, :default_headers].each do |method| it { should respond_to method } end let(:url) { 'https://example.com/v1' } let(:api_client) do uri = URI.parse(url) described_class.default.tap do |api_client| api_client.config.host = uri.host end end describe '.default' do it 'memoizes the instance of MinatoRubyApiClient::ApiClient in subsequent calls' do expect(described_class).to receive(:new).once.and_call_original 2.times { described_class.default } end it 'returns a instance of MinatoRubyApiClient::ApiClient' do expect(described_class.default).to be_a MinatoRubyApiClient::ApiClient end end describe '#build_request_body' do let(:form_params) { { foo: 'bar', bar: 'foobar' } } let(:body) { { foo: 'bar', bar: 'foobar' } } before do @data = api_client.build_request_body(header_params, form_params, body) end context 'when content type is form-url-encoded' do let(:header_params) { { 'Content-Type' => 'application/x-www-form-urlencoded' } } it 'returns data in url encoded format' do expect(@data).to eq('foo=bar&bar=foobar') end end context 'when content type is multipar/form-data' do let(:header_params) { { 'Content-Type' => 'multipart/form-data' } } it 'returns data in hash format' do expect(@data).to eq(form_params) end end context 'when body is a string' do let(:header_params) { { 'Content-Type' => 'text/plain' } } let(:body) { 'body is a string' } it 'returns data in string format' do expect(@data).to eq(body) end end context 'when body is a hash' do let(:header_params) { { 'Content-Type' => 'application/json' } } it 'returns data in json format' do expect(@data).to eq(body.to_json) end end context 'when no body and form content types is passed' do let(:header_params) { { 'Content-Type' => 'application/json' } } let(:body) { nil } it 'returns a null data' do expect(@data).to be_nil end end end describe '#json_mime?' do context 'when content type is json' do it 'returns true' do expect(api_client.json_mime?('application/json')).to be true end end context 'when content type is not json' do it 'returns false' do expect(api_client.json_mime?('application/xml')).to be false end end end describe '#call_api' do let(:opts) { { body: { name: 'Foo Bar' }, auth_names: 'apikey', return_type: 'Object' } } let(:http_response) do { status: 200, headers: { 'Context-type': 'application/json' }, body: { message: 'success' }.to_json } end before do stub_request(:post, /test/) .to_return(http_response) allow(api_client.config).to receive(:logger).and_return(Logger.new(STDOUT)) api_client.config.logger.level = Logger::WARN end context 'when request is successful' do it 'returns data, status and headers' do data, status_code, headers = api_client.call_api(:post, '/test', opts) expect(data).to eq({ message: 'success' }) expect(status_code).to eq(200) end it 'returns nil when return_type is not defined' do opts[:return_type] = nil data, status_code, headers = api_client.call_api(:post, '/test', opts) expect(data).to be_nil end end context 'when request failed' do let(:http_response) do { status: [400, 'Bad Request'], headers: { 'Context-type': 'application/json' }, body: { message: 'Dados inválidos' }.to_json } end it 'raise an error MinatoRubyApiClient::ApiError' do expect { api_client.call_api(:post, '/test', opts) }.to raise_error( an_instance_of(MinatoRubyApiClient::ApiError) .and having_attributes(message: 'An error occurred while communicating with the API.', caused_by: 'Bad Request', status_code: 400)) end end context 'when request timeout' do before { stub_request(:get, /test/).to_raise(Faraday::TimeoutError) } it 'raise an error caused by timeout' do expect { api_client.call_api(:get, '/test', opts) }.to raise_error { |error| expect(error).to be_a(MinatoRubyApiClient::ApiError) expect(error.caused_by).to be_a(Faraday::TimeoutError) } end end end describe 'trace' do let(:config) { MinatoRubyApiClient::Configuration.new } let(:api_client) { described_class.new(config) } context 'when trace is enabled and Rails is defined' do it 'add distributed trace middleware to middleware stack' do allow(Object).to receive(:const_defined?).with(:Rails).and_return(true) allow(Rails.env).to receive(:production?).and_return(true) allow(Minato::Trace).to receive(:enabled?).and_return(true) expect(api_client.config.instance_variable_get('@middlewares')).to include([Minato::Trace::Middleware::DistributedTraceContext]) end end context 'when trace is disabled' do it 'should not add ditributed trace middleware' do allow(Object).to receive(:const_defined?).with(:Rails).and_return(true) allow(Rails.env).to receive(:production?).and_return(true) allow(Minato::Trace).to receive(:enabled?).and_return(false) expect(api_client.config.instance_variable_get('@middlewares')).to eq [] end end context 'when Rails is not defined' do it 'should not add ditributed trace middleware' do allow(Object).to receive(:const_defined?).with(:Rails).and_return(false) allow(Minato::Trace).to receive(:enabled?).and_return(true) expect(api_client.config.instance_variable_get('@middlewares')).to eq [] end end context 'when Rails is defined but is not in productio mode' do it 'should not add ditributed trace middleware' do allow(Object).to receive(:const_defined?).with(:Rails).and_return(true) allow(Rails.env).to receive(:production?).and_return(false) allow(Minato::Trace).to receive(:enabled?).and_return(true) expect(api_client.config.instance_variable_get('@middlewares')).to eq [] end end end describe '#convert_to_type' do context 'when data is nil' do it 'returns nil' do expect(api_client.convert_to_type(nil, 'String')).to be_nil end end context 'when return type is String' do it 'returns string' do expect(api_client.convert_to_type(1, 'String')).to be_a(String) end end context 'when return type is Integer' do it 'returns integer' do expect(api_client.convert_to_type('1', 'Integer')).to be_a(Integer) end end context 'when return type is Float' do it 'returns float' do expect(api_client.convert_to_type('1', 'Float')).to be_a(Float) end end context 'when return type is Boolean' do it 'returns boolean' do expect(api_client.convert_to_type(true, 'Boolean')).to be_truthy end end context 'when return type is Time' do it 'returns time' do expect(api_client.convert_to_type('12:00:00', 'Time')).to be_a(Time) end end context 'when return type is Date' do it 'returns time' do expect(api_client.convert_to_type('2020-12-25 12:00:00', 'Date')).to be_a(Date) end end context 'when return type is Object' do it 'returns data' do data = { id: 1 } expect(api_client.convert_to_type(data, 'Object')).to equal(data) end end context 'when return type is Array' do it 'returns array list of type specified' do data = [1, 2, 3] expect(api_client.convert_to_type(data, 'Array')).to eq(['1', '2', '3']) end end context 'when return type is Hash' do it 'returns hash with key:value of types specifieds' do data = { id: '22', qtd: '12' } expect(api_client.convert_to_type(data, 'Hash')).to eq({ 'id': 22, 'qtd': 12 }) end end context 'when return type is a custom type as a class' do it 'returns a object from the class' do klass = double('Pet', name: 'Bob') allow(MinatoRubyApiClient).to receive(:const_get).with('Pet').and_return(klass) allow(klass).to receive(:build_from_hash).and_return(true) expect(api_client.convert_to_type({ name: 'Bob' }, 'Pet')).to be_truthy end end end describe '#update_params_for_auth!' do let(:header_params) { {} } let(:query_params) { {} } let(:config) { MinatoRubyApiClient::Configuration.new } let(:api_client) { described_class.new(config) } context 'when auth method is bearer auth' do it 'add bearer auth in header' do api_client.config.access_token = 'access_token' api_client.update_params_for_auth!(header_params, query_params, ['bearer_auth']) expect(header_params).to eq({ 'Authorization' => 'Bearer access_token' }) end end context 'when auth method is basic auth' do it 'add basic auth in header' do api_client.config.username = 'user' api_client.config.password = 'pass' basic_auth = Base64.encode64("user:pass").chomp.delete("\n") api_client.update_params_for_auth!(header_params, query_params, ['basic_auth']) expect(header_params).to eq({ 'Authorization' => "Basic #{basic_auth}" }) end end context 'when auth method is apikey' do context 'when api_key prefix is not defined' do it 'add apikey auth in header without prefix' do api_client.config.api_key['api_key'] = 'key' api_client.update_params_for_auth!(header_params, query_params, ['apikey']) expect(header_params).to eq({ 'api_key' => 'key' }) end end context 'when api_key prefix is defined' do it 'add apikey auth in header with prefix' do api_client.config.api_key['api_key'] = 'key' api_client.config.api_key_prefix['api_key'] = 'prefix' api_client.update_params_for_auth!(header_params, query_params, ['apikey']) expect(header_params).to eq({ 'api_key' => 'prefix key' }) end end end end end