From b910c065b2a08197227eef6ad91dc9ad007f9a0e Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 27 Jan 2018 15:17:34 +0000 Subject: [PATCH 01/46] Adds RSpec minimal config Adds bin folder with console and setup commands --- .gitignore | 2 +- .rspec | 3 ++ Gemfile | 1 + bin/console | 14 ++++++ bin/setup | 8 ++++ spec/faraday_spec.rb | 5 ++ spec/spec_helper.rb | 109 +++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 141 insertions(+), 1 deletion(-) create mode 100644 .rspec create mode 100755 bin/console create mode 100755 bin/setup create mode 100644 spec/faraday_spec.rb create mode 100644 spec/spec_helper.rb diff --git a/.gitignore b/.gitignore index a938783be..1d00bea36 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,9 @@ pkg/* tmp .rvmrc .ruby-version +.yardoc ## BUNDLER -bin *.gem .bundle Gemfile.lock diff --git a/.rspec b/.rspec new file mode 100644 index 000000000..bb697427e --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--require spec_helper +--format documentation +--color \ No newline at end of file diff --git a/Gemfile b/Gemfile index cbcddee4e..b208f7d2d 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,7 @@ group :test do gem 'simplecov' gem 'sinatra', '~> 1.3' gem 'typhoeus', '~> 1.3', :require => 'typhoeus' + gem 'rspec', '~> 3.7' end gemspec diff --git a/bin/console b/bin/console new file mode 100755 index 000000000..bf430ea37 --- /dev/null +++ b/bin/console @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby + +require 'bundler/setup' +require 'faraday' + +# You can add fixtures and/or initialization code here to make experimenting +# with your gem easier. You can also use a different console, if you like. + +# (If you use this, don't forget to add pry to your Gemfile!) +# require "pry" +# Pry.start + +require 'irb' +IRB.start(__FILE__) \ No newline at end of file diff --git a/bin/setup b/bin/setup new file mode 100755 index 000000000..b64275cc4 --- /dev/null +++ b/bin/setup @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail +IFS=$'\n\t' +set -vx + +bundle install + +# Do any other automated setup that you need to do here \ No newline at end of file diff --git a/spec/faraday_spec.rb b/spec/faraday_spec.rb new file mode 100644 index 000000000..2b9fc81f8 --- /dev/null +++ b/spec/faraday_spec.rb @@ -0,0 +1,5 @@ +RSpec.describe Faraday do + it 'has a version number' do + expect(Faraday::VERSION).not_to be nil + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..64fbf89f5 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,109 @@ +# This file was generated by the `rspec --init` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause +# this file to always be loaded, without a need to explicitly require it in any +# files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need +# it. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration + +require 'simplecov' +require 'coveralls' + +SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] + +SimpleCov.start do + minimum_coverage 90 + minimum_coverage_by_file 70 +end + +require 'faraday' + +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + + # This allows you to limit a spec run to individual examples or groups + # you care about by tagging them with `:focus` metadata. When nothing + # is tagged with `:focus`, all examples get run. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + # config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + # config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + # config.disable_monkey_patching! + + # This setting enables warnings. It's recommended, but in some cases may + # be too noisy due to issues in dependencies. + # config.warnings = true + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + # if config.files_to_run.one? + # # Use the documentation formatter for detailed output, + # # unless a formatter has already been configured + # # (e.g. via a command-line flag). + # config.default_formatter = "doc" + # end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + # config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +end From dbd25882f341d9281beeb087c81fb9a7c36e591c Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 27 Jan 2018 15:54:30 +0000 Subject: [PATCH 02/46] Faraday::Utils specs --- spec/faraday/utils_spec.rb | 65 ++++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 4 +++ spec/support/helper_methods.rb | 17 +++++++++ 3 files changed, 86 insertions(+) create mode 100644 spec/faraday/utils_spec.rb create mode 100644 spec/support/helper_methods.rb diff --git a/spec/faraday/utils_spec.rb b/spec/faraday/utils_spec.rb new file mode 100644 index 000000000..db1bfe346 --- /dev/null +++ b/spec/faraday/utils_spec.rb @@ -0,0 +1,65 @@ +RSpec.describe Faraday::Utils do + describe 'headers parsing' do + let(:multi_response_headers) { + "HTTP/1.x 500 OK\r\nContent-Type: text/html; charset=UTF-8\r\n" \ + "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n" + } + + it 'parse headers for aggregated responses' do + headers = Faraday::Utils::Headers.new + headers.parse(multi_response_headers) + + result = headers.to_hash + + expect(result["Content-Type"]).to eq("application/json; charset=UTF-8") + end + end + + describe 'URI parsing' do + let(:url) { "http://example.com/abc" } + + # emulates ActiveSupport::SafeBuffer#gsub + FakeSafeBuffer = Struct.new(:string) do + def to_s; self end + def gsub(regex) + string.gsub(regex) { + match, = $&, '' =~ /a/ + yield(match) + } + end + end + + it 'escapes safe buffer' do + str = FakeSafeBuffer.new('$32,000.00') + expect(Faraday::Utils.escape(str)).to eq('%2432%2C000.00') + end + + it 'parses with default parser' do + with_default_uri_parser(nil) do + uri = normalize(url) + expect(uri.host).to eq('example.com') + end + end + + it 'parses with URI' do + with_default_uri_parser(::URI) do + uri = normalize(url) + expect(uri.host).to eq('example.com') + end + end + + it 'parses with block' do + with_default_uri_parser(lambda {|u| "booya#{"!" * u.size}" }) do + expect(normalize(url)).to eq('booya!!!!!!!!!!!!!!!!!!!!!!') + end + end + + it 'replaces headers hash' do + headers = Faraday::Utils::Headers.new('authorization' => 't0ps3cr3t!') + expect(headers).to have_key('authorization') + + headers.replace({'content-type' => 'text/plain'}) + expect(headers).not_to have_key('authorization') + end + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 64fbf89f5..9ee2592fd 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,6 +26,8 @@ require 'faraday' +Dir['./spec/support/**/*.rb'].each { |f| require f } + RSpec.configure do |config| # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest @@ -106,4 +108,6 @@ # test failures related to randomization by passing the same `--seed` value # as the one that triggered the failure. Kernel.srand config.seed + + config.include Faraday::HelperMethods end diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb new file mode 100644 index 000000000..e2f25cea5 --- /dev/null +++ b/spec/support/helper_methods.rb @@ -0,0 +1,17 @@ +module Faraday + module HelperMethods + def normalize(url) + Faraday::Utils::URI(url) + end + + def with_default_uri_parser(parser) + old_parser = Faraday::Utils.default_uri_parser + begin + Faraday::Utils.default_uri_parser = parser + yield + ensure + Faraday::Utils.default_uri_parser = old_parser + end + end + end +end \ No newline at end of file From e45b970d67157afddc5acf82a6f5c6cba08d6577 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 27 Jan 2018 16:31:43 +0000 Subject: [PATCH 03/46] Adds specs for Faraday::Response::Middleware and Faraday::Response::RaiseError --- spec/faraday/response/middleware_spec.rb | 50 +++++++++++++++++++++++ spec/faraday/response/raise_error_spec.rb | 30 ++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 spec/faraday/response/middleware_spec.rb create mode 100644 spec/faraday/response/raise_error_spec.rb diff --git a/spec/faraday/response/middleware_spec.rb b/spec/faraday/response/middleware_spec.rb new file mode 100644 index 000000000..c041d217e --- /dev/null +++ b/spec/faraday/response/middleware_spec.rb @@ -0,0 +1,50 @@ +RSpec.describe Faraday::Response::Middleware do + let(:conn) do + Faraday.new do |b| + b.use custom_middleware + b.adapter :test do |stub| + stub.get('ok') { [200, { 'Content-Type' => 'text/html' }, ''] } + stub.get('not_modified') { [304, nil, nil] } + stub.get('no_content') { [204, nil, nil] } + end + end + end + + context 'with a custom ResponseMiddleware' do + let(:custom_middleware) do + Class.new(Faraday::Response::Middleware) do + def parse(body) + body.upcase + end + end + end + + it 'parses the response' do + expect(conn.get('ok').body).to eq('') + end + end + + context 'with a custom ResponseMiddleware but empty response' do + let(:custom_middleware) do + Class.new(Faraday::Response::Middleware) do + def parse(body) + raise 'this should not be called' + end + end + end + + it 'raises exception for 200 responses' do + expect { conn.get('ok') }.to raise_error(StandardError) + end + + it 'doesn\'t call the middleware for 204 responses' do + expect_any_instance_of(custom_middleware).not_to receive(:parse) + expect(conn.get('no_content').body).to be_nil + end + + it 'doesn\'t call the middleware for 304 responses' do + expect_any_instance_of(custom_middleware).not_to receive(:parse) + expect(conn.get('not_modified').body).to be_nil + end + end +end \ No newline at end of file diff --git a/spec/faraday/response/raise_error_spec.rb b/spec/faraday/response/raise_error_spec.rb new file mode 100644 index 000000000..448a179c5 --- /dev/null +++ b/spec/faraday/response/raise_error_spec.rb @@ -0,0 +1,30 @@ +RSpec.describe Faraday::Response::RaiseError do + let(:conn) do + Faraday.new do |b| + b.response :raise_error + b.adapter :test do |stub| + stub.get('ok') { [200, {'Content-Type' => 'text/html'}, ''] } + stub.get('not-found') { [404, {'X-Reason' => 'because'}, 'keep looking'] } + stub.get('error') { [500, {'X-Error' => 'bailout'}, 'fail'] } + end + end + end + + it 'raises no exceptio for 200 responses' do + expect { conn.get('ok') }.not_to raise_error + end + + it 'raise Faraday::Error::ResourceNotFound for 404 responses' do + expect { conn.get('not-found') }.to raise_error(Faraday::Error::ResourceNotFound) do |ex| + expect(ex.message).to eq('the server responded with status 404') + expect(ex.response[:headers]['X-Reason']).to eq('because') + end + end + + it 'raise Faraday::Error::ClientError for 500 responses' do + expect { conn.get('error') }.to raise_error(Faraday::Error::ClientError) do |ex| + expect(ex.message).to eq('the server responded with status 500') + expect(ex.response[:headers]['X-Error']).to eq('bailout') + end + end +end From fcc2f494de196ef18575f66ba6d862e3c236426d Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 27 Jan 2018 17:23:40 +0000 Subject: [PATCH 04/46] Adds specs for Faraday::Response::Logger --- spec/faraday/response/logger_spec.rb | 137 +++++++++++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 spec/faraday/response/logger_spec.rb diff --git a/spec/faraday/response/logger_spec.rb b/spec/faraday/response/logger_spec.rb new file mode 100644 index 000000000..19b978e56 --- /dev/null +++ b/spec/faraday/response/logger_spec.rb @@ -0,0 +1,137 @@ +require 'stringio' +require 'logger' + +RSpec.describe Faraday::Response::Logger do + let(:string_io) { StringIO.new } + let(:logger) { Logger.new(string_io) } + let(:logger_options) { {} } + let(:conn) do + rubbles = ['Barney', 'Betty', 'Bam Bam'] + + Faraday.new do |b| + b.response :logger, logger, logger_options do |logger| + logger.filter(/(soylent green is) (.+)/, '\1 tasty') + logger.filter(/(api_key:).*"(.+)."/, '\1[API_KEY]') + logger.filter(/(password)=(.+)/, '\1=[HIDDEN]') + end + b.adapter :test do |stubs| + stubs.get('/hello') { [200, { 'Content-Type' => 'text/html' }, 'hello'] } + stubs.post('/ohai') { [200, { 'Content-Type' => 'text/html' }, 'fred'] } + stubs.post('/ohyes') { [200, { 'Content-Type' => 'text/html' }, 'pebbles'] } + stubs.get('/rubbles') { [200, { 'Content-Type' => 'application/json' }, rubbles] } + stubs.get('/filtered_body') { [200, { 'Content-Type' => 'text/html' }, 'soylent green is people'] } + stubs.get('/filtered_headers') { [200, { 'Content-Type' => 'text/html' }, 'headers response'] } + stubs.get('/filtered_params') { [200, { 'Content-Type' => 'text/html' }, 'params response'] } + stubs.get('/filtered_url') { [200, { 'Content-Type' => 'text/html' }, 'url response'] } + end + end + end + + before do + logger.level = Logger::DEBUG + end + + it 'still returns output' do + resp = conn.get '/hello', nil, accept: 'text/html' + expect(resp.body).to eq('hello') + end + + it 'logs method and url' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match("get http:/hello") + end + + it 'logs request headers by default' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Accept: "text/html)) + end + + it 'logs response headers by default' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).to match(%(Content-Type: "text/html)) + end + + it 'does not log request body by default' do + conn.post '/ohai', 'name=Unagi', accept: 'text/html' + expect(string_io.string).not_to match(%(name=Unagi)) + end + + it 'does not log response body by default' do + conn.post '/ohai', 'name=Toro', accept: 'text/html' + expect(string_io.string).not_to match(%(fred)) + end + + it 'logs filter headers' do + conn.headers = { 'api_key' => 'ABC123' } + conn.get '/filtered_headers', nil, accept: 'text/html' + expect(string_io.string).to match(%(api_key:)) + expect(string_io.string).to match(%([API_KEY])) + expect(string_io.string).not_to match(%(ABC123)) + end + + it 'logs filter url' do + conn.get '/filtered_url?password=hunter2', nil, accept: 'text/html' + expect(string_io.string).to match(%([HIDDEN])) + expect(string_io.string).not_to match(%(hunter2)) + end + + context 'when not logging request headers' do + let(:logger_options) { { headers: { request: false } } } + + it 'does not log request headers if option is false' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Accept: "text/html)) + end + end + + context 'when not logging response headers' do + let(:logger_options) { { headers: { response: false } } } + + it 'does log response headers if option is false' do + conn.get '/hello', nil, accept: 'text/html' + expect(string_io.string).not_to match(%(Content-Type: "text/html)) + end + end + + context 'when logging request body' do + let(:logger_options) { { bodies: { request: true } } } + + it 'log only request body' do + conn.post '/ohyes', 'name=Tamago', accept: 'text/html' + expect(string_io.string).to match(%(name=Tamago)) + expect(string_io.string).not_to match(%(pebbles)) + end + end + + context 'when logging response body' do + let(:logger_options) { { bodies: { response: true } } } + + it 'log only response body' do + conn.post '/ohyes', 'name=Hamachi', accept: 'text/html' + expect(string_io.string).to match(%(pebbles)) + expect(string_io.string).not_to match(%(name=Hamachi)) + end + end + + context 'when logging request and response bodies' do + let(:logger_options) { { bodies: true } } + + it 'log request and response body' do + conn.post '/ohyes', 'name=Ebi', accept: 'text/html' + expect(string_io.string).to match(%(name=Ebi)) + expect(string_io.string).to match(%(pebbles)) + end + + it 'log response body object' do + conn.get '/rubbles', nil, accept: 'text/html' + expect(string_io.string).to match(%([\"Barney\", \"Betty\", \"Bam Bam\"]\n)) + end + + it 'logs filter body' do + conn.get '/filtered_body', nil, accept: 'text/html' + expect(string_io.string).to match(%(soylent green is)) + expect(string_io.string).to match(%(tasty)) + expect(string_io.string).not_to match(%(people)) + end + end +end From 6842a8ebfd8c3d4fa45b6759d91b72856e8c07e4 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 27 Jan 2018 17:27:48 +0000 Subject: [PATCH 05/46] Removes minitests that have been turned into specs --- test/adapters/logger_test.rb | 131 ------------------------------- test/response_middleware_test.rb | 72 ----------------- test/strawberry.rb | 2 - test/utils_test.rb | 98 ----------------------- 4 files changed, 303 deletions(-) delete mode 100644 test/adapters/logger_test.rb delete mode 100644 test/response_middleware_test.rb delete mode 100644 test/strawberry.rb delete mode 100644 test/utils_test.rb diff --git a/test/adapters/logger_test.rb b/test/adapters/logger_test.rb deleted file mode 100644 index cddaed31b..000000000 --- a/test/adapters/logger_test.rb +++ /dev/null @@ -1,131 +0,0 @@ -require File.expand_path('../../helper', __FILE__) -require 'stringio' -require 'logger' - -module Adapters - class LoggerTest < Faraday::TestCase - def conn(logger, logger_options={}) - rubbles = ['Barney', 'Betty', 'Bam Bam'] - - Faraday.new do |b| - b.response :logger, @logger, logger_options do | logger | - logger.filter(/(soylent green is) (.+)/,'\1 tasty') - logger.filter(/(api_key:).*"(.+)."/,'\1[API_KEY]') - logger.filter(/(password)=(.+)/,'\1=[HIDDEN]') - end - b.adapter :test do |stubs| - stubs.get('/hello') { [200, {'Content-Type' => 'text/html'}, 'hello'] } - stubs.post('/ohai') { [200, {'Content-Type' => 'text/html'}, 'fred'] } - stubs.post('/ohyes') { [200, {'Content-Type' => 'text/html'}, 'pebbles'] } - stubs.get('/rubbles') { [200, {'Content-Type' => 'application/json'}, rubbles] } - stubs.get('/filtered_body') { [200, {'Content-Type' => 'text/html'}, 'soylent green is people'] } - stubs.get('/filtered_headers') { [200, {'Content-Type' => 'text/html'}, 'headers response'] } - stubs.get('/filtered_params') { [200, {'Content-Type' => 'text/html'}, 'params response'] } - stubs.get('/filtered_url') { [200, {'Content-Type' => 'text/html'}, 'url response'] } - end - end - end - - def setup - @io = StringIO.new - @logger = Logger.new(@io) - @logger.level = Logger::DEBUG - - @conn = conn(@logger) - end - - def test_still_returns_output - resp = @conn.get '/hello', nil, :accept => 'text/html' - assert_equal 'hello', resp.body - end - - def test_logs_method_and_url - @conn.get '/hello', nil, :accept => 'text/html' - assert_match "get http:/hello", @io.string - end - - def test_logs_request_headers_by_default - @conn.get '/hello', nil, :accept => 'text/html' - assert_match %(Accept: "text/html), @io.string - end - - def test_logs_response_headers_by_default - @conn.get '/hello', nil, :accept => 'text/html' - assert_match %(Content-Type: "text/html), @io.string - end - - def test_does_not_log_request_headers_if_option_is_false - app = conn(@logger, :headers => { :request => false }) - app.get '/hello', nil, :accept => 'text/html' - refute_match %(Accept: "text/html), @io.string - end - - def test_does_log_response_headers_if_option_is_false - app = conn(@logger, :headers => { :response => false }) - app.get '/hello', nil, :accept => 'text/html' - refute_match %(Content-Type: "text/html), @io.string - end - - def test_does_not_log_request_body_by_default - @conn.post '/ohai', 'name=Unagi', :accept => 'text/html' - refute_match %(name=Unagi), @io.string - end - - def test_does_not_log_response_body_by_default - @conn.post '/ohai', 'name=Toro', :accept => 'text/html' - refute_match %(fred), @io.string - end - - def test_log_only_request_body - app = conn(@logger, :bodies => { :request => true }) - app.post '/ohyes', 'name=Tamago', :accept => 'text/html' - assert_match %(name=Tamago), @io.string - refute_match %(pebbles), @io.string - end - - def test_log_only_response_body - app = conn(@logger, :bodies => { :response => true }) - app.post '/ohyes', 'name=Hamachi', :accept => 'text/html' - assert_match %(pebbles), @io.string - refute_match %(name=Hamachi), @io.string - end - - def test_log_request_and_response_body - app = conn(@logger, :bodies => true) - app.post '/ohyes', 'name=Ebi', :accept => 'text/html' - assert_match %(name=Ebi), @io.string - assert_match %(pebbles), @io.string - end - - def test_log_response_body_object - app = conn(@logger, :bodies => true) - app.get '/rubbles', nil, :accept => 'text/html' - assert_match %([\"Barney\", \"Betty\", \"Bam Bam\"]\n), @io.string - end - - def test_logs_filter_body - app = conn(@logger, :bodies => true) - app.get '/filtered_body', nil, :accept => 'text/html' - assert_match %(soylent green is), @io.string - assert_match %(tasty), @io.string - refute_match %(people), @io.string - end - - def test_logs_filter_headers - app = conn(@logger) - app.headers = {'api_key' => 'ABC123'} - app.get '/filtered_headers', nil, :accept => 'text/html' - assert_match %(api_key:), @io.string - assert_match %([API_KEY]), @io.string - refute_match %(ABC123), @io.string - end - - def test_logs_filter_url - app = conn(@logger) - app.get '/filtered_url?password=hunter2', nil, :accept => 'text/html' - assert_match %(password=[HIDDEN]), @io.string - refute_match %(hunter2), @io.string - end - - end -end diff --git a/test/response_middleware_test.rb b/test/response_middleware_test.rb deleted file mode 100644 index c22215ee6..000000000 --- a/test/response_middleware_test.rb +++ /dev/null @@ -1,72 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class ResponseMiddlewareTest < Faraday::TestCase - def setup - @conn = Faraday.new do |b| - b.response :raise_error - b.adapter :test do |stub| - stub.get('ok') { [200, {'Content-Type' => 'text/html'}, ''] } - stub.get('not-found') { [404, {'X-Reason' => 'because'}, 'keep looking'] } - stub.get('error') { [500, {'X-Error' => 'bailout'}, 'fail'] } - end - end - end - - class ResponseUpcaser < Faraday::Response::Middleware - def parse(body) - body.upcase - end - end - - def test_success - assert @conn.get('ok') - end - - def test_raises_not_found - error = assert_raises Faraday::Error::ResourceNotFound do - @conn.get('not-found') - end - assert_equal 'the server responded with status 404', error.message - assert_equal 'because', error.response[:headers]['X-Reason'] - end - - def test_raises_error - error = assert_raises Faraday::Error::ClientError do - @conn.get('error') - end - assert_equal 'the server responded with status 500', error.message - assert_equal 'bailout', error.response[:headers]['X-Error'] - end - - def test_upcase - @conn.builder.insert(0, ResponseUpcaser) - assert_equal '', @conn.get('ok').body - end -end - -class ResponseNoBodyMiddleWareTest < Faraday::TestCase - def setup - @conn = Faraday.new do |b| - b.response :raise_error - b.adapter :test do |stub| - stub.get('not_modified') { [304, nil, nil] } - stub.get('no_content') { [204, nil, nil] } - end - end - @conn.builder.insert(0, NotCalled) - end - - class NotCalled < Faraday::Response::Middleware - def parse(body) - raise "this should not be called" - end - end - - def test_204 - assert_nil @conn.get('no_content').body - end - - def test_304 - assert_nil @conn.get('not_modified').body - end -end diff --git a/test/strawberry.rb b/test/strawberry.rb deleted file mode 100644 index 80c8d0b34..000000000 --- a/test/strawberry.rb +++ /dev/null @@ -1,2 +0,0 @@ -class MiddlewareStackTest::Strawberry < MiddlewareStackTest::Handler -end diff --git a/test/utils_test.rb b/test/utils_test.rb deleted file mode 100644 index 1b116c5f9..000000000 --- a/test/utils_test.rb +++ /dev/null @@ -1,98 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class TestUtils < Faraday::TestCase - - # Headers parsing - - def test_headers - "HTTP/1.x 500 OK\r\nContent-Type: text/html; charset=UTF-8\r\n" \ - "HTTP/1.x 200 OK\r\nContent-Type: application/json; charset=UTF-8\r\n\r\n" - end - - def test_headers_parsing_for_aggregated_responses - headers = Faraday::Utils::Headers.new() - headers.parse(test_headers) - - result = headers.to_hash - - assert_equal result["Content-Type"], "application/json; charset=UTF-8" - end - - # URI parsing - - def setup - @url = "http://example.com/abc" - end - - # emulates ActiveSupport::SafeBuffer#gsub - FakeSafeBuffer = Struct.new(:string) do - def to_s() self end - def gsub(regex) - string.gsub(regex) { - match, = $&, '' =~ /a/ - yield(match) - } - end - end - - def test_escaping_safe_buffer - str = FakeSafeBuffer.new('$32,000.00') - assert_equal '%2432%2C000.00', Faraday::Utils.escape(str) - end - - def test_parses_with_default - with_default_uri_parser(nil) do - uri = normalize(@url) - assert_equal 'example.com', uri.host - end - end - - def test_parses_with_URI - with_default_uri_parser(::URI) do - uri = normalize(@url) - assert_equal 'example.com', uri.host - end - end - - def test_parses_with_block - with_default_uri_parser(lambda {|u| "booya#{"!" * u.size}" }) do - assert_equal 'booya!!!!!!!!!!!!!!!!!!!!!!', normalize(@url) - end - end - - def test_replace_header_hash - headers = Faraday::Utils::Headers.new('authorization' => 't0ps3cr3t!') - assert headers.include?('authorization') - - headers.replace({'content-type' => 'text/plain'}) - - assert !headers.include?('authorization') - end - - def normalize(url) - Faraday::Utils::URI(url) - end - - def with_default_uri_parser(parser) - old_parser = Faraday::Utils.default_uri_parser - begin - Faraday::Utils.default_uri_parser = parser - yield - ensure - Faraday::Utils.default_uri_parser = old_parser - end - end - - # YAML parsing - - def test_headers_yaml_roundtrip - headers = Faraday::Utils::Headers.new('User-Agent' => 'safari', 'Content-type' => 'text/html') - result = YAML.load(headers.to_yaml) - - assert result.include?('user-agent'), 'Unable to hydrate to a correct Headers' - assert result.include?('content-type'), 'Unable to hydrate to a correct Headers' - assert result['user-agent'] == 'safari', 'Unable to access rehydrated Headers' - assert result['content-type'] == 'text/html', 'Unable to access rehydrated Headers' - end -end - From 2f550e6f6c62064dc5c7fb87c9a1ee1cb70db556 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Mon, 29 Jan 2018 22:35:55 +0000 Subject: [PATCH 06/46] Puts back `strawberry.rb` file as it's used by some test... --- test/strawberry.rb | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 test/strawberry.rb diff --git a/test/strawberry.rb b/test/strawberry.rb new file mode 100644 index 000000000..80c8d0b34 --- /dev/null +++ b/test/strawberry.rb @@ -0,0 +1,2 @@ +class MiddlewareStackTest::Strawberry < MiddlewareStackTest::Handler +end From 72ae7cae0a3245b975de5660554a8caf8974f494 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 10 Feb 2018 13:58:14 +0000 Subject: [PATCH 07/46] Converts request_middleware_test into rspec --- spec/faraday/request/multipart_spec.rb | 56 ++++++++++ spec/faraday/request/url_encoded_spec.rb | 68 ++++++++++++ spec/faraday/utils_spec.rb | 11 -- spec/support/fake_safe_buffer.rb | 10 ++ spec/support/helper_methods.rb | 10 ++ test/request_middleware_test.rb | 126 ----------------------- 6 files changed, 144 insertions(+), 137 deletions(-) create mode 100644 spec/faraday/request/multipart_spec.rb create mode 100644 spec/faraday/request/url_encoded_spec.rb create mode 100644 spec/support/fake_safe_buffer.rb delete mode 100644 test/request_middleware_test.rb diff --git a/spec/faraday/request/multipart_spec.rb b/spec/faraday/request/multipart_spec.rb new file mode 100644 index 000000000..ac1337fd1 --- /dev/null +++ b/spec/faraday/request/multipart_spec.rb @@ -0,0 +1,56 @@ +Faraday::CompositeReadIO.class_eval { attr_reader :ios } + +RSpec.describe Faraday::Request::Multipart do + let(:conn) do + Faraday.new do |b| + b.request :multipart + b.request :url_encoded + b.adapter :test do |stub| + stub.post('/echo') do |env| + posted_as = env[:request_headers]['Content-Type'] + [200, { 'Content-Type' => posted_as }, env[:body]] + end + end + end + end + + shared_examples 'a multipart request' do + it 'forms a multipart request' do + response = conn.post('/echo', payload) + + expect(response.body).to be_a_kind_of(Faraday::CompositeReadIO) + match = "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX + expect(response.headers['Content-Type']).to start_with(match) + + response.body.send(:ios).map { |io| io.read }.each do |io| + re = regexes.detect { |r| io =~ r } + regexes.delete(re) if re + end + expect(regexes).to eq([]) + end + + it 'generates a unique boundary for each request' do + response1 = conn.post('/echo', payload) + response2 = conn.post('/echo', payload) + expect(response1.headers['Content-Type']).not_to eq(response2.headers['Content-Type']) + end + end + + context 'when multipart objects in param' do + let(:regexes) { [/name\=\"a\"/, + /name=\"b\[c\]\"\; filename\=\"multipart_spec\.rb\"/, + /name=\"b\[d\]\"/] } + + let(:payload) { { :a => 1, :b => { :c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2 } } } + it_behaves_like 'a multipart request' + end + + context 'when multipart objects in array param' do + let(:regexes) { [/name\=\"a\"/, + /name=\"b\[\]\[c\]\"\; filename\=\"multipart_spec\.rb\"/, + /name=\"b\[\]\[d\]\"/] } + + let(:payload) { { :a => 1, :b => [{ :c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2 }] } } + it_behaves_like 'a multipart request' + end +end \ No newline at end of file diff --git a/spec/faraday/request/url_encoded_spec.rb b/spec/faraday/request/url_encoded_spec.rb new file mode 100644 index 000000000..14f12c5e1 --- /dev/null +++ b/spec/faraday/request/url_encoded_spec.rb @@ -0,0 +1,68 @@ +RSpec.describe Faraday::Request::UrlEncoded do + let(:conn) do + Faraday.new do |b| + b.request :multipart + b.request :url_encoded + b.adapter :test do |stub| + stub.post('/echo') do |env| + posted_as = env[:request_headers]['Content-Type'] + [200, {'Content-Type' => posted_as}, env[:body]] + end + end + end + end + + it 'does nothing without payload' do + response = conn.post('/echo') + expect(response.headers['Content-Type']).to be_nil + expect(response.body.empty?).to be_truthy + end + + it 'ignores custom content type' do + response = conn.post('/echo', { :some => 'data' }, 'content-type' => 'application/x-foo') + expect(response.headers['Content-Type']).to eq('application/x-foo') + expect(response.body).to eq({ :some => 'data' }) + end + + it 'works with no headers' do + response = conn.post('/echo', { :fruit => %w[apples oranges] }) + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expect(response.body).to eq('fruit%5B%5D=apples&fruit%5B%5D=oranges') + end + + it 'works with with headers' do + response = conn.post('/echo', {'a'=>123}, 'content-type' => 'application/x-www-form-urlencoded') + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expect(response.body).to eq('a=123') + end + + it 'works with nested params' do + response = conn.post('/echo', { :user => {:name => 'Mislav', :web => 'mislav.net'} }) + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expected = { 'user' => {'name' => 'Mislav', 'web' => 'mislav.net'} } + expect(Faraday::Utils.parse_nested_query(response.body)).to eq(expected) + end + + it 'works with non nested params' do + response = conn.post('/echo', { :dimensions => ['date', 'location']}) do |req| + req.options.params_encoder = Faraday::FlatParamsEncoder + end + expect(response.headers['Content-Type']).to eq('application/x-www-form-urlencoded') + expected = { 'dimensions' => ['date', 'location'] } + expect(Faraday::Utils.parse_query(response.body)).to eq(expected) + expect(response.body).to eq('dimensions=date&dimensions=location') + end + + it 'works with unicode' do + err = capture_warnings { + response = conn.post('/echo', {:str => "eé cç aã aâ"}) + expect(response.body).to eq('str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2') + } + expect(err.empty?).to be_truthy + end + + it 'works with nested keys' do + response = conn.post('/echo', {'a'=>{'b'=>{'c'=>['d']}}}) + expect(response.body).to eq("a%5Bb%5D%5Bc%5D%5B%5D=d") + end +end \ No newline at end of file diff --git a/spec/faraday/utils_spec.rb b/spec/faraday/utils_spec.rb index db1bfe346..ae058b6c3 100644 --- a/spec/faraday/utils_spec.rb +++ b/spec/faraday/utils_spec.rb @@ -18,17 +18,6 @@ describe 'URI parsing' do let(:url) { "http://example.com/abc" } - # emulates ActiveSupport::SafeBuffer#gsub - FakeSafeBuffer = Struct.new(:string) do - def to_s; self end - def gsub(regex) - string.gsub(regex) { - match, = $&, '' =~ /a/ - yield(match) - } - end - end - it 'escapes safe buffer' do str = FakeSafeBuffer.new('$32,000.00') expect(Faraday::Utils.escape(str)).to eq('%2432%2C000.00') diff --git a/spec/support/fake_safe_buffer.rb b/spec/support/fake_safe_buffer.rb new file mode 100644 index 000000000..fc7b365a2 --- /dev/null +++ b/spec/support/fake_safe_buffer.rb @@ -0,0 +1,10 @@ +# emulates ActiveSupport::SafeBuffer#gsub +FakeSafeBuffer = Struct.new(:string) do + def to_s; self end + def gsub(regex) + string.gsub(regex) { + match, = $&, '' =~ /a/ + yield(match) + } + end +end \ No newline at end of file diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index e2f25cea5..26e1d1c07 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -13,5 +13,15 @@ def with_default_uri_parser(parser) Faraday::Utils.default_uri_parser = old_parser end end + + def capture_warnings + old, $stderr = $stderr, StringIO.new + begin + yield + $stderr.string + ensure + $stderr = old + end + end end end \ No newline at end of file diff --git a/test/request_middleware_test.rb b/test/request_middleware_test.rb deleted file mode 100644 index e3b99470c..000000000 --- a/test/request_middleware_test.rb +++ /dev/null @@ -1,126 +0,0 @@ -# encoding: utf-8 -require File.expand_path('../helper', __FILE__) - -Faraday::CompositeReadIO.class_eval { attr_reader :ios } - -class RequestMiddlewareTest < Faraday::TestCase - def conn - Faraday.new do |b| - b.request :multipart - b.request :url_encoded - b.adapter :test do |stub| - stub.post('/echo') do |env| - posted_as = env[:request_headers]['Content-Type'] - [200, {'Content-Type' => posted_as}, env[:body]] - end - end - end - end - - def test_does_nothing_without_payload - response = conn.post('/echo') - assert_nil response.headers['Content-Type'] - assert response.body.empty? - end - - def test_ignores_custom_content_type - response = conn.post('/echo', { :some => 'data' }, 'content-type' => 'application/x-foo') - assert_equal 'application/x-foo', response.headers['Content-Type'] - assert_equal({ :some => 'data' }, response.body) - end - - def test_url_encoded_no_header - response = conn.post('/echo', { :fruit => %w[apples oranges] }) - assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] - assert_equal 'fruit%5B%5D=apples&fruit%5B%5D=oranges', response.body - end - - def test_url_encoded_with_header - response = conn.post('/echo', {'a'=>123}, 'content-type' => 'application/x-www-form-urlencoded') - assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] - assert_equal 'a=123', response.body - end - - def test_url_encoded_nested - response = conn.post('/echo', { :user => {:name => 'Mislav', :web => 'mislav.net'} }) - assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] - expected = { 'user' => {'name' => 'Mislav', 'web' => 'mislav.net'} } - assert_equal expected, Faraday::Utils.parse_nested_query(response.body) - end - - def test_url_encoded_non_nested - response = conn.post('/echo', { :dimensions => ['date', 'location']}) do |req| - req.options.params_encoder = Faraday::FlatParamsEncoder - end - assert_equal 'application/x-www-form-urlencoded', response.headers['Content-Type'] - expected = { 'dimensions' => ['date', 'location'] } - assert_equal expected, Faraday::Utils.parse_query(response.body) - assert_equal 'dimensions=date&dimensions=location', response.body - end - - def test_url_encoded_unicode - err = capture_warnings { - response = conn.post('/echo', {:str => "eé cç aã aâ"}) - assert_equal "str=e%C3%A9+c%C3%A7+a%C3%A3+a%C3%A2", response.body - } - assert err.empty?, "stderr did include: #{err}" - end - - def test_url_encoded_nested_keys - response = conn.post('/echo', {'a'=>{'b'=>{'c'=>['d']}}}) - assert_equal "a%5Bb%5D%5Bc%5D%5B%5D=d", response.body - end - - def test_multipart - # assume params are out of order - regexes = [ - /name\=\"a\"/, - /name=\"b\[c\]\"\; filename\=\"request_middleware_test\.rb\"/, - /name=\"b\[d\]\"/] - - payload = {:a => 1, :b => {:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}} - response = conn.post('/echo', payload) - - assert_kind_of Faraday::CompositeReadIO, response.body - assert response.headers['Content-Type'].start_with?( - "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX, - ) - - response.body.send(:ios).map{|io| io.read}.each do |io| - if re = regexes.detect { |r| io =~ r } - regexes.delete re - end - end - assert_equal [], regexes - end - - def test_multipart_with_arrays - # assume params are out of order - regexes = [ - /name\=\"a\"/, - /name=\"b\[\]\[c\]\"\; filename\=\"request_middleware_test\.rb\"/, - /name=\"b\[\]\[d\]\"/] - - payload = {:a => 1, :b =>[{:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}]} - response = conn.post('/echo', payload) - - assert_kind_of Faraday::CompositeReadIO, response.body - assert response.headers['Content-Type'].start_with?( - "multipart/form-data; boundary=%s" % Faraday::Request::Multipart::DEFAULT_BOUNDARY_PREFIX, - ) - - response.body.send(:ios).map{|io| io.read}.each do |io| - if re = regexes.detect { |r| io =~ r } - regexes.delete re - end - end - assert_equal [], regexes - end - - def test_multipart_unique_boundary - payload = {:a => 1, :b =>[{:c => Faraday::UploadIO.new(__FILE__, 'text/x-ruby'), :d => 2}]} - response1 = conn.post('/echo', payload) - response2 = conn.post('/echo', payload) - assert response1.headers['Content-Type'] != response2.headers['Content-Type'] - end -end From 223066708554191cf430fdc8b232c03b39708fb6 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sat, 10 Feb 2018 15:29:29 +0000 Subject: [PATCH 08/46] Converts parameters_test into rspec --- spec/faraday/params_encoders/flat_spec.rb | 11 ++ spec/faraday/params_encoders/nested_spec.rb | 104 +++++++++++++ .../support/shared_examples/params_encoder.rb | 16 ++ test/parameters_test.rb | 141 ------------------ 4 files changed, 131 insertions(+), 141 deletions(-) create mode 100644 spec/faraday/params_encoders/flat_spec.rb create mode 100644 spec/faraday/params_encoders/nested_spec.rb create mode 100644 spec/support/shared_examples/params_encoder.rb delete mode 100644 test/parameters_test.rb diff --git a/spec/faraday/params_encoders/flat_spec.rb b/spec/faraday/params_encoders/flat_spec.rb new file mode 100644 index 000000000..3c33937bc --- /dev/null +++ b/spec/faraday/params_encoders/flat_spec.rb @@ -0,0 +1,11 @@ +require 'rack/utils' + +RSpec.describe Faraday::FlatParamsEncoder do + it_behaves_like 'a params encoder' + + it 'decodes arrays' do + query = 'a=one&a=two&a=three' + expected = {'a' => %w(one two three) } + expect(subject.decode(query)).to eq(expected) + end +end \ No newline at end of file diff --git a/spec/faraday/params_encoders/nested_spec.rb b/spec/faraday/params_encoders/nested_spec.rb new file mode 100644 index 000000000..72d17da9e --- /dev/null +++ b/spec/faraday/params_encoders/nested_spec.rb @@ -0,0 +1,104 @@ +require 'rack/utils' + +RSpec.describe Faraday::NestedParamsEncoder do + it_behaves_like 'a params encoder' + + it 'decodes arrays' do + query = 'a[1]=one&a[2]=two&a[3]=three' + expected = { 'a' => %w(one two three) } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes hashes' do + query = 'a[b1]=one&a[b2]=two&a[b][c]=foo' + expected = { "a" => { "b1" => "one", "b2" => "two", "b" => { "c" => "foo" } } } + expect(subject.decode(query)).to eq(expected) + end + + it 'decodes nested arrays rack compat' do + query = 'a[][one]=1&a[][two]=2&a[][one]=3&a[][two]=4' + expected = Rack::Utils.parse_nested_query(query) + expect(subject.decode(query)).to eq(expected) + end + + it 'decode_nested_array_mixed_types' do + query = 'a[][one]=1&a[]=2&a[]=&a[]' + expected = Rack::Utils.parse_nested_query(query) + expect(subject.decode(query)).to eq(expected) + end + + it 'decode_nested_ignores_invalid_array' do + query = '[][a]=1&b=2' + expected = { "a" => "1", "b" => "2" } + expect(subject.decode(query)).to eq(expected) + end + + it 'decode_nested_ignores_repeated_array_notation' do + query = 'a[][][]=1' + expected = { "a" => ["1"] } + expect(subject.decode(query)).to eq(expected) + end + + it 'decode_nested_ignores_malformed_keys' do + query = '=1&[]=2' + expected = {} + expect(subject.decode(query)).to eq(expected) + end + + it 'decode_nested_subkeys_dont_have_to_be_in_brackets' do + query = 'a[b]c[d]e=1' + expected = { "a" => { "b" => { "c" => { "d" => { "e" => "1" } } } } } + expect(subject.decode(query)).to eq(expected) + end + + it 'decode_nested_final_value_overrides_any_type' do + query = 'a[b][c]=1&a[b]=2' + expected = { "a" => { "b" => "2" } } + expect(subject.decode(query)).to eq(expected) + end + + it 'encode_rack_compat' do + params = { :a => [{ :one => "1", :two => "2" }, "3", ""] } + result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&') + expected = Rack::Utils.build_nested_query(params).split('&') + expect(result).to match_array(expected) + end + + shared_examples 'a wrong decoding' do + it do + expect { subject.decode(query) }.to raise_error(TypeError) do |e| + expect(e.message).to eq(error_message) + end + end + end + + context 'when expecting hash but getting string' do + let(:query) { 'a=1&a[b]=2' } + let(:error_message) { "expected Hash (got String) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting hash but getting array' do + let(:query) { 'a[]=1&a[b]=2' } + let(:error_message) { "expected Hash (got Array) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting nested hash but getting non nested' do + let(:query) { 'a[b]=1&a[b][c]=2' } + let(:error_message) { "expected Hash (got String) for param `b'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting array but getting hash' do + let(:query) { 'a[b]=1&a[]=2' } + let(:error_message) { "expected Array (got Hash) for param `a'" } + it_behaves_like 'a wrong decoding' + end + + context 'when expecting array but getting string' do + let(:query) { 'a=1&a[]=2' } + let(:error_message) { "expected Array (got String) for param `a'" } + it_behaves_like 'a wrong decoding' + end +end \ No newline at end of file diff --git a/spec/support/shared_examples/params_encoder.rb b/spec/support/shared_examples/params_encoder.rb new file mode 100644 index 000000000..784625c95 --- /dev/null +++ b/spec/support/shared_examples/params_encoder.rb @@ -0,0 +1,16 @@ +shared_examples 'a params encoder' do + it 'escapes safe buffer' do + monies = FakeSafeBuffer.new('$32,000.00') + expect(subject.encode('a' => monies)).to eq('a=%2432%2C000.00') + end + + it 'raises type error for empty string' do + expect { subject.encode('') }.to raise_error(TypeError) do |error| + expect(error.message).to eq("Can't convert String into Hash.") + end + end + + it 'encodes nil' do + expect(subject.encode('a' => nil)).to eq('a') + end +end \ No newline at end of file diff --git a/test/parameters_test.rb b/test/parameters_test.rb deleted file mode 100644 index 9b32d5d0b..000000000 --- a/test/parameters_test.rb +++ /dev/null @@ -1,141 +0,0 @@ -require File.expand_path("../helper", __FILE__) -require "rack/utils" - -class TestParameters < Faraday::TestCase - # emulates ActiveSupport::SafeBuffer#gsub - FakeSafeBuffer = Struct.new(:string) do - def to_s() self end - def gsub(regex) - string.gsub(regex) { - match, = $&, '' =~ /a/ - yield(match) - } - end - end - - def test_escaping_safe_buffer_nested - monies = FakeSafeBuffer.new("$32,000.00") - assert_equal "a=%2432%2C000.00", Faraday::NestedParamsEncoder.encode("a" => monies) - end - - def test_escaping_safe_buffer_flat - monies = FakeSafeBuffer.new("$32,000.00") - assert_equal "a=%2432%2C000.00", Faraday::FlatParamsEncoder.encode("a" => monies) - end - - def test_raises_typeerror_nested - error = assert_raises TypeError do - Faraday::NestedParamsEncoder.encode("") - end - assert_equal "Can't convert String into Hash.", error.message - end - - def test_raises_typeerror_flat - error = assert_raises TypeError do - Faraday::FlatParamsEncoder.encode("") - end - assert_equal "Can't convert String into Hash.", error.message - end - - def test_decode_array_nested - query = "a[1]=one&a[2]=two&a[3]=three" - expected = {"a" => ["one", "two", "three"]} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_array_flat - query = "a=one&a=two&a=three" - expected = {"a" => ["one", "two", "three"]} - assert_equal expected, Faraday::FlatParamsEncoder.decode(query) - end - - def test_nested_decode_hash - query = "a[b1]=one&a[b2]=two&a[b][c]=foo" - expected = {"a" => {"b1" => "one", "b2" => "two", "b" => {"c" => "foo"}}} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_encode_nil_nested - assert_equal "a", Faraday::NestedParamsEncoder.encode("a" => nil) - end - - def test_encode_nil_flat - assert_equal "a", Faraday::FlatParamsEncoder.encode("a" => nil) - end - - def test_decode_nested_array_rack_compat - query = "a[][one]=1&a[][two]=2&a[][one]=3&a[][two]=4" - expected = Rack::Utils.parse_nested_query(query) - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_nested_array_mixed_types - query = "a[][one]=1&a[]=2&a[]=&a[]" - expected = Rack::Utils.parse_nested_query(query) - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_nested_ignores_invalid_array - query = "[][a]=1&b=2" - expected = {"a" => "1", "b" => "2"} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_nested_ignores_repeated_array_notation - query = "a[][][]=1" - expected = {"a" => ["1"]} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_nested_ignores_malformed_keys - query = "=1&[]=2" - expected = {} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_nested_subkeys_dont_have_to_be_in_brackets - query = "a[b]c[d]e=1" - expected = {"a" => {"b" => {"c" => {"d" => {"e" => "1"}}}}} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_decode_nested_raises_error_when_expecting_hash - error = assert_raises TypeError do - Faraday::NestedParamsEncoder.decode("a=1&a[b]=2") - end - assert_equal "expected Hash (got String) for param `a'", error.message - - error = assert_raises TypeError do - Faraday::NestedParamsEncoder.decode("a[]=1&a[b]=2") - end - assert_equal "expected Hash (got Array) for param `a'", error.message - - error = assert_raises TypeError do - Faraday::NestedParamsEncoder.decode("a[b]=1&a[]=2") - end - assert_equal "expected Array (got Hash) for param `a'", error.message - - error = assert_raises TypeError do - Faraday::NestedParamsEncoder.decode("a=1&a[]=2") - end - assert_equal "expected Array (got String) for param `a'", error.message - - error = assert_raises TypeError do - Faraday::NestedParamsEncoder.decode("a[b]=1&a[b][c]=2") - end - assert_equal "expected Hash (got String) for param `b'", error.message - end - - def test_decode_nested_final_value_overrides_any_type - query = "a[b][c]=1&a[b]=2" - expected = {"a" => {"b" => "2"}} - assert_equal expected, Faraday::NestedParamsEncoder.decode(query) - end - - def test_encode_rack_compat_nested - params = { :a => [{:one => "1", :two => "2"}, "3", ""] } - expected = Rack::Utils.build_nested_query(params) - assert_equal expected.split("&").sort, - Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split("&").sort - end -end From 46b16f5df2d5319d943a0f84fa792f3cb1f97c47 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sun, 11 Feb 2018 15:45:25 +0000 Subject: [PATCH 09/46] Converts options_test into rspec --- spec/faraday/options/env_spec.rb | 28 ++ spec/faraday/options/options_spec.rb | 284 ++++++++++++++++ spec/faraday/options/proxy_options_spec.rb | 33 ++ spec/faraday/options/request_options_spec.rb | 15 + test/options_test.rb | 333 ------------------- 5 files changed, 360 insertions(+), 333 deletions(-) create mode 100644 spec/faraday/options/env_spec.rb create mode 100644 spec/faraday/options/options_spec.rb create mode 100644 spec/faraday/options/proxy_options_spec.rb create mode 100644 spec/faraday/options/request_options_spec.rb delete mode 100644 test/options_test.rb diff --git a/spec/faraday/options/env_spec.rb b/spec/faraday/options/env_spec.rb new file mode 100644 index 000000000..c23286f98 --- /dev/null +++ b/spec/faraday/options/env_spec.rb @@ -0,0 +1,28 @@ +RSpec.describe Faraday::Env do + it 'allows to access members' do + e = Faraday::Env.new + expect(e.method).to be_nil + e.method = :get + expect(e.method).to eq(:get) + end + + it 'allows to access symbol non members' do + e = Faraday::Env.new + expect(e[:custom]).to be_nil + e[:custom] = :boom + expect(e[:custom]).to eq(:boom) + end + + it 'allows to access string non members' do + e = Faraday::Env.new + expect(e['custom']).to be_nil + e['custom'] = :boom + expect(e['custom']).to eq(:boom) + end + + it 'ignores false when fetching' do + ssl = Faraday::SSLOptions.new + ssl.verify = false + expect(ssl.fetch(:verify, true)).to be_falsey + end +end \ No newline at end of file diff --git a/spec/faraday/options/options_spec.rb b/spec/faraday/options/options_spec.rb new file mode 100644 index 000000000..545e33764 --- /dev/null +++ b/spec/faraday/options/options_spec.rb @@ -0,0 +1,284 @@ +RSpec.describe Faraday::Options do + SubOptions = Class.new(Faraday::Options.new(:sub_a, :sub_b)) + class ParentOptions < Faraday::Options.new(:a, :b, :c) + options c: SubOptions + end + + describe '#merge' do + it 'merges options with hashes' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.b).to be_nil + + dup = options.merge a: 2, b: 3 + expect(dup.a).to eq(2) + expect(dup.b).to eq(3) + expect(options.a).to eq(1) + expect(options.b).to be_nil + end + + it 'deeply merges two options' do + sub_opts1 = SubOptions.from(sub_a: 3) + sub_opts2 = SubOptions.from(sub_b: 4) + opt1 = ParentOptions.from(a: 1, c: sub_opts1) + opt2 = ParentOptions.from(b: 2, c: sub_opts2) + + merged = opt1.merge(opt2) + + expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) + expected = ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) + expect(merged).to eq(expected) + end + + it 'deeply merges options with hashes' do + sub_opts1 = SubOptions.from(sub_a: 3) + sub_opts2 = { sub_b: 4 } + opt1 = ParentOptions.from(a: 1, c: sub_opts1) + opt2 = { b: 2, c: sub_opts2 } + + merged = opt1.merge(opt2) + + expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) + expected = ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) + expect(merged).to eq(expected) + end + + it 'deeply merges options with nil' do + sub_opts = SubOptions.new(3, 4) + options = ParentOptions.new(1, 2, sub_opts) + expect(options.a).to eq(1) + expect(options.b).to eq(2) + expect(options.c.sub_a).to eq(3) + expect(options.c.sub_b).to eq(4) + + options2 = ParentOptions.from(b: 5, c: nil) + + merged = options.merge(options2) + + expect(merged.b).to eq(5) + expect(merged.c).to eq(sub_opts) + end + + it 'deeply merges options with options having nil sub-options' do + options = ParentOptions.from(a: 1) + + sub_opts = SubOptions.new(3, 4) + options2 = ParentOptions.from(b: 2, c: sub_opts) + + expect(options.a).to eq(1) + expect(options2.b).to eq(2) + expect(options2.c.sub_a).to eq(3) + expect(options2.c.sub_b).to eq(4) + + merged = options.merge(options2) + + expect(merged.c).to eq(sub_opts) + end + + describe '#dup' do + it 'duplicate options but not sub-options' do + sub_opts = SubOptions.from(sub_a: 3) + opts = ParentOptions.from(b: 1, c: sub_opts) + + duped = opts.dup + duped.b = 2 + duped.c.sub_a = 4 + + expect(opts.b).to eq(1) + expect(opts.c.sub_a).to eq(4) + end + end + + describe '#deep_dup' do + it 'duplicate options and also suboptions' do + sub_opts = SubOptions.from(sub_a: 3) + opts = ParentOptions.from(b: 1, c: sub_opts) + + duped = opts.deep_dup + duped.b = 2 + duped.c.sub_a = 4 + + expect(opts.b).to eq(1) + expect(opts.c.sub_a).to eq(3) + end + end + + describe '#clear' do + it 'clears the options' do + options = SubOptions.new(1) + expect(options.empty?).not_to be_truthy + options.clear + expect(options.empty?).to be_truthy + end + end + + + describe '#empty?' do + it 'returns true only if all options are nil' do + options = SubOptions.new + expect(options.empty?).to be_truthy + options.sub_a = 1 + expect(options.empty?).not_to be_truthy + options.delete(:sub_a) + expect(options.empty?).to be_truthy + end + end + + describe '#each_key' do + it 'allows to iterate through keys' do + options = ParentOptions.new(1, 2, 3) + enum = options.each_key + expect(enum.next.to_sym).to eq(:a) + expect(enum.next.to_sym).to eq(:b) + expect(enum.next.to_sym).to eq(:c) + end + end + + describe '#key?' do + it 'returns true if the key exists and is not nil' do + options = SubOptions.new + expect(options.key?(:sub_a)).not_to be_truthy + options.sub_a = 1 + expect(options.key?(:sub_a)).to be_truthy + end + end + + describe '#each_value' do + it 'allows to iterate through values' do + options = ParentOptions.new(1, 2, 3) + enum = options.each_value + expect(enum.next).to eq(1) + expect(enum.next).to eq(2) + expect(enum.next).to eq(3) + end + end + + describe '#value?' do + it 'returns true if any key has that value' do + options = SubOptions.new + expect(options.value?(1)).not_to be_truthy + options.sub_a = 1 + expect(options.value?(1)).to be_truthy + end + end + + describe '#update' do + it 'updates options from hashes' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.b).to be_nil + + updated = options.update :a => 2, :b => 3 + expect(options.a).to eq(2) + expect(options.b).to eq(3) + expect(updated).to eq(options) + end + end + + describe '#delete' do + it 'allows to remove value for key' do + options = ParentOptions.new(1) + expect(options.a).to eq(1) + expect(options.delete(:a)).to eq(1) + expect(options.a).to be_nil + end + end + + describe '#from' do + it { expect { ParentOptions.from invalid: 1 }.to raise_error(NoMethodError) } + + it 'works with options' do + options = ParentOptions.new(1) + + value = ParentOptions.from(options) + expect(value.a).to eq(1) + expect(value.b).to be_nil + end + + it 'works with options with sub object' do + sub = SubOptions.new(1) + options = ParentOptions.from a: 1, c: sub + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq(1) + end + + it 'works with hash' do + options = ParentOptions.from a: 1 + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + end + + it 'works with hash with sub object' do + options = ParentOptions.from a: 1, c: { sub_a: 1 } + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to eq(1) + expect(options.b).to be_nil + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq(1) + end + + it 'works with deep hash' do + hash = { b: 1 } + options = ParentOptions.from a: hash + expect(options.a[:b]).to eq(1) + + hash[:b] = 2 + expect(options.a[:b]).to eq(1) + + options.a[:b] = 3 + expect(hash[:b]).to eq(2) + expect(options.a[:b]).to eq(3) + end + + it 'works with nil' do + options = ParentOptions.from(nil) + expect(options).to be_a_kind_of(ParentOptions) + expect(options.a).to be_nil + expect(options.b).to be_nil + end + + it 'respects inheritance' do + subclass = Class.new(ParentOptions) + options = subclass.from(c: { sub_a: 'hello' }) + expect(options.c).to be_a_kind_of(SubOptions) + expect(options.c.sub_a).to eq('hello') + end + end + + describe '#fetch' do + subject { SubOptions.new } + + context 'when the fetched key has no value' do + it 'uses falsey default' do + expect(subject.fetch(:sub_a, false) { |k| :blah }).to be_falsey + end + + it 'accepts block' do + expect(subject.fetch(:sub_a) { |k| "yo #{k.inspect}" }).to eq('yo :sub_a') + end + + it 'needs a default if key is missing' do + expect { subject.fetch(:sub_a) }.to raise_error(Faraday::Options.fetch_error_class) + end + end + + context 'when the fetched key has a value' do + before do + subject.sub_a = 1 + end + + it 'grabs value' do + expect(subject.fetch(:sub_a, false) { |k| :blah }).to eq(1) + end + + it 'works with key' do + expect(subject.fetch(:sub_a)).to eq(1) + end + end + end + end +end \ No newline at end of file diff --git a/spec/faraday/options/proxy_options_spec.rb b/spec/faraday/options/proxy_options_spec.rb new file mode 100644 index 000000000..048f325bb --- /dev/null +++ b/spec/faraday/options/proxy_options_spec.rb @@ -0,0 +1,33 @@ +RSpec.describe Faraday::ProxyOptions do + describe '#from' do + it 'works with string' do + options = Faraday::ProxyOptions.from 'http://user:pass@example.org' + expect(options.user).to eq('user') + expect(options.password).to eq('pass') + expect(options.uri).to be_a_kind_of(URI) + expect(options.path).to eq('') + expect(options.port).to eq(80) + expect(options.host).to eq('example.org') + expect(options.scheme).to eq('http') + end + + it 'works with nil' do + options = Faraday::ProxyOptions.from nil + expect(options).to be_a_kind_of(Faraday::ProxyOptions) + end + + it 'works with no auth' do + proxy = Faraday::ProxyOptions.from 'http://example.org' + expect(proxy.user).to be_nil + expect(proxy.password).to be_nil + end + end + + it 'allows hash access' do + proxy = Faraday::ProxyOptions.from 'http://a%40b:pw%20d@example.org' + expect(proxy.user).to eq('a@b') + expect(proxy[:user]).to eq('a@b') + expect(proxy.password).to eq('pw d') + expect(proxy[:password]).to eq('pw d') + end +end \ No newline at end of file diff --git a/spec/faraday/options/request_options_spec.rb b/spec/faraday/options/request_options_spec.rb new file mode 100644 index 000000000..6c8e28616 --- /dev/null +++ b/spec/faraday/options/request_options_spec.rb @@ -0,0 +1,15 @@ +RSpec.describe Faraday::RequestOptions do + it 'allows to set the request proxy' do + options = Faraday::RequestOptions.new + expect(options.proxy).to be_nil + + expect { options[:proxy] = { booya: 1 } }.to raise_error(NoMethodError) + + options[:proxy] = { user: 'user' } + expect(options.proxy).to be_a_kind_of(Faraday::ProxyOptions) + expect(options.proxy.user).to eq('user') + + options.proxy = nil + expect(options.proxy).to be_nil + end +end \ No newline at end of file diff --git a/test/options_test.rb b/test/options_test.rb deleted file mode 100644 index f006213e8..000000000 --- a/test/options_test.rb +++ /dev/null @@ -1,333 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class OptionsTest < Faraday::TestCase - SubOptions = Class.new(Faraday::Options.new(:sub_a, :sub_b)) - class ParentOptions < Faraday::Options.new(:a, :b, :c) - options :c => SubOptions - end - - def test_deep_merge - sub_opts1 = SubOptions.from(sub_a: 3) - sub_opts2 = SubOptions.from(sub_b: 4) - opt1 = ParentOptions.from(a: 1, c: sub_opts1) - opt2 = ParentOptions.from(b: 2, c: sub_opts2) - - merged = opt1.merge(opt2) - - expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) - assert_equal merged, ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) - end - - def test_deep_merge_with_hash - sub_opts1 = SubOptions.from(sub_a: 3) - sub_opts2 = { sub_b: 4 } - opt1 = ParentOptions.from(a: 1, c: sub_opts1) - opt2 = { b: 2, c: sub_opts2 } - - merged = opt1.merge(opt2) - - expected_sub_opts = SubOptions.from(sub_a: 3, sub_b: 4) - assert_equal merged, ParentOptions.from(a: 1, b: 2, c: expected_sub_opts) - end - - def test_deep_merge_with_nil - sub_opts = SubOptions.new(3, 4) - options = ParentOptions.new(1, 2, sub_opts) - assert_equal options.a, 1 - assert_equal options.b, 2 - assert_equal options.c.sub_a, 3 - assert_equal options.c.sub_b, 4 - - options2 = ParentOptions.from(b: 5, c: nil) - - merged = options.merge(options2) - - assert_equal merged.b, 5 - assert_equal merged.c, sub_opts - end - - def test_deep_merge_with_sub_nil - options = ParentOptions.from(a: 1) - - sub_opts = SubOptions.new(3, 4) - options2 = ParentOptions.from(b: 2, c: sub_opts) - - assert_equal options.a, 1 - assert_equal options2.b, 2 - assert_equal options2.c.sub_a, 3 - assert_equal options2.c.sub_b, 4 - - merged = options.merge(options2) - - assert_equal merged.c, sub_opts - end - - def test_dup_is_shallow - sub_opts = SubOptions.from(sub_a: 3) - opts = ParentOptions.from(b: 1, c: sub_opts) - - duped = opts.dup - duped.b = 2 - duped.c.sub_a = 4 - - assert_equal opts.b, 1 - assert_equal opts.c.sub_a, 4 - end - - def test_deep_dup - sub_opts = SubOptions.from(sub_a: 3) - opts = ParentOptions.from(b: 1, c: sub_opts) - - duped = opts.deep_dup - duped.b = 2 - duped.c.sub_a = 4 - - assert_equal opts.b, 1 - assert_equal opts.c.sub_a, 3 - end - - def test_clear - options = SubOptions.new(1) - assert !options.empty? - assert options.clear - assert options.empty? - end - - def test_empty - options = SubOptions.new - assert options.empty? - options.sub_a = 1 - assert !options.empty? - options.delete(:sub_a) - assert options.empty? - end - - def test_each_key - options = ParentOptions.new(1, 2, 3) - enum = options.each_key - assert_equal enum.next.to_sym, :a - assert_equal enum.next.to_sym, :b - assert_equal enum.next.to_sym, :c - end - - def test_key? - options = SubOptions.new - assert !options.key?(:sub_a) - options.sub_a = 1 - assert options.key?(:sub_a) - end - - def test_each_value - options = ParentOptions.new(1, 2, 3) - enum = options.each_value - assert_equal enum.next, 1 - assert_equal enum.next, 2 - assert_equal enum.next, 3 - end - - def test_value? - options = SubOptions.new - assert !options.value?(1) - options.sub_a = 1 - assert options.value?(1) - end - - def test_request_proxy_setter - options = Faraday::RequestOptions.new - assert_nil options.proxy - - assert_raises NoMethodError do - options[:proxy] = {:booya => 1} - end - - options[:proxy] = {:user => 'user'} - assert_kind_of Faraday::ProxyOptions, options.proxy - assert_equal 'user', options.proxy.user - - options.proxy = nil - assert_nil options.proxy - end - - def test_proxy_options_from_string - options = Faraday::ProxyOptions.from 'http://user:pass@example.org' - assert_equal 'user', options.user - assert_equal 'pass', options.password - assert_kind_of URI, options.uri - assert_equal '', options.path - assert_equal 80, options.port - assert_equal 'example.org', options.host - assert_equal 'http', options.scheme - end - - def test_proxy_options_from_nil - options = Faraday::ProxyOptions.from nil - assert_kind_of Faraday::ProxyOptions, options - end - - def test_proxy_options_hash_access - proxy = Faraday::ProxyOptions.from 'http://a%40b:pw%20d@example.org' - assert_equal 'a@b', proxy[:user] - assert_equal 'a@b', proxy.user - assert_equal 'pw d', proxy[:password] - assert_equal 'pw d', proxy.password - end - - def test_proxy_options_no_auth - proxy = Faraday::ProxyOptions.from 'http://example.org' - assert_nil proxy.user - assert_nil proxy.password - end - - def test_from_options - options = ParentOptions.new(1) - - value = ParentOptions.from(options) - assert_equal 1, value.a - assert_nil value.b - end - - def test_from_options_with_sub_object - sub = SubOptions.new(1) - options = ParentOptions.from :a => 1, :c => sub - assert_kind_of ParentOptions, options - assert_equal 1, options.a - assert_nil options.b - assert_kind_of SubOptions, options.c - assert_equal 1, options.c.sub_a - end - - def test_from_hash - options = ParentOptions.from :a => 1 - assert_kind_of ParentOptions, options - assert_equal 1, options.a - assert_nil options.b - end - - def test_from_hash_with_sub_object - options = ParentOptions.from :a => 1, :c => {:sub_a => 1} - assert_kind_of ParentOptions, options - assert_equal 1, options.a - assert_nil options.b - assert_kind_of SubOptions, options.c - assert_equal 1, options.c.sub_a - end - - def test_inheritance - subclass = Class.new(ParentOptions) - options = subclass.from(:c => {:sub_a => 'hello'}) - assert_kind_of SubOptions, options.c - assert_equal 'hello', options.c.sub_a - end - - def test_from_deep_hash - hash = {:b => 1} - options = ParentOptions.from :a => hash - assert_equal 1, options.a[:b] - - hash[:b] = 2 - assert_equal 1, options.a[:b] - - options.a[:b] = 3 - assert_equal 2, hash[:b] - assert_equal 3, options.a[:b] - end - - def test_from_nil - options = ParentOptions.from(nil) - assert_kind_of ParentOptions, options - assert_nil options.a - assert_nil options.b - end - - def test_invalid_key - assert_raises NoMethodError do - ParentOptions.from :invalid => 1 - end - end - - def test_update - options = ParentOptions.new(1) - assert_equal 1, options.a - assert_nil options.b - - updated = options.update :a => 2, :b => 3 - assert_equal 2, options.a - assert_equal 3, options.b - assert_equal options, updated - end - - def test_delete - options = ParentOptions.new(1) - assert_equal 1, options.a - assert_equal 1, options.delete(:a) - assert_nil options.a - end - - def test_merge - options = ParentOptions.new(1) - assert_equal 1, options.a - assert_nil options.b - - dup = options.merge :a => 2, :b => 3 - assert_equal 2, dup.a - assert_equal 3, dup.b - assert_equal 1, options.a - assert_nil options.b - end - - def test_env_access_member - e = Faraday::Env.new - assert_nil e.method - e.method = :get - assert_equal :get, e.method - end - - def test_env_access_symbol_non_member - e = Faraday::Env.new - assert_nil e[:custom] - e[:custom] = :boom - assert_equal :boom, e[:custom] - end - - def test_env_access_string_non_member - e = Faraday::Env.new - assert_nil e["custom"] - e["custom"] = :boom - assert_equal :boom, e["custom"] - end - - def test_env_fetch_ignores_false - ssl = Faraday::SSLOptions.new - ssl.verify = false - assert !ssl.fetch(:verify, true) - end - - def test_fetch_grabs_value - opt = Faraday::SSLOptions.new - opt.verify = 1 - assert_equal 1, opt.fetch(:verify, false) { |k| :blah } - end - - def test_fetch_uses_falsey_default - opt = Faraday::SSLOptions.new - assert_equal false, opt.fetch(:verify, false) { |k| :blah } - end - - def test_fetch_accepts_block - opt = Faraday::SSLOptions.new - assert_equal "yo :verify", opt.fetch(:verify) { |k| "yo #{k.inspect}"} - end - - def test_fetch_needs_a_default_if_key_is_missing - opt = Faraday::SSLOptions.new - assert_raises Faraday::Options.fetch_error_class do - opt.fetch :verify - end - end - - def test_fetch_works_with_key - opt = Faraday::SSLOptions.new - opt.verify = 1 - assert_equal 1, opt.fetch(:verify) - end -end From fe9c30e80f38684754c4ac4e10f0d44110f3968d Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sun, 11 Feb 2018 15:51:43 +0000 Subject: [PATCH 10/46] Disables minimum coverage for MiniTest --- test/helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/helper.rb b/test/helper.rb index 371723138..dd0150711 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -6,7 +6,7 @@ SimpleCov.start do add_filter '/bundle/' add_filter '/test/' - minimum_coverage(87) + # minimum_coverage(87) end gem 'minitest' if defined? Bundler From 97bd2e86bcb436eb3f0eb0f89e2f76a50624766e Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sun, 11 Feb 2018 17:46:07 +0000 Subject: [PATCH 11/46] Introduces webmock started implementation of shared examples for adapters based on webmock rather than the live server --- Gemfile | 1 + spec/faraday/adapter/net_http_spec.rb | 3 + spec/spec_helper.rb | 1 + spec/support/helper_methods.rb | 4 ++ spec/support/live_server_mocks.rb | 5 ++ spec/support/shared_examples/adapter.rb | 83 +++++++++++++++++++++++++ test/adapters/integration.rb | 40 ------------ 7 files changed, 97 insertions(+), 40 deletions(-) create mode 100644 spec/faraday/adapter/net_http_spec.rb create mode 100644 spec/support/live_server_mocks.rb create mode 100644 spec/support/shared_examples/adapter.rb diff --git a/Gemfile b/Gemfile index b208f7d2d..7006f38d7 100644 --- a/Gemfile +++ b/Gemfile @@ -23,6 +23,7 @@ group :test do gem 'sinatra', '~> 1.3' gem 'typhoeus', '~> 1.3', :require => 'typhoeus' gem 'rspec', '~> 3.7' + gem 'webmock', '~> 3.3' end gemspec diff --git a/spec/faraday/adapter/net_http_spec.rb b/spec/faraday/adapter/net_http_spec.rb new file mode 100644 index 000000000..d0be64c6a --- /dev/null +++ b/spec/faraday/adapter/net_http_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe Faraday::Adapter::NetHttp do + it_behaves_like 'an adapter' +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 9ee2592fd..b86feea57 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -16,6 +16,7 @@ require 'simplecov' require 'coveralls' +require 'webmock/rspec' SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index 26e1d1c07..f0c2d679f 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -1,5 +1,9 @@ module Faraday module HelperMethods + def ssl_mode? + ENV['SSL'] == 'yes' + end + def normalize(url) Faraday::Utils::URI(url) end diff --git a/spec/support/live_server_mocks.rb b/spec/support/live_server_mocks.rb new file mode 100644 index 000000000..69dd5833c --- /dev/null +++ b/spec/support/live_server_mocks.rb @@ -0,0 +1,5 @@ +module LiveServerMock + def self.stub_all(webmock) + webmock.stub_request(:get, 'example.com/echo') + end +end \ No newline at end of file diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb new file mode 100644 index 000000000..a5b5a296d --- /dev/null +++ b/spec/support/shared_examples/adapter.rb @@ -0,0 +1,83 @@ +shared_examples 'an adapter' do |*features, **options| + let(:adapter) { subject.class.name.split('::').last } + + let(:conn_options) { { headers: { 'X-Faraday-Adapter' => adapter } }.merge(options[:conn_options] || {}) } + + let(:adapter_options) do + return [] unless options[:adapter_options] + if options[:adapter_options].is_a?(Array) + options[:adapter_options] + else + [options[:adapter_options]] + end + end + + let(:protocol) { ssl_mode? ? 'https' : 'http' } + let(:remote) { "#{protocol}://example.com" } + + let(:conn) do + conn_options[:ssl] ||= {} + conn_options[:ssl][:ca_file] ||= ENV['SSL_FILE'] + + Faraday::Connection.new(remote, conn_options) do |conn| + conn.request :multipart + conn.request :url_encoded + conn.response :raise_error + conn.adapter subject.class, *adapter_options + end + end + + let(:stub) { stub_request(http_method, remote) } + + after do + expect(stub).to have_been_requested + end + + describe '#get' do + let(:http_method) { :get } + + it 'retrieves the response body' do + res_body = 'test' + stub.to_return(body: res_body) + expect(conn.get('/').body).to eq(res_body) + end + + it 'sends url encoded parameters' do + query = { name: 'zack' } + stub.with(query: query) + conn.get('/', query) + end + + it 'retrieves the response headers' do + stub.to_return(headers: { 'Content-Type' => 'text/plain' }) + response = conn.get('/') + expect(response.headers['Content-Type']).to match(/text\/plain/) + expect(response.headers['content-type']).to match(/text\/plain/) + end + + it 'handles headers with multiple values' do + stub.to_return(headers: { 'Set-Cookie' => 'one, two' }) + response = conn.get('/') + expect(response.headers['set-cookie']).to eq('one, two') + end + + it 'with body' do + body = { bodyrock: 'true' } + stub.with(body: body) + conn.get('/') do |req| + req.body = body + end + end + + it 'sends user agent' do + stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) + conn.get('/', nil, user_agent: 'Agent Faraday') + end + + it 'parses the reason phrase' do + stub.to_return(status: [200, 'OK']) + response = conn.get('/') + expect(response.reason_phrase).to eq('OK') + end + end +end \ No newline at end of file diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index d48d2d96f..6e0abf860 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -156,46 +156,6 @@ module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request - def test_GET_retrieves_the_response_body - assert_equal 'get', get('echo').body - end - - def test_GET_send_url_encoded_params - assert_equal %(get ?{"name"=>"zack"}), get('echo', :name => 'zack').body - end - - def test_GET_retrieves_the_response_headers - response = get('echo') - assert_match(/text\/plain/, response.headers['Content-Type'], 'original case fail') - assert_match(/text\/plain/, response.headers['content-type'], 'lowercase fail') - end - - def test_GET_handles_headers_with_multiple_values - assert_equal 'one, two', get('multi').headers['set-cookie'] - end - - def test_GET_with_body - response = get('echo') do |req| - req.body = {'bodyrock' => true} - end - assert_equal %(get {"bodyrock"=>"true"}), response.body - end - - def test_GET_sends_user_agent - response = get('echo_header', {:name => 'user-agent'}, :user_agent => 'Agent Faraday') - assert_equal 'Agent Faraday', response.body - end - - def test_GET_ssl - expected = self.class.ssl_mode?.to_s - assert_equal expected, get('ssl').body - end - - def test_GET_reason_phrase - response = get('echo') - assert_equal "OK", response.reason_phrase - end - def test_POST_send_url_encoded_params assert_equal %(post {"name"=>"zack"}), post('echo', :name => 'zack').body end From cc3fed68a29cd8eb5712d7753312b34b4e9c1399 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sun, 11 Feb 2018 17:47:50 +0000 Subject: [PATCH 12/46] fix --- test/adapters/patron_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/adapters/patron_test.rb b/test/adapters/patron_test.rb index 03b695c06..06567d53e 100644 --- a/test/adapters/patron_test.rb +++ b/test/adapters/patron_test.rb @@ -11,7 +11,7 @@ def adapter() :patron end undef :test_PATCH_send_url_encoded_params # https://github.com/toland/patron/issues/52 - undef :test_GET_with_body + # undef :test_GET_with_body # no support for SSL peer verification undef :test_GET_ssl_fails_with_bad_cert if ssl_mode? From 296fc9bc9c1ad175a5b9dbb8f3fd047ff800f03a Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Sun, 18 Feb 2018 22:24:07 +0000 Subject: [PATCH 13/46] Adds tests for Excon, HTTPClient, NetHTTPPersistent, Patron and Typhoeus adapters --- spec/faraday/adapter/excon_spec.rb | 3 +++ spec/faraday/adapter/httpclient_spec.rb | 3 +++ .../adapter/net_http_persistent_spec.rb | 3 +++ spec/faraday/adapter/patron_spec.rb | 3 +++ spec/faraday/adapter/typhoeus_spec.rb | 3 +++ spec/support/shared_examples/adapter.rb | 22 +++++++++---------- 6 files changed, 26 insertions(+), 11 deletions(-) create mode 100644 spec/faraday/adapter/excon_spec.rb create mode 100644 spec/faraday/adapter/httpclient_spec.rb create mode 100644 spec/faraday/adapter/net_http_persistent_spec.rb create mode 100644 spec/faraday/adapter/patron_spec.rb create mode 100644 spec/faraday/adapter/typhoeus_spec.rb diff --git a/spec/faraday/adapter/excon_spec.rb b/spec/faraday/adapter/excon_spec.rb new file mode 100644 index 000000000..9d9befb3d --- /dev/null +++ b/spec/faraday/adapter/excon_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe Faraday::Adapter::Excon do + it_behaves_like 'an adapter' +end \ No newline at end of file diff --git a/spec/faraday/adapter/httpclient_spec.rb b/spec/faraday/adapter/httpclient_spec.rb new file mode 100644 index 000000000..0bebeb38c --- /dev/null +++ b/spec/faraday/adapter/httpclient_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe Faraday::Adapter::HTTPClient do + it_behaves_like 'an adapter' +end \ No newline at end of file diff --git a/spec/faraday/adapter/net_http_persistent_spec.rb b/spec/faraday/adapter/net_http_persistent_spec.rb new file mode 100644 index 000000000..507b54976 --- /dev/null +++ b/spec/faraday/adapter/net_http_persistent_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe Faraday::Adapter::NetHttpPersistent do + it_behaves_like 'an adapter' +end \ No newline at end of file diff --git a/spec/faraday/adapter/patron_spec.rb b/spec/faraday/adapter/patron_spec.rb new file mode 100644 index 000000000..20dee662a --- /dev/null +++ b/spec/faraday/adapter/patron_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe Faraday::Adapter::Patron do + it_behaves_like 'an adapter' +end \ No newline at end of file diff --git a/spec/faraday/adapter/typhoeus_spec.rb b/spec/faraday/adapter/typhoeus_spec.rb new file mode 100644 index 000000000..c89565800 --- /dev/null +++ b/spec/faraday/adapter/typhoeus_spec.rb @@ -0,0 +1,3 @@ +RSpec.describe Faraday::Adapter::Typhoeus do + it_behaves_like 'an adapter' +end \ No newline at end of file diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index a5b5a296d..4127cd009 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -1,5 +1,5 @@ shared_examples 'an adapter' do |*features, **options| - let(:adapter) { subject.class.name.split('::').last } + let(:adapter) { described_class.name.split('::').last } let(:conn_options) { { headers: { 'X-Faraday-Adapter' => adapter } }.merge(options[:conn_options] || {}) } @@ -23,14 +23,14 @@ conn.request :multipart conn.request :url_encoded conn.response :raise_error - conn.adapter subject.class, *adapter_options + conn.adapter described_class, *adapter_options end end - let(:stub) { stub_request(http_method, remote) } + let(:request_stub) { stub_request(http_method, remote) } after do - expect(stub).to have_been_requested + expect(request_stub).to have_been_requested end describe '#get' do @@ -38,44 +38,44 @@ it 'retrieves the response body' do res_body = 'test' - stub.to_return(body: res_body) + request_stub.to_return(body: res_body) expect(conn.get('/').body).to eq(res_body) end it 'sends url encoded parameters' do query = { name: 'zack' } - stub.with(query: query) + request_stub.with(query: query) conn.get('/', query) end it 'retrieves the response headers' do - stub.to_return(headers: { 'Content-Type' => 'text/plain' }) + request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) response = conn.get('/') expect(response.headers['Content-Type']).to match(/text\/plain/) expect(response.headers['content-type']).to match(/text\/plain/) end it 'handles headers with multiple values' do - stub.to_return(headers: { 'Set-Cookie' => 'one, two' }) + request_stub.to_return(headers: { 'Set-Cookie' => 'one, two' }) response = conn.get('/') expect(response.headers['set-cookie']).to eq('one, two') end it 'with body' do body = { bodyrock: 'true' } - stub.with(body: body) + request_stub.with(body: body) conn.get('/') do |req| req.body = body end end it 'sends user agent' do - stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) + request_stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) conn.get('/', nil, user_agent: 'Agent Faraday') end it 'parses the reason phrase' do - stub.to_return(status: [200, 'OK']) + request_stub.to_return(status: [200, 'OK']) response = conn.get('/') expect(response.reason_phrase).to eq('OK') end From d3ec206b051a7b4b7a638be15afb2518d54c3fc0 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Thu, 8 Mar 2018 21:04:42 +0000 Subject: [PATCH 14/46] Allows to configure features on a per-adapter base --- Gemfile | 4 ++++ spec/faraday/adapter/excon_spec.rb | 2 ++ spec/faraday/adapter/httpclient_spec.rb | 2 ++ .../adapter/net_http_persistent_spec.rb | 2 ++ spec/faraday/adapter/net_http_spec.rb | 2 ++ spec/faraday/adapter/patron_spec.rb | 2 ++ spec/faraday/adapter/typhoeus_spec.rb | 2 ++ spec/support/helper_methods.rb | 18 ++++++++++++++ spec/support/live_server_mocks.rb | 5 ---- spec/support/shared_examples/adapter.rb | 24 +++++++++++-------- 10 files changed, 48 insertions(+), 15 deletions(-) delete mode 100644 spec/support/live_server_mocks.rb diff --git a/Gemfile b/Gemfile index 7006f38d7..4b6c0c8e0 100644 --- a/Gemfile +++ b/Gemfile @@ -6,6 +6,10 @@ gem 'ffi-ncurses', '~> 0.3', :platforms => :jruby gem 'jruby-openssl', '~> 0.8.8', :platforms => :jruby gem 'rake' +group :development, :test do + gem 'pry' +end + group :test do gem 'coveralls', :require => false gem 'em-http-request', '>= 1.1', :require => 'em-http' diff --git a/spec/faraday/adapter/excon_spec.rb b/spec/faraday/adapter/excon_spec.rb index 9d9befb3d..13b93a53b 100644 --- a/spec/faraday/adapter/excon_spec.rb +++ b/spec/faraday/adapter/excon_spec.rb @@ -1,3 +1,5 @@ RSpec.describe Faraday::Adapter::Excon do + features :body_on_get, :reason_phrase_parse + it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/httpclient_spec.rb b/spec/faraday/adapter/httpclient_spec.rb index 0bebeb38c..ea3a13a0e 100644 --- a/spec/faraday/adapter/httpclient_spec.rb +++ b/spec/faraday/adapter/httpclient_spec.rb @@ -1,3 +1,5 @@ RSpec.describe Faraday::Adapter::HTTPClient do + features :body_on_get, :reason_phrase_parse + it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/net_http_persistent_spec.rb b/spec/faraday/adapter/net_http_persistent_spec.rb index 507b54976..c706d2c82 100644 --- a/spec/faraday/adapter/net_http_persistent_spec.rb +++ b/spec/faraday/adapter/net_http_persistent_spec.rb @@ -1,3 +1,5 @@ RSpec.describe Faraday::Adapter::NetHttpPersistent do + features :body_on_get, :reason_phrase_parse + it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/net_http_spec.rb b/spec/faraday/adapter/net_http_spec.rb index d0be64c6a..afd46b108 100644 --- a/spec/faraday/adapter/net_http_spec.rb +++ b/spec/faraday/adapter/net_http_spec.rb @@ -1,3 +1,5 @@ RSpec.describe Faraday::Adapter::NetHttp do + features :body_on_get, :reason_phrase_parse + it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/patron_spec.rb b/spec/faraday/adapter/patron_spec.rb index 20dee662a..b344326d2 100644 --- a/spec/faraday/adapter/patron_spec.rb +++ b/spec/faraday/adapter/patron_spec.rb @@ -1,3 +1,5 @@ RSpec.describe Faraday::Adapter::Patron do + features :reason_phrase_parse + it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/typhoeus_spec.rb b/spec/faraday/adapter/typhoeus_spec.rb index c89565800..aa668a7b6 100644 --- a/spec/faraday/adapter/typhoeus_spec.rb +++ b/spec/faraday/adapter/typhoeus_spec.rb @@ -1,3 +1,5 @@ RSpec.describe Faraday::Adapter::Typhoeus do + features :body_on_get + it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index f0c2d679f..f2da871a8 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -1,5 +1,23 @@ module Faraday module HelperMethods + def self.included(base) + base.extend ClassMethods + end + + module ClassMethods + def features(*features) + @features = features + end + + def on_feature(name, &block) + if @features.nil? + superclass.on_feature(name, &block) if superclass.respond_to?(:on_feature) + else + yield if block_given? and @features.include?(name) + end + end + end + def ssl_mode? ENV['SSL'] == 'yes' end diff --git a/spec/support/live_server_mocks.rb b/spec/support/live_server_mocks.rb deleted file mode 100644 index 69dd5833c..000000000 --- a/spec/support/live_server_mocks.rb +++ /dev/null @@ -1,5 +0,0 @@ -module LiveServerMock - def self.stub_all(webmock) - webmock.stub_request(:get, 'example.com/echo') - end -end \ No newline at end of file diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 4127cd009..82bf494e4 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -1,4 +1,4 @@ -shared_examples 'an adapter' do |*features, **options| +shared_examples 'an adapter' do |**options| let(:adapter) { described_class.name.split('::').last } let(:conn_options) { { headers: { 'X-Faraday-Adapter' => adapter } }.merge(options[:conn_options] || {}) } @@ -61,11 +61,13 @@ expect(response.headers['set-cookie']).to eq('one, two') end - it 'with body' do - body = { bodyrock: 'true' } - request_stub.with(body: body) - conn.get('/') do |req| - req.body = body + on_feature :body_on_get do + it 'with body' do + body = { bodyrock: 'true' } + request_stub.with(body: body) + conn.get('/') do |req| + req.body = body + end end end @@ -74,10 +76,12 @@ conn.get('/', nil, user_agent: 'Agent Faraday') end - it 'parses the reason phrase' do - request_stub.to_return(status: [200, 'OK']) - response = conn.get('/') - expect(response.reason_phrase).to eq('OK') + on_feature :reason_phrase_parse do + it 'parses the reason phrase' do + request_stub.to_return(status: [200, 'OK']) + response = conn.get('/') + expect(response.reason_phrase).to eq('OK') + end end end end \ No newline at end of file From 1610d302f2a042f557b294948f8c63c0f40d41df Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Thu, 8 Mar 2018 21:45:04 +0000 Subject: [PATCH 15/46] Adds common #post adapter tests --- spec/spec_helper.rb | 1 + spec/support/helper_methods.rb | 4 +++ spec/support/shared_examples/adapter.rb | 33 +++++++++++++++++++++++++ test/adapters/integration.rb | 20 --------------- 4 files changed, 38 insertions(+), 20 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b86feea57..31e919e43 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -26,6 +26,7 @@ end require 'faraday' +require 'pry' Dir['./spec/support/**/*.rb'].each { |f| require f } diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index f2da871a8..13fe70534 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -45,5 +45,9 @@ def capture_warnings $stderr = old end end + + def multipart_file + Faraday::UploadIO.new(__FILE__, 'text/x-ruby') + end end end \ No newline at end of file diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 82bf494e4..0c8123185 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -84,4 +84,37 @@ end end end + + describe '#post' do + let(:http_method) { :post } + + it 'sends url encoded parameters' do + body = { name: 'zack' } + request_stub.with(body: body) + conn.post('/', body) + end + + it 'sends url encoded nested parameters' do + body = { name: { first: 'zack' } } + request_stub.with(body: body) + conn.post('/', body) + end + + it 'retrieves the response headers' do + request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) + response = conn.post('/') + expect(response.headers['Content-Type']).to match(/text\/plain/) + expect(response.headers['content-type']).to match(/text\/plain/) + end + + it 'sends files' do + body = { uploaded_file: multipart_file } + request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| + # WebMock does not support matching body for multipart/form-data requests yet :( + # https://github.com/bblimke/webmock/issues/623 + request.body =~ %r[RubyMultipartPost] + end + conn.post('/', body) + end + end end \ No newline at end of file diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index 6e0abf860..4733c4ba3 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -156,26 +156,6 @@ module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request - def test_POST_send_url_encoded_params - assert_equal %(post {"name"=>"zack"}), post('echo', :name => 'zack').body - end - - def test_POST_send_url_encoded_nested_params - resp = post('echo', 'name' => {'first' => 'zack'}) - assert_equal %(post {"name"=>{"first"=>"zack"}}), resp.body - end - - def test_POST_retrieves_the_response_headers - assert_match(/text\/plain/, post('echo').headers['content-type']) - end - - def test_POST_sends_files - resp = post('file') do |req| - req.body = {'uploaded_file' => Faraday::UploadIO.new(__FILE__, 'text/x-ruby')} - end - assert_equal "file integration.rb text/x-ruby #{File.size(__FILE__)}", resp.body - end - def test_PUT_send_url_encoded_params assert_equal %(put {"name"=>"zack"}), put('echo', :name => 'zack').body end From 575a4af97b0890beb4f80605ab521b86ae99d948 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Thu, 8 Mar 2018 22:47:44 +0000 Subject: [PATCH 16/46] Isolate http methods tests into a set of shared examples --- lib/faraday.rb | 2 + lib/faraday/connection.rb | 4 +- spec/support/helper_methods.rb | 4 ++ spec/support/shared_examples/adapter.rb | 69 ++----------------- .../support/shared_examples/request_method.rb | 57 +++++++++++++++ test/adapters/default_test.rb | 2 +- test/adapters/integration.rb | 13 ---- 7 files changed, 72 insertions(+), 79 deletions(-) create mode 100644 spec/support/shared_examples/request_method.rb diff --git a/lib/faraday.rb b/lib/faraday.rb index ddb79575d..fdea18c05 100644 --- a/lib/faraday.rb +++ b/lib/faraday.rb @@ -17,6 +17,8 @@ # module Faraday VERSION = "0.14.0" + METHODS_WITH_QUERY = %w[get head delete] + METHODS_WITH_BODY = %w[post put patch] class << self # The root path that Faraday is being loaded from. diff --git a/lib/faraday/connection.rb b/lib/faraday/connection.rb index 0ef6ff569..bbc27044c 100644 --- a/lib/faraday/connection.rb +++ b/lib/faraday/connection.rb @@ -132,7 +132,7 @@ def headers=(hash) # (url = nil, params = nil, headers = nil) # # verb - An HTTP verb: get, head, or delete. - %w[get head delete].each do |method| + METHODS_WITH_QUERY.each do |method| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(url = nil, params = nil, headers = nil) run_request(:#{method}, url, nil, headers) { |request| @@ -169,7 +169,7 @@ def #{method}(url = nil, params = nil, headers = nil) # (url = nil, body = nil, headers = nil) # # verb - An HTTP verb: post, put, or patch. - %w[post put patch].each do |method| + METHODS_WITH_BODY.each do |method| class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{method}(url = nil, body = nil, headers = nil, &block) run_request(:#{method}, url, body, headers, &block) diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index 13fe70534..017a082a0 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -49,5 +49,9 @@ def capture_warnings def multipart_file Faraday::UploadIO.new(__FILE__, 'text/x-ruby') end + + def method_with_body?(method) + METHODS_WITH_BODY.include?(method.to_s) + end end end \ No newline at end of file diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 0c8123185..1b8b03225 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -36,30 +36,7 @@ describe '#get' do let(:http_method) { :get } - it 'retrieves the response body' do - res_body = 'test' - request_stub.to_return(body: res_body) - expect(conn.get('/').body).to eq(res_body) - end - - it 'sends url encoded parameters' do - query = { name: 'zack' } - request_stub.with(query: query) - conn.get('/', query) - end - - it 'retrieves the response headers' do - request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) - response = conn.get('/') - expect(response.headers['Content-Type']).to match(/text\/plain/) - expect(response.headers['content-type']).to match(/text\/plain/) - end - - it 'handles headers with multiple values' do - request_stub.to_return(headers: { 'Set-Cookie' => 'one, two' }) - response = conn.get('/') - expect(response.headers['set-cookie']).to eq('one, two') - end + it_behaves_like 'a request method', multipart_support: false on_feature :body_on_get do it 'with body' do @@ -70,51 +47,17 @@ end end end - - it 'sends user agent' do - request_stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) - conn.get('/', nil, user_agent: 'Agent Faraday') - end - - on_feature :reason_phrase_parse do - it 'parses the reason phrase' do - request_stub.to_return(status: [200, 'OK']) - response = conn.get('/') - expect(response.reason_phrase).to eq('OK') - end - end end describe '#post' do let(:http_method) { :post } - it 'sends url encoded parameters' do - body = { name: 'zack' } - request_stub.with(body: body) - conn.post('/', body) - end - - it 'sends url encoded nested parameters' do - body = { name: { first: 'zack' } } - request_stub.with(body: body) - conn.post('/', body) - end + it_behaves_like 'a request method' + end - it 'retrieves the response headers' do - request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) - response = conn.post('/') - expect(response.headers['Content-Type']).to match(/text\/plain/) - expect(response.headers['content-type']).to match(/text\/plain/) - end + describe '#put' do + let(:http_method) { :put } - it 'sends files' do - body = { uploaded_file: multipart_file } - request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| - # WebMock does not support matching body for multipart/form-data requests yet :( - # https://github.com/bblimke/webmock/issues/623 - request.body =~ %r[RubyMultipartPost] - end - conn.post('/', body) - end + it_behaves_like 'a request method' end end \ No newline at end of file diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb new file mode 100644 index 000000000..98b678dca --- /dev/null +++ b/spec/support/shared_examples/request_method.rb @@ -0,0 +1,57 @@ +shared_examples 'a request method' do |multipart_support: true| + let(:query_or_body) { method_with_body?(http_method) ? :body : :query } + + it 'retrieves the response body' do + res_body = 'test' + request_stub.to_return(body: res_body) + expect(conn.public_send(http_method, '/').body).to eq(res_body) + end + + it 'handles headers with multiple values' do + request_stub.to_return(headers: { 'Set-Cookie' => 'one, two' }) + response = conn.public_send(http_method, '/') + expect(response.headers['set-cookie']).to eq('one, two') + end + + it 'sends url encoded parameters' do + payload = { name: 'zack' } + request_stub.with(Hash[query_or_body, payload]) + conn.public_send(http_method, '/', payload) + end + + it 'sends url encoded nested parameters' do + payload = { name: { first: 'zack' } } + request_stub.with(Hash[query_or_body, payload]) + conn.public_send(http_method, '/', payload) + end + + it 'retrieves the response headers' do + request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) + response = conn.public_send(http_method, '/') + expect(response.headers['Content-Type']).to match(/text\/plain/) + expect(response.headers['content-type']).to match(/text\/plain/) + end + + it 'sends user agent' do + request_stub.with(headers: { 'User-Agent' => 'Agent Faraday' }) + conn.public_send(http_method, '/', nil, user_agent: 'Agent Faraday') + end + + it 'sends files' do + payload = { uploaded_file: multipart_file } + request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| + # WebMock does not support matching body for multipart/form-data requests yet :( + # https://github.com/bblimke/webmock/issues/623 + request.body =~ %r[RubyMultipartPost] + end + conn.public_send(http_method, '/', payload) + end if multipart_support + + on_feature :reason_phrase_parse do + it 'parses the reason phrase' do + request_stub.to_return(status: [200, 'OK']) + response = conn.public_send(http_method, '/') + expect(response.reason_phrase).to eq('OK') + end + end +end \ No newline at end of file diff --git a/test/adapters/default_test.rb b/test/adapters/default_test.rb index 4d30064fb..b0036a329 100644 --- a/test/adapters/default_test.rb +++ b/test/adapters/default_test.rb @@ -7,7 +7,7 @@ def adapter() :default end Integration.apply(self, :NonParallel, :Streaming) do # default stack is not configured with Multipart - undef :test_POST_sends_files + # undef :test_POST_sends_files end end diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index 4733c4ba3..1b2f8a4c0 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -156,19 +156,6 @@ module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request - def test_PUT_send_url_encoded_params - assert_equal %(put {"name"=>"zack"}), put('echo', :name => 'zack').body - end - - def test_PUT_send_url_encoded_nested_params - resp = put('echo', 'name' => {'first' => 'zack'}) - assert_equal %(put {"name"=>{"first"=>"zack"}}), resp.body - end - - def test_PUT_retrieves_the_response_headers - assert_match(/text\/plain/, put('echo').headers['content-type']) - end - def test_PATCH_send_url_encoded_params assert_equal %(patch {"name"=>"zack"}), patch('echo', :name => 'zack').body end From 5c544e6cbcc96b12ca82aa1c54f45533ee598217 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Tue, 13 Mar 2018 21:06:25 +0000 Subject: [PATCH 17/46] Skips some tests under Patron as there's an issue with PATCH body (https://github.com/toland/patron/issues/163) Hides skipped test form RSpec result --- spec/spec_helper.rb | 11 +++++++++++ spec/support/shared_examples/adapter.rb | 12 +++++++++--- spec/support/shared_examples/request_method.rb | 15 +++++++++++++-- 3 files changed, 33 insertions(+), 5 deletions(-) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 31e919e43..b2f6c4002 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -113,3 +113,14 @@ config.include Faraday::HelperMethods end + +# Extends RSpec DocumentationFormatter to hide skipped tests. +module FormatterOverrides + def example_pending(_) + end + + def dump_pending(_) + end + + RSpec::Core::Formatters::DocumentationFormatter.prepend self +end diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 1b8b03225..68e9ff11c 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -19,7 +19,7 @@ conn_options[:ssl] ||= {} conn_options[:ssl][:ca_file] ||= ENV['SSL_FILE'] - Faraday::Connection.new(remote, conn_options) do |conn| + Faraday.new(remote, conn_options) do |conn| conn.request :multipart conn.request :url_encoded conn.response :raise_error @@ -29,8 +29,8 @@ let(:request_stub) { stub_request(http_method, remote) } - after do - expect(request_stub).to have_been_requested + after do |example| + expect(request_stub).to have_been_requested unless example.skipped? end describe '#get' do @@ -60,4 +60,10 @@ it_behaves_like 'a request method' end + + describe '#patch' do + let(:http_method) { :patch } + + it_behaves_like 'a request method', multipart_support: false + end end \ No newline at end of file diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb index 98b678dca..18933273f 100644 --- a/spec/support/shared_examples/request_method.rb +++ b/spec/support/shared_examples/request_method.rb @@ -1,4 +1,4 @@ -shared_examples 'a request method' do |multipart_support: true| +shared_examples 'a request method' do let(:query_or_body) { method_with_body?(http_method) ? :body : :query } it 'retrieves the response body' do @@ -14,12 +14,18 @@ end it 'sends url encoded parameters' do + # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 + skip if described_class == Faraday::Adapter::Patron && http_method == :patch + payload = { name: 'zack' } request_stub.with(Hash[query_or_body, payload]) conn.public_send(http_method, '/', payload) end it 'sends url encoded nested parameters' do + # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 + skip if described_class == Faraday::Adapter::Patron && http_method == :patch + payload = { name: { first: 'zack' } } request_stub.with(Hash[query_or_body, payload]) conn.public_send(http_method, '/', payload) @@ -38,6 +44,11 @@ end it 'sends files' do + # Can't send files on get methods + skip if http_method == :get + # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 + skip if described_class == Faraday::Adapter::Patron && http_method == :patch + payload = { uploaded_file: multipart_file } request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| # WebMock does not support matching body for multipart/form-data requests yet :( @@ -45,7 +56,7 @@ request.body =~ %r[RubyMultipartPost] end conn.public_send(http_method, '/', payload) - end if multipart_support + end on_feature :reason_phrase_parse do it 'parses the reason phrase' do From fc3542f3b5a1226038a85275c8324f9bf31d137c Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Mon, 19 Mar 2018 07:52:49 +0000 Subject: [PATCH 18/46] fixes --- lib/faraday/adapter/patron.rb | 3 +- spec/spec_helper.rb | 14 +++ spec/support/shared_examples/adapter.rb | 27 +++++- .../support/shared_examples/request_method.rb | 47 +++++++++- test/adapters/integration.rb | 90 +++++++------------ test/adapters/patron_test.rb | 2 +- 6 files changed, 115 insertions(+), 68 deletions(-) diff --git a/lib/faraday/adapter/patron.rb b/lib/faraday/adapter/patron.rb index ddefd1893..383bcb34d 100644 --- a/lib/faraday/adapter/patron.rb +++ b/lib/faraday/adapter/patron.rb @@ -85,7 +85,8 @@ def configure_ssl(session, ssl) private - CURL_TIMEOUT_MESSAGES = [ "Connection time-out", + CURL_TIMEOUT_MESSAGES = [ + "Connection time-out", "Connection timed out", "Timed out before name resolve", "server connect has timed out", diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b2f6c4002..a730f9c27 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -17,6 +17,7 @@ require 'simplecov' require 'coveralls' require 'webmock/rspec' +WebMock.disable_net_connect!(allow_localhost: true) SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] @@ -124,3 +125,16 @@ def dump_pending(_) RSpec::Core::Formatters::DocumentationFormatter.prepend self end + +# Allows to disable WebMock stubs +module DisablingStub + def disable + @disabled = true + end + + def disabled? + @disabled + end + + WebMock::RequestStub.prepend self +end diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 68e9ff11c..470bf81b1 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -27,16 +27,16 @@ end end - let(:request_stub) { stub_request(http_method, remote) } + let!(:request_stub) { stub_request(http_method, remote) } after do |example| - expect(request_stub).to have_been_requested unless example.skipped? + expect(request_stub).to have_been_requested unless example.skipped? or request_stub.disabled? end describe '#get' do let(:http_method) { :get } - it_behaves_like 'a request method', multipart_support: false + it_behaves_like 'a request method' on_feature :body_on_get do it 'with body' do @@ -61,9 +61,28 @@ it_behaves_like 'a request method' end + describe '#delete' do + let(:http_method) { :delete } + + it_behaves_like 'a request method' + end + describe '#patch' do let(:http_method) { :patch } - it_behaves_like 'a request method', multipart_support: false + it_behaves_like 'a request method' end + + describe '#head' do + let(:http_method) { :head } + + it_behaves_like 'a request method' + end + + # TODO: Enable after adding API for options method + # describe '#options' do + # let(:http_method) { :options } + # + # it_behaves_like 'a request method' + # end end \ No newline at end of file diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb index 18933273f..bc44c543b 100644 --- a/spec/support/shared_examples/request_method.rb +++ b/spec/support/shared_examples/request_method.rb @@ -44,8 +44,8 @@ end it 'sends files' do - # Can't send files on get methods - skip if http_method == :get + # Can't send files on get, head and delete methods + skip unless method_with_body?(http_method) # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 skip if described_class == Faraday::Adapter::Patron && http_method == :patch @@ -58,6 +58,49 @@ conn.public_send(http_method, '/', payload) end + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # it 'handles open timeout responses' do + # request_stub.to_timeout + # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::Error::ConnectionFailed) + # end + + it 'handles connection error' do + request_stub.disable + expect { conn.public_send(http_method, 'http://localhost:4') }.to raise_error(Faraday::Error::ConnectionFailed) + end + + # TODO: Fix proxy tests + # it 'handles requests with proxy' do + # conn_options[:proxy] = 'http://google.co.uk' + # # stub_request(:get, 'http://example.com/') + # + # # binding.pry + # conn.public_send(http_method, '/') + # # assert_equal 'get', res.body + # + # # unless self.class.ssl_mode? + # # # proxy can't append "Via" header for HTTPS responses + # # assert_match(/:#{proxy_uri.port}$/, res['via']) + # # end + # end + # + # it 'handles proxy failures' do + # proxy_uri = URI(ENV['LIVE_PROXY']) + # proxy_uri.password = 'WRONG' + # conn = create_connection(:proxy => proxy_uri) + # + # err = assert_raises Faraday::Error::ConnectionFailed do + # conn.get '/echo' + # end + # + # unless self.class.ssl_mode? && (self.class.jruby? || + # adapter == :em_http || adapter == :em_synchrony) + # # JRuby raises "End of file reached" which cannot be distinguished from a 407 + # # EM raises "connection closed by server" due to https://github.com/igrigorik/em-socksify/pull/19 + # assert_equal %{407 "Proxy Authentication Required "}, err.message + # end + # end + on_feature :reason_phrase_parse do it 'parses the reason phrase' do request_stub.to_return(status: [200, 'OK']) diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index 1b2f8a4c0..c2751aab7 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -156,31 +156,7 @@ module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request - def test_PATCH_send_url_encoded_params - assert_equal %(patch {"name"=>"zack"}), patch('echo', :name => 'zack').body - end - - def test_OPTIONS - resp = run_request(:options, 'echo', nil, {}) - assert_equal 'options', resp.body - end - - def test_HEAD_retrieves_no_response_body - assert_equal '', head('echo').body - end - - def test_HEAD_retrieves_the_response_headers - assert_match(/text\/plain/, head('echo').headers['content-type']) - end - - def test_DELETE_retrieves_the_response_headers - assert_match(/text\/plain/, delete('echo').headers['content-type']) - end - - def test_DELETE_retrieves_the_body - assert_equal %(delete), delete('echo').body - end - + # This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 def test_timeout conn = create_connection(:request => {:timeout => 1, :open_timeout => 1}) assert_raises Faraday::Error::TimeoutError do @@ -188,41 +164,35 @@ def test_timeout end end - def test_connection_error - assert_raises Faraday::Error::ConnectionFailed do - get 'http://localhost:4' - end - end - - def test_proxy - proxy_uri = URI(ENV['LIVE_PROXY']) - conn = create_connection(:proxy => proxy_uri) - - res = conn.get '/echo' - assert_equal 'get', res.body - - unless self.class.ssl_mode? - # proxy can't append "Via" header for HTTPS responses - assert_match(/:#{proxy_uri.port}$/, res['via']) - end - end - - def test_proxy_auth_fail - proxy_uri = URI(ENV['LIVE_PROXY']) - proxy_uri.password = 'WRONG' - conn = create_connection(:proxy => proxy_uri) - - err = assert_raises Faraday::Error::ConnectionFailed do - conn.get '/echo' - end - - unless self.class.ssl_mode? && (self.class.jruby? || - adapter == :em_http || adapter == :em_synchrony) - # JRuby raises "End of file reached" which cannot be distinguished from a 407 - # EM raises "connection closed by server" due to https://github.com/igrigorik/em-socksify/pull/19 - assert_equal %{407 "Proxy Authentication Required "}, err.message - end - end + # def test_proxy + # proxy_uri = URI(ENV['LIVE_PROXY']) + # conn = create_connection(:proxy => proxy_uri) + # + # res = conn.get '/echo' + # assert_equal 'get', res.body + # + # unless self.class.ssl_mode? + # # proxy can't append "Via" header for HTTPS responses + # assert_match(/:#{proxy_uri.port}$/, res['via']) + # end + # end + + # def test_proxy_auth_fail + # proxy_uri = URI(ENV['LIVE_PROXY']) + # proxy_uri.password = 'WRONG' + # conn = create_connection(:proxy => proxy_uri) + # + # err = assert_raises Faraday::Error::ConnectionFailed do + # conn.get '/echo' + # end + # + # unless self.class.ssl_mode? && (self.class.jruby? || + # adapter == :em_http || adapter == :em_synchrony) + # # JRuby raises "End of file reached" which cannot be distinguished from a 407 + # # EM raises "connection closed by server" due to https://github.com/igrigorik/em-socksify/pull/19 + # assert_equal %{407 "Proxy Authentication Required "}, err.message + # end + # end def test_empty_body_response_represented_as_blank_string response = get('204') diff --git a/test/adapters/patron_test.rb b/test/adapters/patron_test.rb index 06567d53e..37ddd5e02 100644 --- a/test/adapters/patron_test.rb +++ b/test/adapters/patron_test.rb @@ -8,7 +8,7 @@ def adapter() :patron end unless jruby? Integration.apply(self, :NonParallel, :NonStreaming) do # https://github.com/toland/patron/issues/34 - undef :test_PATCH_send_url_encoded_params + # undef :test_PATCH_send_url_encoded_params # https://github.com/toland/patron/issues/52 # undef :test_GET_with_body From 1030d2e36b29b6d9f3aa72126ec2ec16057bf142 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Mon, 26 Mar 2018 22:59:52 +0100 Subject: [PATCH 19/46] Streaming WIP --- spec/faraday/adapter/httpclient_spec.rb | 2 +- .../adapter/net_http_persistent_spec.rb | 2 +- spec/faraday/adapter/net_http_spec.rb | 2 +- spec/support/helper_methods.rb | 5 ++ spec/support/shared_examples/adapter.rb | 67 +++++++++++--- .../support/shared_examples/request_method.rb | 87 +++++++++---------- spec/support/streaming_response_checker.rb | 33 +++++++ test/adapters/integration.rb | 81 ++--------------- 8 files changed, 147 insertions(+), 132 deletions(-) create mode 100644 spec/support/streaming_response_checker.rb diff --git a/spec/faraday/adapter/httpclient_spec.rb b/spec/faraday/adapter/httpclient_spec.rb index ea3a13a0e..9c16a3ab5 100644 --- a/spec/faraday/adapter/httpclient_spec.rb +++ b/spec/faraday/adapter/httpclient_spec.rb @@ -1,5 +1,5 @@ RSpec.describe Faraday::Adapter::HTTPClient do - features :body_on_get, :reason_phrase_parse + features :body_on_get, :reason_phrase_parse, :compression it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/net_http_persistent_spec.rb b/spec/faraday/adapter/net_http_persistent_spec.rb index c706d2c82..5e90fbbf3 100644 --- a/spec/faraday/adapter/net_http_persistent_spec.rb +++ b/spec/faraday/adapter/net_http_persistent_spec.rb @@ -1,5 +1,5 @@ RSpec.describe Faraday::Adapter::NetHttpPersistent do - features :body_on_get, :reason_phrase_parse + features :body_on_get, :reason_phrase_parse, :compression it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/net_http_spec.rb b/spec/faraday/adapter/net_http_spec.rb index afd46b108..5afad1555 100644 --- a/spec/faraday/adapter/net_http_spec.rb +++ b/spec/faraday/adapter/net_http_spec.rb @@ -1,5 +1,5 @@ RSpec.describe Faraday::Adapter::NetHttp do - features :body_on_get, :reason_phrase_parse + features :body_on_get, :reason_phrase_parse, :compression it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index 017a082a0..de6a33f38 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -53,5 +53,10 @@ def multipart_file def method_with_body?(method) METHODS_WITH_BODY.include?(method.to_s) end + + def big_string + kb = 1024 + (32..126).map{|i| i.chr}.cycle.take(50*kb).join + end end end \ No newline at end of file diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 470bf81b1..253f52e6c 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -1,4 +1,6 @@ shared_examples 'an adapter' do |**options| + include Faraday::StreamingResponseChecker + let(:adapter) { described_class.name.split('::').last } let(:conn_options) { { headers: { 'X-Faraday-Adapter' => adapter } }.merge(options[:conn_options] || {}) } @@ -16,7 +18,7 @@ let(:remote) { "#{protocol}://example.com" } let(:conn) do - conn_options[:ssl] ||= {} + conn_options[:ssl] ||= {} conn_options[:ssl][:ca_file] ||= ENV['SSL_FILE'] Faraday.new(remote, conn_options) do |conn| @@ -29,17 +31,17 @@ let!(:request_stub) { stub_request(http_method, remote) } - after do |example| - expect(request_stub).to have_been_requested unless example.skipped? or request_stub.disabled? + after do + expect(request_stub).to have_been_requested unless request_stub.disabled? end describe '#get' do let(:http_method) { :get } - it_behaves_like 'a request method' + it_behaves_like 'a request method', :get on_feature :body_on_get do - it 'with body' do + it 'handles request body' do body = { bodyrock: 'true' } request_stub.with(body: body) conn.get('/') do |req| @@ -47,36 +49,81 @@ end end end + + on_feature :compression do + it 'handles gzip compression' do + request_stub.with(headers: { 'Accept-Encoding' => %r[\bgzip\b] }) + conn.get('/') + end + + it 'handles deflate compression' do + request_stub.with(headers: { 'Accept-Encoding' => %r[\bdeflate\b] }) + conn.get('/') + end + end + + # Streaming + let(:streamed) { [] } + + it 'handles streaming' do + # response, streamed = streaming_request(create_connection, :get, 'stream') + # check_streaming_response(streamed, :chunk_size => 16*1024) + # assert_equal "", response.body + request_stub.to_return(body: big_string) + + response = conn.get('/') do |req| + req.options.on_data = Proc.new { |*args| streamed << args } + end + expect(response.body).to eq('') + check_streaming_response(streamed, :chunk_size => 16*1024) + end + + # def test_non_GET_streaming + # response, streamed = streaming_request(create_connection, :post, 'stream') + # check_streaming_response(streamed, chunk_size: 16 * 1024) + # + # assert_equal "", response.body + # end + # + # def test_GET_streaming_empty_response + # _, streamed = streaming_request(create_connection, :get, 'empty_stream') + # assert_equal [["", 0]], streamed + # end + # + # def test_non_GET_streaming_empty_response + # _, streamed = streaming_request(create_connection, :post, 'empty_stream') + # assert_equal [["", 0]], streamed + # end end describe '#post' do let(:http_method) { :post } - it_behaves_like 'a request method' + it_behaves_like 'a request method', :post end describe '#put' do let(:http_method) { :put } - it_behaves_like 'a request method' + it_behaves_like 'a request method', :put end describe '#delete' do let(:http_method) { :delete } - it_behaves_like 'a request method' + it_behaves_like 'a request method', :delete end describe '#patch' do let(:http_method) { :patch } - it_behaves_like 'a request method' + it_behaves_like 'a request method', :patch end describe '#head' do let(:http_method) { :head } - it_behaves_like 'a request method' + it_behaves_like 'a request method', :head end # TODO: Enable after adding API for options method diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb index bc44c543b..8b6dd5294 100644 --- a/spec/support/shared_examples/request_method.rb +++ b/spec/support/shared_examples/request_method.rb @@ -1,5 +1,6 @@ -shared_examples 'a request method' do +shared_examples 'a request method' do |http_method| let(:query_or_body) { method_with_body?(http_method) ? :body : :query } + let(:response) { conn.public_send(http_method, '/') } it 'retrieves the response body' do res_body = 'test' @@ -9,31 +10,11 @@ it 'handles headers with multiple values' do request_stub.to_return(headers: { 'Set-Cookie' => 'one, two' }) - response = conn.public_send(http_method, '/') expect(response.headers['set-cookie']).to eq('one, two') end - it 'sends url encoded parameters' do - # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 - skip if described_class == Faraday::Adapter::Patron && http_method == :patch - - payload = { name: 'zack' } - request_stub.with(Hash[query_or_body, payload]) - conn.public_send(http_method, '/', payload) - end - - it 'sends url encoded nested parameters' do - # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 - skip if described_class == Faraday::Adapter::Patron && http_method == :patch - - payload = { name: { first: 'zack' } } - request_stub.with(Hash[query_or_body, payload]) - conn.public_send(http_method, '/', payload) - end - it 'retrieves the response headers' do request_stub.to_return(headers: { 'Content-Type' => 'text/plain' }) - response = conn.public_send(http_method, '/') expect(response.headers['Content-Type']).to match(/text\/plain/) expect(response.headers['content-type']).to match(/text\/plain/) end @@ -43,19 +24,48 @@ conn.public_send(http_method, '/', nil, user_agent: 'Agent Faraday') end - it 'sends files' do - # Can't send files on get, head and delete methods - skip unless method_with_body?(http_method) - # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 - skip if described_class == Faraday::Adapter::Patron && http_method == :patch + it 'represents empty body response as blank string' do + expect(response.body).to eq('') + end + + it 'handles connection error' do + request_stub.disable + expect { conn.public_send(http_method, 'http://localhost:4') }.to raise_error(Faraday::Error::ConnectionFailed) + end + + # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 + unless described_class == Faraday::Adapter::Patron && http_method == :patch + it 'sends url encoded parameters' do + payload = { name: 'zack' } + request_stub.with(Hash[query_or_body, payload]) + conn.public_send(http_method, '/', payload) + end + + it 'sends url encoded nested parameters' do + payload = { name: { first: 'zack' } } + request_stub.with(Hash[query_or_body, payload]) + conn.public_send(http_method, '/', payload) + end + + it 'sends files' do + # Can't send files on get, head and delete methods + skip unless method_with_body?(http_method) + + payload = { uploaded_file: multipart_file } + request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| + # WebMock does not support matching body for multipart/form-data requests yet :( + # https://github.com/bblimke/webmock/issues/623 + request.body =~ %r[RubyMultipartPost] + end + conn.public_send(http_method, '/', payload) + end + end - payload = { uploaded_file: multipart_file } - request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| - # WebMock does not support matching body for multipart/form-data requests yet :( - # https://github.com/bblimke/webmock/issues/623 - request.body =~ %r[RubyMultipartPost] + on_feature :reason_phrase_parse do + it 'parses the reason phrase' do + request_stub.to_return(status: [200, 'OK']) + expect(response.reason_phrase).to eq('OK') end - conn.public_send(http_method, '/', payload) end # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 @@ -64,11 +74,6 @@ # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::Error::ConnectionFailed) # end - it 'handles connection error' do - request_stub.disable - expect { conn.public_send(http_method, 'http://localhost:4') }.to raise_error(Faraday::Error::ConnectionFailed) - end - # TODO: Fix proxy tests # it 'handles requests with proxy' do # conn_options[:proxy] = 'http://google.co.uk' @@ -100,12 +105,4 @@ # assert_equal %{407 "Proxy Authentication Required "}, err.message # end # end - - on_feature :reason_phrase_parse do - it 'parses the reason phrase' do - request_stub.to_return(status: [200, 'OK']) - response = conn.public_send(http_method, '/') - expect(response.reason_phrase).to eq('OK') - end - end end \ No newline at end of file diff --git a/spec/support/streaming_response_checker.rb b/spec/support/streaming_response_checker.rb new file mode 100644 index 000000000..d8c5f6684 --- /dev/null +++ b/spec/support/streaming_response_checker.rb @@ -0,0 +1,33 @@ +module Faraday + module StreamingResponseChecker + def check_streaming_response(streamed, options = {}) + opts = { + :prefix => '', + :streaming? => true + }.merge(options) + + expected_response = opts[:prefix] + big_string + + chunks, sizes = streamed.transpose + + # Check that the total size of the chunks (via the last size returned) + # is the same size as the expected_response + expect(sizes.last).to eq(expected_response.bytesize) + + start_index = 0 + expected_chunks = [] + chunks.each do |actual_chunk| + expected_chunk = expected_response[start_index..((start_index + actual_chunk.bytesize) - 1)] + expected_chunks << expected_chunk + start_index += expected_chunk.bytesize + end + + # it's easier to read a smaller portion, so we check that first + expect(expected_chunks[0][0..255]).to eq(chunks[0][0..255]) + + [expected_chunks, chunks].transpose.each do |expected, actual| + expect(actual).to eq(expected) + end + end + end +end \ No newline at end of file diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index c2751aab7..ad4c2b1cb 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -14,7 +14,7 @@ def self.apply(base, *extra_features) features = [:Common] features.concat extra_features features << :SSL if base.ssl_mode? - features.each {|name| base.send(:include, self.const_get(name)) } + features.each { |name| base.send(:include, self.const_get(name)) } yield if block_given? elsif !defined? @warned warn "Warning: Not running integration tests against a live server." @@ -67,7 +67,7 @@ def test_callback_is_called_in_parallel_with_no_streaming_support err = capture_warnings do connection.in_parallel do resp1, streamed1 = streaming_request(connection, :get, 'stream?a=1') - resp2, streamed2 = streaming_request(connection, :get, 'stream?b=2', :chunk_size => 16*1024) + resp2, streamed2 = streaming_request(connection, :get, 'stream?b=2', :chunk_size => 16 * 1024) assert connection.in_parallel? assert_nil resp1.body assert_nil resp2.body @@ -77,39 +77,19 @@ def test_callback_is_called_in_parallel_with_no_streaming_support end assert !connection.in_parallel? assert_match(/Streaming .+ not yet implemented/, err) - opts = {:streaming? => false, :chunk_size => 16*1024} + opts = { :streaming? => false, :chunk_size => 16 * 1024 } check_streaming_response(streamed1, opts.merge(:prefix => '{"a"=>"1"}')) check_streaming_response(streamed2, opts.merge(:prefix => '{"b"=>"2"}')) end end module Streaming - def test_GET_streaming - response, streamed = streaming_request(create_connection, :get, 'stream') - check_streaming_response(streamed, :chunk_size => 16*1024) - assert_equal "", response.body - end - - def test_non_GET_streaming - response, streamed = streaming_request(create_connection, :post, 'stream') - check_streaming_response(streamed, :chunk_size => 16*1024) - - assert_equal "", response.body - end - - def test_GET_streaming_empty_response - _, streamed = streaming_request(create_connection, :get, 'empty_stream') - assert_equal [["", 0]], streamed - end - def test_non_GET_streaming_empty_response - _, streamed = streaming_request(create_connection, :post, 'empty_stream') - assert_equal [["", 0]], streamed - end end module NonStreaming include Faraday::Shared + def test_GET_streaming response, streamed = nil err = capture_warnings do @@ -134,17 +114,13 @@ def test_non_GET_streaming end module Compression - def test_GET_handles_compression - res = get('echo_header', :name => 'accept-encoding') - assert_match(/\bgzip\b/, res.body) - assert_match(/\bdeflate\b/, res.body) - end + end module SSL def test_GET_ssl_fails_with_bad_cert ca_file = 'tmp/faraday-different-ca-cert.crt' - conn = create_connection(:ssl => {:ca_file => ca_file}) + conn = create_connection(:ssl => { :ca_file => ca_file }) err = assert_raises Faraday::SSLError do conn.get('/ssl') end @@ -158,7 +134,7 @@ module Common # This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 def test_timeout - conn = create_connection(:request => {:timeout => 1, :open_timeout => 1}) + conn = create_connection(:request => { :timeout => 1, :open_timeout => 1 }) assert_raises Faraday::Error::TimeoutError do conn.get '/slow' end @@ -194,11 +170,6 @@ def test_timeout # end # end - def test_empty_body_response_represented_as_blank_string - response = get('204') - assert_equal '', response.body - end - def adapter raise NotImplementedError.new("Need to override #adapter") end @@ -231,44 +202,6 @@ def create_connection(options = {}) conn.builder.insert_before adapter_handler, Faraday::Response::RaiseError end end - - def streaming_request(connection, method, path, options={}) - streamed = [] - response = connection.send(method, path) do |req| - req.options.on_data = Proc.new{|*args| streamed << args} - end - - [response, streamed] - end - - def check_streaming_response(streamed, options={}) - opts = { - :prefix => '', - :streaming? => true - }.merge(options) - expected_response = opts[:prefix] + Faraday::Shared.big_string - - chunks, sizes = streamed.transpose - - # Check that the total size of the chunks (via the last size returned) - # is the same size as the expected_response - assert_equal sizes.last, expected_response.bytesize - - start_index = 0 - expected_chunks = [] - chunks.each do |actual_chunk| - expected_chunk = expected_response[start_index..((start_index + actual_chunk.bytesize)-1)] - expected_chunks << expected_chunk - start_index += expected_chunk.bytesize - end - - # it's easier to read a smaller portion, so we check that first - assert_equal expected_chunks[0][0..255], chunks[0][0..255] - - [expected_chunks, chunks].transpose.each do |expected, actual| - assert_equal expected, actual - end - end end end end From 79746fd27725508589fc8ae3130985a5c2898a25 Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Fri, 30 Mar 2018 13:56:57 +0100 Subject: [PATCH 20/46] Finish converting streaming tests. Converts parallel tests. --- spec/faraday/adapter/net_http_spec.rb | 2 +- spec/faraday/adapter/typhoeus_spec.rb | 2 +- spec/support/helper_methods.rb | 6 +- spec/support/shared_examples/adapter.rb | 45 ---------- .../support/shared_examples/request_method.rb | 90 ++++++++++++++++--- test/adapters/integration.rb | 38 ++++---- 6 files changed, 102 insertions(+), 81 deletions(-) diff --git a/spec/faraday/adapter/net_http_spec.rb b/spec/faraday/adapter/net_http_spec.rb index 5afad1555..2f094cdaa 100644 --- a/spec/faraday/adapter/net_http_spec.rb +++ b/spec/faraday/adapter/net_http_spec.rb @@ -1,5 +1,5 @@ RSpec.describe Faraday::Adapter::NetHttp do - features :body_on_get, :reason_phrase_parse, :compression + features :body_on_get, :reason_phrase_parse, :compression, :streaming it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/faraday/adapter/typhoeus_spec.rb b/spec/faraday/adapter/typhoeus_spec.rb index aa668a7b6..c62b527eb 100644 --- a/spec/faraday/adapter/typhoeus_spec.rb +++ b/spec/faraday/adapter/typhoeus_spec.rb @@ -1,5 +1,5 @@ RSpec.describe Faraday::Adapter::Typhoeus do - features :body_on_get + features :body_on_get, :parallel it_behaves_like 'an adapter' end \ No newline at end of file diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index de6a33f38..069eb522d 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -16,6 +16,10 @@ def on_feature(name, &block) yield if block_given? and @features.include?(name) end end + + def method_with_body?(method) + METHODS_WITH_BODY.include?(method.to_s) + end end def ssl_mode? @@ -51,7 +55,7 @@ def multipart_file end def method_with_body?(method) - METHODS_WITH_BODY.include?(method.to_s) + self.class.method_with_body?(method) end def big_string diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 253f52e6c..8aa796e8b 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -49,51 +49,6 @@ end end end - - on_feature :compression do - it 'handles gzip compression' do - request_stub.with(headers: { 'Accept-Encoding' => %r[\bgzip\b] }) - conn.get('/') - end - - it 'handles deflate compression' do - request_stub.with(headers: { 'Accept-Encoding' => %r[\bdeflate\b] }) - conn.get('/') - end - end - - # Streaming - let(:streamed) { [] } - - it 'handles streaming' do - # response, streamed = streaming_request(create_connection, :get, 'stream') - # check_streaming_response(streamed, :chunk_size => 16*1024) - # assert_equal "", response.body - request_stub.to_return(body: big_string) - - response = conn.get('/') do |req| - req.options.on_data = Proc.new { |*args| streamed << args } - end - expect(response.body).to eq('') - check_streaming_response(streamed, :chunk_size => 16*1024) - end - - # def test_non_GET_streaming - # response, streamed = streaming_request(create_connection, :post, 'stream') - # check_streaming_response(streamed, chunk_size: 16 * 1024) - # - # assert_equal "", response.body - # end - # - # def test_GET_streaming_empty_response - # _, streamed = streaming_request(create_connection, :get, 'empty_stream') - # assert_equal [["", 0]], streamed - # end - # - # def test_non_GET_streaming_empty_response - # _, streamed = streaming_request(create_connection, :post, 'empty_stream') - # assert_equal [["", 0]], streamed - # end end describe '#post' do diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb index 8b6dd5294..b3b424c26 100644 --- a/spec/support/shared_examples/request_method.rb +++ b/spec/support/shared_examples/request_method.rb @@ -47,17 +47,17 @@ conn.public_send(http_method, '/', payload) end - it 'sends files' do - # Can't send files on get, head and delete methods - skip unless method_with_body?(http_method) - - payload = { uploaded_file: multipart_file } - request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| - # WebMock does not support matching body for multipart/form-data requests yet :( - # https://github.com/bblimke/webmock/issues/623 - request.body =~ %r[RubyMultipartPost] + # Can't send files on get, head and delete methods + if method_with_body?(http_method) + it 'sends files' do + payload = { uploaded_file: multipart_file } + request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| + # WebMock does not support matching body for multipart/form-data requests yet :( + # https://github.com/bblimke/webmock/issues/623 + request.body =~ %r[RubyMultipartPost] + end + conn.public_send(http_method, '/', payload) end - conn.public_send(http_method, '/', payload) end end @@ -68,6 +68,76 @@ end end + on_feature :compression do + # Accept-Encoding header not sent for HEAD requests as body is not expected in the response. + unless http_method == :head + it 'handles gzip compression' do + request_stub.with(headers: { 'Accept-Encoding' => %r[\bgzip\b] }) + conn.public_send(http_method, '/') + end + + it 'handles deflate compression' do + request_stub.with(headers: { 'Accept-Encoding' => %r[\bdeflate\b] }) + conn.public_send(http_method, '/') + end + end + end + + on_feature :streaming do + describe 'streaming' do + let(:streamed) { [] } + + context 'when response is empty' do + it do + conn.public_send(http_method, '/') do |req| + req.options.on_data = Proc.new { |*args| streamed << args } + end + + expect(streamed).to eq([["", 0]]) + end + end + + context 'when response contains big data' do + before { request_stub.to_return(body: big_string) } + + + it 'handles streaming' do + response = conn.public_send(http_method, '/') do |req| + req.options.on_data = Proc.new { |*args| streamed << args } + end + + expect(response.body).to eq('') + check_streaming_response(streamed, chunk_size: 16 * 1024) + end + end + end + end + + on_feature :parallel do + it 'handles parallel requests' do + resp1, resp2 = nil, nil + payload1 = { a: '1' } + payload2 = { b: '2' } + request_stub.with(Hash[query_or_body, payload1]) + .to_return(body: payload1.to_json) + stub_request(http_method, remote).with(Hash[query_or_body, payload2]) + .to_return(body: payload2.to_json) + + conn.in_parallel do + resp1 = conn.public_send(http_method, '/', payload1) + resp2 = conn.public_send(http_method, '/', payload2) + + expect(conn.in_parallel?).to be_truthy + expect(resp1.body).to be_nil + expect(resp2.body).to be_nil + end + + expect(conn.in_parallel?).to be_falsey + expect(resp1.body).to eq(payload1.to_json) + expect(resp2.body).to eq(payload2.to_json) + end + end + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 # it 'handles open timeout responses' do # request_stub.to_timeout diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index ad4c2b1cb..c63e55bfd 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -25,21 +25,21 @@ def self.apply(base, *extra_features) end module Parallel - def test_in_parallel - resp1, resp2 = nil, nil - - connection = create_connection - connection.in_parallel do - resp1 = connection.get('echo?a=1') - resp2 = connection.get('echo?b=2') - assert connection.in_parallel? - assert_nil resp1.body - assert_nil resp2.body - end - assert !connection.in_parallel? - assert_equal 'get ?{"a"=>"1"}', resp1.body - assert_equal 'get ?{"b"=>"2"}', resp2.body - end + # def test_in_parallel + # resp1, resp2 = nil, nil + # + # connection = create_connection + # connection.in_parallel do + # resp1 = connection.get('echo?a=1') + # resp2 = connection.get('echo?b=2') + # assert connection.in_parallel? + # assert_nil resp1.body + # assert_nil resp2.body + # end + # assert !connection.in_parallel? + # assert_equal 'get ?{"a"=>"1"}', resp1.body + # assert_equal 'get ?{"b"=>"2"}', resp2.body + # end end module NonParallel @@ -83,10 +83,6 @@ def test_callback_is_called_in_parallel_with_no_streaming_support end end - module Streaming - - end - module NonStreaming include Faraday::Shared @@ -113,10 +109,6 @@ def test_non_GET_streaming end end - module Compression - - end - module SSL def test_GET_ssl_fails_with_bad_cert ca_file = 'tmp/faraday-different-ca-cert.crt' From eb08c1e22c23790cf3f736ad910217d84bbe7a9f Mon Sep 17 00:00:00 2001 From: Mattia Giuffrida Date: Thu, 31 May 2018 08:25:24 +0100 Subject: [PATCH 21/46] Updates webmock to fix patron issue with PATCH requests. --- Gemfile | 2 +- spec/support/shared_examples/request_method.rb | 14 +++++++++++--- test/adapters/integration.rb | 18 ------------------ 3 files changed, 12 insertions(+), 22 deletions(-) diff --git a/Gemfile b/Gemfile index 4b6c0c8e0..989acb943 100644 --- a/Gemfile +++ b/Gemfile @@ -27,7 +27,7 @@ group :test do gem 'sinatra', '~> 1.3' gem 'typhoeus', '~> 1.3', :require => 'typhoeus' gem 'rspec', '~> 3.7' - gem 'webmock', '~> 3.3' + gem 'webmock', '~> 3.4' end gemspec diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb index b3b424c26..8e4a883bd 100644 --- a/spec/support/shared_examples/request_method.rb +++ b/spec/support/shared_examples/request_method.rb @@ -33,8 +33,17 @@ expect { conn.public_send(http_method, 'http://localhost:4') }.to raise_error(Faraday::Error::ConnectionFailed) end - # Issue with Patron and PATCH body: https://github.com/toland/patron/issues/163 - unless described_class == Faraday::Adapter::Patron && http_method == :patch + # context 'when wrong ssl certificate is provided' do + # let(:ca_file_path) { 'tmp/faraday-different-ca-cert.crt' } + # before { conn_options.merge!(ssl: { ca_file: ca_file_path }) } + # + # it do + # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::SSLError) # do |ex| + # # expect(ex.message).to include?('certificate') + # # end + # end + # end + it 'sends url encoded parameters' do payload = { name: 'zack' } request_stub.with(Hash[query_or_body, payload]) @@ -59,7 +68,6 @@ conn.public_send(http_method, '/', payload) end end - end on_feature :reason_phrase_parse do it 'parses the reason phrase' do diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index c63e55bfd..b44e2ff1f 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -24,24 +24,6 @@ def self.apply(base, *extra_features) end end - module Parallel - # def test_in_parallel - # resp1, resp2 = nil, nil - # - # connection = create_connection - # connection.in_parallel do - # resp1 = connection.get('echo?a=1') - # resp2 = connection.get('echo?b=2') - # assert connection.in_parallel? - # assert_nil resp1.body - # assert_nil resp2.body - # end - # assert !connection.in_parallel? - # assert_equal 'get ?{"a"=>"1"}', resp1.body - # assert_equal 'get ?{"b"=>"2"}', resp2.body - # end - end - module NonParallel def test_no_parallel_support connection = create_connection From 50b856807cd12251a485d361185063c97eae644d Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 21 Sep 2018 09:47:28 +0100 Subject: [PATCH 22/46] Fixes small issue with logger spec --- spec/faraday/response/logger_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/faraday/response/logger_spec.rb b/spec/faraday/response/logger_spec.rb index 19b978e56..9324454f0 100644 --- a/spec/faraday/response/logger_spec.rb +++ b/spec/faraday/response/logger_spec.rb @@ -38,7 +38,7 @@ it 'logs method and url' do conn.get '/hello', nil, accept: 'text/html' - expect(string_io.string).to match("get http:/hello") + expect(string_io.string).to match("GET http:/hello") end it 'logs request headers by default' do From 752b64f924eb83f8b21397a95a56bdcfc4382c96 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 21 Sep 2018 10:05:32 +0100 Subject: [PATCH 23/46] Switches travis to use RSpec instead of MiniTest --- .travis.yml | 2 -- Rakefile | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index 267bde692..045af2160 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ sudo: false language: ruby -script: bundle exec script/test cache: bundler - rvm: - 2.2 - 2.3 diff --git a/Rakefile b/Rakefile index 2830fb212..e5a08338e 100644 --- a/Rakefile +++ b/Rakefile @@ -4,5 +4,5 @@ task :default => :test desc "Run all tests" task :test do - exec 'script/test' + exec 'rspec' end From 54b24cdf58e558e2b1a1bf14312a577477bf1f25 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 21 Sep 2018 17:34:15 +0100 Subject: [PATCH 24/46] Converts autorization middleware tests. Moves Faraday::Utils classes (Headers and ParamsHash) into separate files. Converts Faraday::Utils::Headers tests. --- lib/faraday/utils.rb | 188 +-------------------- lib/faraday/utils/headers.rb | 131 ++++++++++++++ lib/faraday/utils/params_hash.rb | 60 +++++++ spec/faraday/request/authorization_spec.rb | 86 ++++++++++ spec/faraday/utils/headers_spec.rb | 80 +++++++++ test/authentication_middleware_test.rb | 65 ------- test/env_test.rb | 68 -------- 7 files changed, 360 insertions(+), 318 deletions(-) create mode 100644 lib/faraday/utils/headers.rb create mode 100644 lib/faraday/utils/params_hash.rb create mode 100644 spec/faraday/request/authorization_spec.rb create mode 100644 spec/faraday/utils/headers_spec.rb delete mode 100644 test/authentication_middleware_test.rb diff --git a/lib/faraday/utils.rb b/lib/faraday/utils.rb index 4ab1f3420..46f92e6a7 100644 --- a/lib/faraday/utils.rb +++ b/lib/faraday/utils.rb @@ -1,194 +1,12 @@ require 'thread' +require_relative 'utils/headers' +require_relative 'utils/params_hash' + module Faraday module Utils extend self - # Adapted from Rack::Utils::HeaderHash - class Headers < ::Hash - def self.from(value) - new(value) - end - - def self.allocate - new_self = super - new_self.initialize_names - new_self - end - - def initialize(hash = nil) - super() - @names = {} - self.update(hash || {}) - end - - def initialize_names - @names = {} - end - - # on dup/clone, we need to duplicate @names hash - def initialize_copy(other) - super - @names = other.names.dup - end - - # need to synchronize concurrent writes to the shared KeyMap - keymap_mutex = Mutex.new - - # symbol -> string mapper + cache - KeyMap = Hash.new do |map, key| - value = if key.respond_to?(:to_str) - key - else - key.to_s.split('_'). # :user_agent => %w(user agent) - each { |w| w.capitalize! }. # => %w(User Agent) - join('-') # => "User-Agent" - end - keymap_mutex.synchronize { map[key] = value } - end - KeyMap[:etag] = "ETag" - - def [](k) - k = KeyMap[k] - super(k) || super(@names[k.downcase]) - end - - def []=(k, v) - k = KeyMap[k] - k = (@names[k.downcase] ||= k) - # join multiple values with a comma - v = v.to_ary.join(', ') if v.respond_to? :to_ary - super(k, v) - end - - def fetch(k, *args, &block) - k = KeyMap[k] - key = @names.fetch(k.downcase, k) - super(key, *args, &block) - end - - def delete(k) - k = KeyMap[k] - if k = @names[k.downcase] - @names.delete k.downcase - super(k) - end - end - - def include?(k) - @names.include? k.downcase - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def merge!(other) - other.each { |k, v| self[k] = v } - self - end - alias_method :update, :merge! - - def merge(other) - hash = dup - hash.merge! other - end - - def replace(other) - clear - @names.clear - self.update other - self - end - - def to_hash() ::Hash.new.update(self) end - - def parse(header_string) - return unless header_string && !header_string.empty? - - headers = header_string.split(/\r\n/) - - # Find the last set of response headers. - start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0 - last_response = headers.slice(start_index, headers.size) - - last_response. - tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line - map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines - each { |key, value| - # join multiple values with a comma - if self[key] - self[key] << ', ' << value - else - self[key] = value - end - } - end - - protected - - def names - @names - end - end - - # hash with stringified keys - class ParamsHash < Hash - def [](key) - super(convert_key(key)) - end - - def []=(key, value) - super(convert_key(key), value) - end - - def delete(key) - super(convert_key(key)) - end - - def include?(key) - super(convert_key(key)) - end - - alias_method :has_key?, :include? - alias_method :member?, :include? - alias_method :key?, :include? - - def update(params) - params.each do |key, value| - self[key] = value - end - self - end - alias_method :merge!, :update - - def merge(params) - dup.update(params) - end - - def replace(other) - clear - update(other) - end - - def merge_query(query, encoder = nil) - if query && !query.empty? - update((encoder || Utils.default_params_encoder).decode(query)) - end - self - end - - def to_query(encoder = nil) - (encoder || Utils.default_params_encoder).encode(self) - end - - private - - def convert_key(key) - key.to_s - end - end - def build_query(params) FlatParamsEncoder.encode(params) end diff --git a/lib/faraday/utils/headers.rb b/lib/faraday/utils/headers.rb new file mode 100644 index 000000000..d44d9960d --- /dev/null +++ b/lib/faraday/utils/headers.rb @@ -0,0 +1,131 @@ +module Faraday + module Utils + # Adapted from Rack::Utils::HeaderHash + class Headers < ::Hash + def self.from(value) + new(value) + end + + def self.allocate + new_self = super + new_self.initialize_names + new_self + end + + def initialize(hash = nil) + super() + @names = {} + self.update(hash || {}) + end + + def initialize_names + @names = {} + end + + # on dup/clone, we need to duplicate @names hash + def initialize_copy(other) + super + @names = other.names.dup + end + + # need to synchronize concurrent writes to the shared KeyMap + keymap_mutex = Mutex.new + + # symbol -> string mapper + cache + KeyMap = Hash.new do |map, key| + value = if key.respond_to?(:to_str) + key + else + key.to_s.split('_'). # :user_agent => %w(user agent) + each { |w| w.capitalize! }. # => %w(User Agent) + join('-') # => "User-Agent" + end + keymap_mutex.synchronize { map[key] = value } + end + KeyMap[:etag] = "ETag" + + def [](k) + k = KeyMap[k] + super(k) || super(@names[k.downcase]) + end + + def []=(k, v) + k = KeyMap[k] + k = (@names[k.downcase] ||= k) + # join multiple values with a comma + v = v.to_ary.join(', ') if v.respond_to? :to_ary + super(k, v) + end + + def fetch(k, *args, &block) + k = KeyMap[k] + key = @names.fetch(k.downcase, k) + super(key, *args, &block) + end + + def delete(k) + k = KeyMap[k] + if k = @names[k.downcase] + @names.delete k.downcase + super(k) + end + end + + def include?(k) + @names.include? k.downcase + end + + alias_method :has_key?, :include? + alias_method :member?, :include? + alias_method :key?, :include? + + def merge!(other) + other.each { |k, v| self[k] = v } + self + end + alias_method :update, :merge! + + def merge(other) + hash = dup + hash.merge! other + end + + def replace(other) + clear + @names.clear + self.update other + self + end + + def to_hash() ::Hash.new.update(self) end + + def parse(header_string) + return unless header_string && !header_string.empty? + + headers = header_string.split(/\r\n/) + + # Find the last set of response headers. + start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0 + last_response = headers.slice(start_index, headers.size) + + last_response. + tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line + map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines + each { |key, value| + # join multiple values with a comma + if self[key] + self[key] << ', ' << value + else + self[key] = value + end + } + end + + protected + + def names + @names + end + end + end +end diff --git a/lib/faraday/utils/params_hash.rb b/lib/faraday/utils/params_hash.rb new file mode 100644 index 000000000..cf99ddb1a --- /dev/null +++ b/lib/faraday/utils/params_hash.rb @@ -0,0 +1,60 @@ +module Faraday + module Utils + # Hash with stringified keys + class ParamsHash < Hash + def [](key) + super(convert_key(key)) + end + + def []=(key, value) + super(convert_key(key), value) + end + + def delete(key) + super(convert_key(key)) + end + + def include?(key) + super(convert_key(key)) + end + + alias_method :has_key?, :include? + alias_method :member?, :include? + alias_method :key?, :include? + + def update(params) + params.each do |key, value| + self[key] = value + end + self + end + alias_method :merge!, :update + + def merge(params) + dup.update(params) + end + + def replace(other) + clear + update(other) + end + + def merge_query(query, encoder = nil) + if query && !query.empty? + update((encoder || Utils.default_params_encoder).decode(query)) + end + self + end + + def to_query(encoder = nil) + (encoder || Utils.default_params_encoder).encode(self) + end + + private + + def convert_key(key) + key.to_s + end + end + end +end diff --git a/spec/faraday/request/authorization_spec.rb b/spec/faraday/request/authorization_spec.rb new file mode 100644 index 000000000..e802ef06b --- /dev/null +++ b/spec/faraday/request/authorization_spec.rb @@ -0,0 +1,86 @@ +RSpec.describe Faraday::Request::Authorization do + let(:conn) do + Faraday.new do |b| + b.request auth_type, *auth_config + b.adapter :test do |stub| + stub.get('/auth-echo') do |env| + [200, {}, env[:request_headers]['Authorization']] + end + end + end + end + + shared_examples 'does not interfere with existing authentication' do + context 'and request already has an authentication header' do + let(:response) { conn.get('/auth-echo', nil, authorization: 'Token token="bar"') } + + it 'does not interfere with existing authorization' do + expect(response.body).to eq('Token token="bar"') + end + end + end + + let(:response) { conn.get('/auth-echo') } + + describe 'basic_auth' do + let(:auth_type) { :basic_auth } + + context 'when passed correct params' do + let(:auth_config) { %w(aladdin opensesame) } + + it { expect(response.body).to eq('Basic YWxhZGRpbjpvcGVuc2VzYW1l') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed very long values' do + let(:auth_config) { ['A' * 255, ''] } + + it { expect(response.body).to eq("Basic #{'QUFB' * 85}Og==") } + + include_examples 'does not interfere with existing authentication' + end + end + + describe 'token_auth' do + let(:auth_type) { :token_auth } + + context 'when passed correct params' do + let(:auth_config) { 'quux' } + + it { expect(response.body).to eq('Token token="quux"') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when other values are provided' do + let(:auth_config) { ['baz', foo: 42] } + + it { expect(response.body).to match(/^Token /) } + it { expect(response.body).to match(/token="baz"/) } + it { expect(response.body).to match(/foo="42"/) } + + include_examples 'does not interfere with existing authentication' + end + end + + describe 'authorization' do + let(:auth_type) { :authorization } + + context 'when passed two strings' do + let(:auth_config) { ['custom', 'abc def'] } + + it { expect(response.body).to eq('custom abc def') } + + include_examples 'does not interfere with existing authentication' + end + + context 'when passed a string and a hash' do + let(:auth_config) { ['baz', foo: 42] } + + it { expect(response.body).to eq('baz foo="42"') } + + include_examples 'does not interfere with existing authentication' + end + end +end diff --git a/spec/faraday/utils/headers_spec.rb b/spec/faraday/utils/headers_spec.rb new file mode 100644 index 000000000..1dd5c4172 --- /dev/null +++ b/spec/faraday/utils/headers_spec.rb @@ -0,0 +1,80 @@ +RSpec.describe Faraday::Utils::Headers do + subject { Faraday::Utils::Headers.new } + + context 'when Content-Type is set to application/json' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject.keys).to eq(['Content-Type']) } + it { expect(subject['Content-Type']).to eq('application/json') } + it { expect(subject['CONTENT-TYPE']).to eq('application/json') } + it { expect(subject['content-type']).to eq('application/json') } + it { is_expected.to include('content-type') } + end + + context 'when Content-Type is set to application/xml' do + before { subject['Content-Type'] = 'application/xml' } + + it { expect(subject.keys).to eq(['Content-Type']) } + it { expect(subject['Content-Type']).to eq('application/xml') } + it { expect(subject['CONTENT-TYPE']).to eq('application/xml') } + it { expect(subject['content-type']).to eq('application/xml') } + it { is_expected.to include('content-type') } + end + + describe '#fetch' do + before { subject['Content-Type'] = 'application/json' } + + it { expect(subject.fetch('Content-Type')).to eq('application/json') } + it { expect(subject.fetch('CONTENT-TYPE')).to eq('application/json') } + it { expect(subject.fetch(:content_type)).to eq('application/json') } + it { expect(subject.fetch('invalid', 'default')).to eq('default') } + it { expect(subject.fetch('invalid', false)).to eq(false) } + it { expect(subject.fetch('invalid', nil)).to be_nil } + it { expect(subject.fetch('Invalid') { |key| "#{key} key" }).to eq('Invalid key') } + it 'calls a block when provided' do + block_called = false + expect(subject.fetch('content-type') { block_called = true }).to eq('application/json') + expect(block_called).to be_falsey + end + it 'raises an error if key not found' do + expected_error = defined?(KeyError) ? KeyError : IndexError + expect { subject.fetch('invalid') }.to raise_error(expected_error) + end + end + + describe '#delete' do + before do + subject['Content-Type'] = 'application/json' + @deleted = subject.delete('content-type') + end + + it { expect(@deleted).to eq('application/json') } + it { expect(subject.size).to eq(0) } + it { is_expected.not_to include('content-type') } + it { expect(subject.delete('content-type')).to be_nil } + end + + describe '#parse' do + before { subject.parse(headers) } + + context 'when response headers leave http status line out' do + let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n" } + + it { expect(subject.keys).to eq(%w(Content-Type)) } + it { expect(subject['Content-Type']).to eq('text/html') } + it { expect(subject['content-type']).to eq('text/html') } + end + + context 'when response headers values include a colon' do + let(:headers) { "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n" } + + it { expect(subject['location']).to eq('http://sushi.com/') } + end + + context 'when response headers include a blank line' do + let(:headers) { "HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n" } + + it { expect(subject['content-type']).to eq('text/html') } + end + end +end diff --git a/test/authentication_middleware_test.rb b/test/authentication_middleware_test.rb deleted file mode 100644 index 1fbad6169..000000000 --- a/test/authentication_middleware_test.rb +++ /dev/null @@ -1,65 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class AuthenticationMiddlewareTest < Faraday::TestCase - def conn - Faraday::Connection.new('http://example.net/') do |builder| - yield(builder) - builder.adapter :test do |stub| - stub.get('/auth-echo') do |env| - [200, {}, env[:request_headers]['Authorization']] - end - end - end - end - - def test_basic_middleware_adds_basic_header - response = conn { |b| b.request :basic_auth, 'aladdin', 'opensesame' }.get('/auth-echo') - assert_equal 'Basic YWxhZGRpbjpvcGVuc2VzYW1l', response.body - end - - def test_basic_middleware_adds_basic_header_correctly_with_long_values - response = conn { |b| b.request :basic_auth, 'A' * 255, '' }.get('/auth-echo') - assert_equal "Basic #{'QUFB' * 85}Og==", response.body - end - - def test_basic_middleware_does_not_interfere_with_existing_authorization - response = conn { |b| b.request :basic_auth, 'aladdin', 'opensesame' }. - get('/auth-echo', nil, :authorization => 'Token token="bar"') - assert_equal 'Token token="bar"', response.body - end - - def test_token_middleware_adds_token_header - response = conn { |b| b.request :token_auth, 'quux' }.get('/auth-echo') - assert_equal 'Token token="quux"', response.body - end - - def test_token_middleware_includes_other_values_if_provided - response = conn { |b| - b.request :token_auth, 'baz', :foo => 42 - }.get('/auth-echo') - assert_match(/^Token /, response.body) - assert_match(/token="baz"/, response.body) - assert_match(/foo="42"/, response.body) - end - - def test_token_middleware_does_not_interfere_with_existing_authorization - response = conn { |b| b.request :token_auth, 'quux' }. - get('/auth-echo', nil, :authorization => 'Token token="bar"') - assert_equal 'Token token="bar"', response.body - end - - def test_authorization_middleware_with_string - response = conn { |b| - b.request :authorization, 'custom', 'abc def' - }.get('/auth-echo') - assert_match(/^custom abc def$/, response.body) - end - - def test_authorization_middleware_with_hash - response = conn { |b| - b.request :authorization, 'baz', :foo => 42 - }.get('/auth-echo') - assert_match(/^baz /, response.body) - assert_match(/foo="42"/, response.body) - end -end diff --git a/test/env_test.rb b/test/env_test.rb index fd1cb2242..30f057b04 100644 --- a/test/env_test.rb +++ b/test/env_test.rb @@ -97,75 +97,7 @@ def make_env(method = :get, connection = @conn, &block) end end -class HeadersTest < Faraday::TestCase - def setup - @headers = Faraday::Utils::Headers.new - end - - def test_normalizes_different_capitalizations - @headers['Content-Type'] = 'application/json' - assert_equal ['Content-Type'], @headers.keys - assert_equal 'application/json', @headers['Content-Type'] - assert_equal 'application/json', @headers['CONTENT-TYPE'] - assert_equal 'application/json', @headers['content-type'] - assert @headers.include?('content-type') - - @headers['content-type'] = 'application/xml' - assert_equal ['Content-Type'], @headers.keys - assert_equal 'application/xml', @headers['Content-Type'] - assert_equal 'application/xml', @headers['CONTENT-TYPE'] - assert_equal 'application/xml', @headers['content-type'] - end - - def test_fetch_key - @headers['Content-Type'] = 'application/json' - block_called = false - assert_equal 'application/json', @headers.fetch('content-type') { block_called = true } - assert_equal 'application/json', @headers.fetch('Content-Type') - assert_equal 'application/json', @headers.fetch('CONTENT-TYPE') - assert_equal 'application/json', @headers.fetch(:content_type) - assert_equal false, block_called - assert_equal 'default', @headers.fetch('invalid', 'default') - assert_equal false, @headers.fetch('invalid', false) - assert_nil @headers.fetch('invalid', nil) - - assert_equal 'Invalid key', @headers.fetch('Invalid') { |key| "#{key} key" } - - expected_error = defined?(KeyError) ? KeyError : IndexError - assert_raises(expected_error) { @headers.fetch('invalid') } - end - - def test_delete_key - @headers['Content-Type'] = 'application/json' - assert_equal 1, @headers.size - assert @headers.include?('content-type') - assert_equal 'application/json', @headers.delete('content-type') - assert_equal 0, @headers.size - assert !@headers.include?('content-type') - assert_nil @headers.delete('content-type') - end - - def test_parse_response_headers_leaves_http_status_line_out - @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n") - assert_equal %w(Content-Type), @headers.keys - end - - def test_parse_response_headers_parses_lower_cased_header_name_and_value - @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n") - assert_equal 'text/html', @headers['content-type'] - end - - def test_parse_response_headers_parses_lower_cased_header_name_and_value_with_colon - @headers.parse("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nLocation: http://sushi.com/\r\n\r\n") - assert_equal 'http://sushi.com/', @headers['location'] - end - - def test_parse_response_headers_parses_blank_lines - @headers.parse("HTTP/1.1 200 OK\r\n\r\nContent-Type: text/html\r\n\r\n") - assert_equal 'text/html', @headers['content-type'] - end -end class ResponseTest < Faraday::TestCase def setup From f774396cae13f22d675a96f2fc4876fa046f3d8f Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 28 Sep 2018 14:30:48 +0100 Subject: [PATCH 25/46] Style fixes to headers.rb --- lib/faraday/utils/headers.rb | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/lib/faraday/utils/headers.rb b/lib/faraday/utils/headers.rb index d44d9960d..6881b3e57 100644 --- a/lib/faraday/utils/headers.rb +++ b/lib/faraday/utils/headers.rb @@ -36,9 +36,9 @@ def initialize_copy(other) value = if key.respond_to?(:to_str) key else - key.to_s.split('_'). # :user_agent => %w(user agent) - each { |w| w.capitalize! }. # => %w(User Agent) - join('-') # => "User-Agent" + key.to_s.split('_') # :user_agent => %w(user agent) + .each { |w| w.capitalize! } # => %w(User Agent) + .join('-') # => "User-Agent" end keymap_mutex.synchronize { map[key] = value } end @@ -65,7 +65,7 @@ def fetch(k, *args, &block) def delete(k) k = KeyMap[k] - if k = @names[k.downcase] + if (k = @names[k.downcase]) @names.delete k.downcase super(k) end @@ -83,6 +83,7 @@ def merge!(other) other.each { |k, v| self[k] = v } self end + alias_method :update, :merge! def merge(other) @@ -97,7 +98,9 @@ def replace(other) self end - def to_hash() ::Hash.new.update(self) end + def to_hash + ::Hash.new.update(self) + end def parse(header_string) return unless header_string && !header_string.empty? @@ -108,17 +111,11 @@ def parse(header_string) start_index = headers.rindex { |x| x.match(/^HTTP\//) } || 0 last_response = headers.slice(start_index, headers.size) - last_response. - tap { |a| a.shift if a.first.index('HTTP/') == 0 }. # drop the HTTP status line - map { |h| h.split(/:\s*/, 2) }.reject { |p| p[0].nil? }. # split key and value, ignore blank lines - each { |key, value| - # join multiple values with a comma - if self[key] - self[key] << ', ' << value - else - self[key] = value - end - } + last_response + .tap { |a| a.shift if a.first.index('HTTP/') == 0 } # drop the HTTP status line + .map { |h| h.split(/:\s*/, 2) } # split key and value + .reject { |p| p[0].nil? } # ignore blank lines + .each { |key, value| add_parsed(key, value) } # join multiple values with a comma end protected @@ -126,6 +123,12 @@ def parse(header_string) def names @names end + + private + + def add_parsed(key, value) + self[key] ? self[key] << ', ' << value : self[key] = value + end end end end From 7cfc5849483e300a663d8bc6ad07b39625b06a4b Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 28 Sep 2018 17:06:17 +0100 Subject: [PATCH 26/46] Ignores /spec folder from when calculating coverage %. Converts and isolates Faraday::Request and Faraday::Response tests. --- spec/faraday/options/env_spec.rb | 33 +++-- spec/faraday/request_spec.rb | 107 +++++++++++++++++ spec/faraday/response_spec.rb | 74 ++++++++++++ spec/spec_helper.rb | 1 + test/env_test.rb | 200 ------------------------------- 5 files changed, 203 insertions(+), 212 deletions(-) create mode 100644 spec/faraday/request_spec.rb create mode 100644 spec/faraday/response_spec.rb delete mode 100644 test/env_test.rb diff --git a/spec/faraday/options/env_spec.rb b/spec/faraday/options/env_spec.rb index c23286f98..6ff52e07f 100644 --- a/spec/faraday/options/env_spec.rb +++ b/spec/faraday/options/env_spec.rb @@ -1,23 +1,21 @@ RSpec.describe Faraday::Env do + subject { Faraday::Env.new } it 'allows to access members' do - e = Faraday::Env.new - expect(e.method).to be_nil - e.method = :get - expect(e.method).to eq(:get) + expect(subject.method).to be_nil + subject.method = :get + expect(subject.method).to eq(:get) end it 'allows to access symbol non members' do - e = Faraday::Env.new - expect(e[:custom]).to be_nil - e[:custom] = :boom - expect(e[:custom]).to eq(:boom) + expect(subject[:custom]).to be_nil + subject[:custom] = :boom + expect(subject[:custom]).to eq(:boom) end it 'allows to access string non members' do - e = Faraday::Env.new - expect(e['custom']).to be_nil - e['custom'] = :boom - expect(e['custom']).to eq(:boom) + expect(subject['custom']).to be_nil + subject['custom'] = :boom + expect(subject['custom']).to eq(:boom) end it 'ignores false when fetching' do @@ -25,4 +23,15 @@ ssl.verify = false expect(ssl.fetch(:verify, true)).to be_falsey end + + it 'retains custom members' do + subject[:foo] = "custom 1" + subject[:bar] = :custom_2 + env2 = Faraday::Env.from(subject) + env2[:baz] = "custom 3" + + expect(env2[:foo]).to eq("custom 1") + expect(env2[:bar]).to eq(:custom_2) + expect(subject[:baz]).to be_nil + end end \ No newline at end of file diff --git a/spec/faraday/request_spec.rb b/spec/faraday/request_spec.rb new file mode 100644 index 000000000..77b2a136b --- /dev/null +++ b/spec/faraday/request_spec.rb @@ -0,0 +1,107 @@ +RSpec.describe Faraday::Request do + let(:conn) do + Faraday.new(url: 'http://sushi.com/api', + headers: { 'Mime-Version' => '1.0' }, + request: { oauth: { consumer_key: 'anonymous' } }) + end + let(:method) { :get } + let(:block) { nil } + + subject { conn.build_request(method, &block) } + + context 'when nothing particular is configured' do + it { expect(subject.method).to eq(:get) } + it { expect(subject.to_env(conn).ssl.verify).to be_falsey } + end + + context 'when method is post' do + let(:method) { :post } + + it { expect(subject.method).to eq(:post) } + end + + context 'when setting the url on setup with a URI' do + let(:block) { Proc.new { |req| req.url URI.parse('foo.json?a=1') } } + + it { expect(subject.path).to eq(URI.parse('foo.json')) } + it { expect(subject.params).to eq({ 'a' => '1' }) } + it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1') } + end + + context 'when setting the url on setup with a string path and params' do + let(:block) { Proc.new { |req| req.url 'foo.json', { 'a' => 1 } } } + + it { expect(subject.path).to eq('foo.json') } + it { expect(subject.params).to eq({ 'a' => 1 }) } + it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1') } + end + + context 'when setting the url on setup with a path including params' do + let(:block) { Proc.new { |req| req.url 'foo.json?b=2&a=1#qqq' } } + + it { expect(subject.path).to eq('foo.json') } + it { expect(subject.params).to eq({ 'a' => '1', 'b' => '2' }) } + it { expect(subject.to_env(conn).url.to_s).to eq('http://sushi.com/api/foo.json?a=1&b=2') } + end + + context 'when setting a header on setup with []= syntax' do + let(:block) { Proc.new { |req| req['Server'] = 'Faraday' } } + let(:headers) { subject.to_env(conn).request_headers } + + it { expect(subject.headers['Server']).to eq('Faraday') } + it { expect(headers['mime-version']).to eq('1.0') } + it { expect(headers['server']).to eq('Faraday') } + end + + context 'when setting the body on setup' do + let(:block) { Proc.new { |req| req.body = 'hi' } } + + it { expect(subject.body).to eq('hi') } + it { expect(subject.to_env(conn).body).to eq('hi') } + end + + context 'with global request options set' do + let(:env_request) { subject.to_env(conn).request } + + before do + conn.options.timeout = 3 + conn.options.open_timeout = 5 + conn.ssl.verify = false + conn.proxy = 'http://proxy.com' + end + + it { expect(subject.options.timeout).to eq(3) } + it { expect(subject.options.open_timeout).to eq(5) } + it { expect(env_request.timeout).to eq(3) } + it { expect(env_request.open_timeout).to eq(5) } + + context 'and per-request options set' do + let(:block) do + Proc.new do |req| + req.options.timeout = 10 + req.options.boundary = 'boo' + req.options.oauth[:consumer_secret] = 'xyz' + req.options.context = { + foo: 'foo', + bar: 'bar' + } + end + end + + it { expect(subject.options.timeout).to eq(10) } + it { expect(subject.options.open_timeout).to eq(5) } + it { expect(env_request.timeout).to eq(10) } + it { expect(env_request.open_timeout).to eq(5) } + it { expect(env_request.boundary).to eq('boo') } + it { expect(env_request.context).to eq({ foo: 'foo', bar: 'bar' }) } + it do + oauth_expected = { consumer_secret: 'xyz', consumer_key: 'anonymous' } + expect(env_request.oauth).to eq(oauth_expected) + end + end + end + + it 'supports marshal serialization' do + expect(Marshal.load(Marshal.dump(subject))).to eq(subject) + end +end diff --git a/spec/faraday/response_spec.rb b/spec/faraday/response_spec.rb new file mode 100644 index 000000000..805e9ea26 --- /dev/null +++ b/spec/faraday/response_spec.rb @@ -0,0 +1,74 @@ +RSpec.describe Faraday::Response do + subject { Faraday::Response.new(env) } + + let(:env) do + Faraday::Env.from(status: 404, body: 'yikes', + response_headers: { 'Content-Type' => 'text/plain' }) + end + + it { expect(subject.finished?).to be_truthy } + it { expect { subject.finish({}) }.to raise_error(RuntimeError) } + it { expect(subject.success?).to be_falsey } + it { expect(subject.status).to eq(404) } + it { expect(subject.body).to eq('yikes') } + it { expect(subject.headers['Content-Type']).to eq('text/plain') } + it { expect(subject['content-type']).to eq('text/plain') } + + describe '#apply_request' do + before { subject.apply_request(body: 'a=b', method: :post) } + + it { expect(subject.body).to eq('yikes') } + it { expect(subject.env[:method]).to eq(:post) } + end + + describe '#to_hash' do + let(:hash) { subject.to_hash } + + it { expect(hash).to be_a(Hash) } + it { expect(hash).to eq(env.to_hash) } + it { expect(hash[:status]).to eq(subject.status) } + it { expect(hash[:response_headers]).to eq(subject.headers) } + it { expect(hash[:body]).to eq(subject.body) } + end + + describe 'marshal serialization support' do + subject { Faraday::Response.new } + let(:loaded) { Marshal.load(Marshal.dump(subject)) } + + before do + subject.on_complete {} + subject.finish(env.merge(params: 'moo')) + end + + it { expect(loaded.env[:params]).to be_nil } + it { expect(loaded.env[:body]).to eq(env[:body]) } + it { expect(loaded.env[:response_headers]).to eq(env[:response_headers]) } + it { expect(loaded.env[:status]).to eq(env[:status]) } + end + + describe '#on_complete' do + subject { Faraday::Response.new } + + it 'parse body on finish' do + subject.on_complete { |env| env[:body] = env[:body].upcase } + subject.finish(env) + + expect(subject.body).to eq('YIKES') + end + + it 'can access response body in on_complete callback' do + subject.on_complete { |env| env[:body] = subject.body.upcase } + subject.finish(env) + + expect(subject.body).to eq('YIKES') + end + + it 'can access response body in on_complete callback' do + callback_env = nil + subject.on_complete { |env| callback_env = env } + subject.finish({}) + + expect(subject.env).to eq(callback_env) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a730f9c27..1164bc67e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -22,6 +22,7 @@ SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter, Coveralls::SimpleCov::Formatter] SimpleCov.start do + add_filter '/spec/' minimum_coverage 90 minimum_coverage_by_file 70 end diff --git a/test/env_test.rb b/test/env_test.rb deleted file mode 100644 index 30f057b04..000000000 --- a/test/env_test.rb +++ /dev/null @@ -1,200 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class EnvTest < Faraday::TestCase - def setup - @conn = Faraday.new :url => 'http://sushi.com/api', - :headers => {'Mime-Version' => '1.0'}, - :request => {:oauth => {:consumer_key => 'anonymous'}} - - @conn.options.timeout = 3 - @conn.options.open_timeout = 5 - @conn.ssl.verify = false - @conn.proxy = 'http://proxy.com' - end - - def test_request_create_stores_method - env = make_env(:get) - assert_equal :get, env.method - end - - def test_request_create_stores_uri - env = make_env do |req| - req.url 'foo.json', 'a' => 1 - end - assert_equal 'http://sushi.com/api/foo.json?a=1', env.url.to_s - end - - def test_request_create_stores_uri_with_anchor - env = make_env do |req| - req.url 'foo.json?b=2&a=1#qqq' - end - assert_equal 'http://sushi.com/api/foo.json?a=1&b=2', env.url.to_s - end - - def test_request_create_stores_headers - env = make_env do |req| - req['Server'] = 'Faraday' - end - headers = env.request_headers - assert_equal '1.0', headers['mime-version'] - assert_equal 'Faraday', headers['server'] - end - - def test_request_create_stores_body - env = make_env do |req| - req.body = 'hi' - end - assert_equal 'hi', env.body - end - - def test_global_request_options - env = make_env - assert_equal 3, env.request.timeout - assert_equal 5, env.request.open_timeout - end - - def test_per_request_options - env = make_env do |req| - req.options.timeout = 10 - req.options.boundary = 'boo' - req.options.oauth[:consumer_secret] = 'xyz' - req.options.context = { - foo: 'foo', - bar: 'bar' - } - end - - assert_equal 10, env.request.timeout - assert_equal 5, env.request.open_timeout - assert_equal 'boo', env.request.boundary - assert_equal env.request.context, { foo: 'foo', bar: 'bar' } - - oauth_expected = {:consumer_secret => 'xyz', :consumer_key => 'anonymous'} - assert_equal oauth_expected, env.request.oauth - end - - def test_request_create_stores_ssl_options - env = make_env - assert_equal false, env.ssl.verify - end - - def test_custom_members_are_retained - env = make_env - env[:foo] = "custom 1" - env[:bar] = :custom_2 - env2 = Faraday::Env.from(env) - assert_equal "custom 1", env2[:foo] - assert_equal :custom_2, env2[:bar] - env2[:baz] = "custom 3" - assert_nil env[:baz] - end - - private - - def make_env(method = :get, connection = @conn, &block) - request = connection.build_request(method, &block) - request.to_env(connection) - end -end - - - -class ResponseTest < Faraday::TestCase - def setup - @env = Faraday::Env.from \ - :status => 404, :body => 'yikes', - :response_headers => {'Content-Type' => 'text/plain'} - @response = Faraday::Response.new @env - end - - def test_finished - assert @response.finished? - end - - def test_error_on_finish - assert_raises RuntimeError do - @response.finish({}) - end - end - - def test_body_is_parsed_on_finish - response = Faraday::Response.new - response.on_complete { |env| env[:body] = env[:body].upcase } - response.finish(@env) - - assert_equal "YIKES", response.body - end - - def test_response_body_is_available_during_on_complete - response = Faraday::Response.new - response.on_complete { |env| env[:body] = response.body.upcase } - response.finish(@env) - - assert_equal "YIKES", response.body - end - - def test_env_in_on_complete_is_identical_to_response_env - response = Faraday::Response.new - callback_env = nil - response.on_complete { |env| callback_env = env } - response.finish({}) - - assert_same response.env, callback_env - end - - def test_not_success - assert !@response.success? - end - - def test_status - assert_equal 404, @response.status - end - - def test_body - assert_equal 'yikes', @response.body - end - - def test_headers - assert_equal 'text/plain', @response.headers['Content-Type'] - assert_equal 'text/plain', @response['content-type'] - end - - def test_apply_request - @response.apply_request :body => 'a=b', :method => :post - assert_equal 'yikes', @response.body - assert_equal :post, @response.env[:method] - end - - def test_marshal_response - @response = Faraday::Response.new - @response.on_complete { } - @response.finish @env.merge(:params => 'moo') - - loaded = Marshal.load Marshal.dump(@response) - assert_nil loaded.env[:params] - assert_equal %w[body response_headers status], loaded.env.keys.map { |k| k.to_s }.sort - end - - def test_marshal_request - @request = Faraday::Request.create(:post) do |request| - request.options = Faraday::RequestOptions.new - request.params = Faraday::Utils::ParamsHash.new({ 'a' => 'c' }) - request.headers = { 'b' => 'd' } - request.body = 'hello, world!' - request.url 'http://localhost/foo' - end - - loaded = Marshal.load(Marshal.dump(@request)) - - assert_equal @request, loaded - end - - def test_hash - hash = @response.to_hash - assert_kind_of Hash, hash - assert_equal @env.to_hash, hash - assert_equal hash[:status], @response.status - assert_equal hash[:response_headers], @response.headers - assert_equal hash[:body], @response.body - end -end From e1d75486f79116ae253930fb343bc82c02f249e0 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 28 Sep 2018 17:39:32 +0100 Subject: [PATCH 27/46] Test SSL enabled and disabled in the same test run. Comments out unused function. --- lib/faraday/utils.rb | 72 ++++++++++++------------- spec/support/shared_examples/adapter.rb | 12 +++++ 2 files changed, 48 insertions(+), 36 deletions(-) diff --git a/lib/faraday/utils.rb b/lib/faraday/utils.rb index 46f92e6a7..a393d610a 100644 --- a/lib/faraday/utils.rb +++ b/lib/faraday/utils.rb @@ -44,42 +44,42 @@ class << self attr_writer :default_params_encoder end - # Stolen from Rack - def normalize_params(params, name, v = nil) - name =~ %r(\A[\[\]]*([^\[\]]+)\]*) - k = $1 || '' - after = $' || '' - - return if k.empty? - - if after == "" - if params[k] - params[k] = Array[params[k]] unless params[k].kind_of?(Array) - params[k] << v - else - params[k] = v - end - elsif after == "[]" - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - params[k] << v - elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - child_key = $1 - params[k] ||= [] - raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - normalize_params(params[k].last, child_key, v) - else - params[k] << normalize_params({}, child_key, v) - end - else - params[k] ||= {} - raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - params[k] = normalize_params(params[k], after, v) - end - - return params - end + # # Stolen from Rack + # def normalize_params(params, name, v = nil) + # name =~ %r(\A[\[\]]*([^\[\]]+)\]*) + # k = $1 || '' + # after = $' || '' + # + # return if k.empty? + # + # if after == "" + # if params[k] + # params[k] = Array[params[k]] unless params[k].kind_of?(Array) + # params[k] << v + # else + # params[k] = v + # end + # elsif after == "[]" + # params[k] ||= [] + # raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) + # params[k] << v + # elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) + # child_key = $1 + # params[k] ||= [] + # raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) + # if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) + # normalize_params(params[k].last, child_key, v) + # else + # params[k] << normalize_params({}, child_key, v) + # end + # else + # params[k] ||= {} + # raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) + # params[k] = normalize_params(params[k], after, v) + # end + # + # return params + # end # Normalize URI() behavior across Ruby versions # diff --git a/spec/support/shared_examples/adapter.rb b/spec/support/shared_examples/adapter.rb index 8aa796e8b..040eac967 100644 --- a/spec/support/shared_examples/adapter.rb +++ b/spec/support/shared_examples/adapter.rb @@ -1,4 +1,16 @@ shared_examples 'an adapter' do |**options| + context 'with SSL enabled' do + before { ENV['SSL'] = 'yes' } + include_examples 'adapter examples', options + end + + context 'with SSL disabled' do + before { ENV['SSL'] = 'no' } + include_examples 'adapter examples' + end +end + +shared_examples 'adapter examples' do |**options| include Faraday::StreamingResponseChecker let(:adapter) { described_class.name.split('::').last } From 20cff7b8a744572c5b028eaf41003cffe2cf2be0 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 28 Sep 2018 17:40:56 +0100 Subject: [PATCH 28/46] Actaully, let's remove it! --- lib/faraday/utils.rb | 37 ------------------------------------- 1 file changed, 37 deletions(-) diff --git a/lib/faraday/utils.rb b/lib/faraday/utils.rb index a393d610a..52a610a9b 100644 --- a/lib/faraday/utils.rb +++ b/lib/faraday/utils.rb @@ -44,43 +44,6 @@ class << self attr_writer :default_params_encoder end - # # Stolen from Rack - # def normalize_params(params, name, v = nil) - # name =~ %r(\A[\[\]]*([^\[\]]+)\]*) - # k = $1 || '' - # after = $' || '' - # - # return if k.empty? - # - # if after == "" - # if params[k] - # params[k] = Array[params[k]] unless params[k].kind_of?(Array) - # params[k] << v - # else - # params[k] = v - # end - # elsif after == "[]" - # params[k] ||= [] - # raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - # params[k] << v - # elsif after =~ %r(^\[\]\[([^\[\]]+)\]$) || after =~ %r(^\[\](.+)$) - # child_key = $1 - # params[k] ||= [] - # raise TypeError, "expected Array (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Array) - # if params[k].last.is_a?(Hash) && !params[k].last.key?(child_key) - # normalize_params(params[k].last, child_key, v) - # else - # params[k] << normalize_params({}, child_key, v) - # end - # else - # params[k] ||= {} - # raise TypeError, "expected Hash (got #{params[k].class.name}) for param `#{k}'" unless params[k].is_a?(Hash) - # params[k] = normalize_params(params[k], after, v) - # end - # - # return params - # end - # Normalize URI() behavior across Ruby versions # # url - A String or URI. From e137e0b898a9c78e4539fa809744f33c53c41b91 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Wed, 3 Oct 2018 15:05:25 +0100 Subject: [PATCH 29/46] Fix proxy and timeout tests. --- .../support/shared_examples/request_method.rb | 115 +++++++++--------- 1 file changed, 55 insertions(+), 60 deletions(-) diff --git a/spec/support/shared_examples/request_method.rb b/spec/support/shared_examples/request_method.rb index 8e4a883bd..6f76260d5 100644 --- a/spec/support/shared_examples/request_method.rb +++ b/spec/support/shared_examples/request_method.rb @@ -39,35 +39,53 @@ # # it do # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::SSLError) # do |ex| - # # expect(ex.message).to include?('certificate') - # # end + # expect(ex.message).to include?('certificate') + # end # end # end - it 'sends url encoded parameters' do - payload = { name: 'zack' } - request_stub.with(Hash[query_or_body, payload]) - conn.public_send(http_method, '/', payload) - end + it 'sends url encoded parameters' do + payload = { name: 'zack' } + request_stub.with(Hash[query_or_body, payload]) + conn.public_send(http_method, '/', payload) + end - it 'sends url encoded nested parameters' do - payload = { name: { first: 'zack' } } - request_stub.with(Hash[query_or_body, payload]) - conn.public_send(http_method, '/', payload) - end + it 'sends url encoded nested parameters' do + payload = { name: { first: 'zack' } } + request_stub.with(Hash[query_or_body, payload]) + conn.public_send(http_method, '/', payload) + end - # Can't send files on get, head and delete methods - if method_with_body?(http_method) - it 'sends files' do - payload = { uploaded_file: multipart_file } - request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| - # WebMock does not support matching body for multipart/form-data requests yet :( - # https://github.com/bblimke/webmock/issues/623 - request.body =~ %r[RubyMultipartPost] - end - conn.public_send(http_method, '/', payload) + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # Should raise Faraday::TimeoutError + it 'supports timeout option' do + conn_options[:request] = { timeout: 1 } + request_stub.to_timeout + exc = adapter == 'NetHttp' ? Faraday::Error::ConnectionFailed : Faraday::TimeoutError + expect { conn.public_send(http_method, '/') }.to raise_error(exc) + end + + # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 + # Should raise Faraday::Error::ConnectionFailed + it 'supports open_timeout option' do + conn_options[:request] = { open_timeout: 1 } + request_stub.to_timeout + exc = adapter == 'NetHttp' ? Faraday::Error::ConnectionFailed : Faraday::TimeoutError + expect { conn.public_send(http_method, '/') }.to raise_error(exc) + end + + # Can't send files on get, head and delete methods + if method_with_body?(http_method) + it 'sends files' do + payload = { uploaded_file: multipart_file } + request_stub.with(headers: { "Content-Type" => %r[\Amultipart/form-data] }) do |request| + # WebMock does not support matching body for multipart/form-data requests yet :( + # https://github.com/bblimke/webmock/issues/623 + request.body =~ %r[RubyMultipartPost] end + conn.public_send(http_method, '/', payload) end + end on_feature :reason_phrase_parse do it 'parses the reason phrase' do @@ -127,9 +145,9 @@ payload1 = { a: '1' } payload2 = { b: '2' } request_stub.with(Hash[query_or_body, payload1]) - .to_return(body: payload1.to_json) + .to_return(body: payload1.to_json) stub_request(http_method, remote).with(Hash[query_or_body, payload2]) - .to_return(body: payload2.to_json) + .to_return(body: payload2.to_json) conn.in_parallel do resp1 = conn.public_send(http_method, '/', payload1) @@ -146,41 +164,18 @@ end end - # TODO: This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 - # it 'handles open timeout responses' do - # request_stub.to_timeout - # expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::Error::ConnectionFailed) - # end + it 'handles requests with proxy' do + conn_options[:proxy] = 'http://google.co.uk' - # TODO: Fix proxy tests - # it 'handles requests with proxy' do - # conn_options[:proxy] = 'http://google.co.uk' - # # stub_request(:get, 'http://example.com/') - # - # # binding.pry - # conn.public_send(http_method, '/') - # # assert_equal 'get', res.body - # - # # unless self.class.ssl_mode? - # # # proxy can't append "Via" header for HTTPS responses - # # assert_match(/:#{proxy_uri.port}$/, res['via']) - # # end - # end - # - # it 'handles proxy failures' do - # proxy_uri = URI(ENV['LIVE_PROXY']) - # proxy_uri.password = 'WRONG' - # conn = create_connection(:proxy => proxy_uri) - # - # err = assert_raises Faraday::Error::ConnectionFailed do - # conn.get '/echo' - # end - # - # unless self.class.ssl_mode? && (self.class.jruby? || - # adapter == :em_http || adapter == :em_synchrony) - # # JRuby raises "End of file reached" which cannot be distinguished from a 407 - # # EM raises "connection closed by server" due to https://github.com/igrigorik/em-socksify/pull/19 - # assert_equal %{407 "Proxy Authentication Required "}, err.message - # end - # end + # binding.pry + res = conn.public_send(http_method, '/') + expect(res.status).to eq(200) + end + + it 'handles proxy failures' do + conn_options[:proxy] = 'http://google.co.uk' + request_stub.to_return(status: 407) + + expect { conn.public_send(http_method, '/') }.to raise_error(Faraday::Error::ConnectionFailed) + end end \ No newline at end of file From 374ffedfc189237b39d8088e757d86a208f66a53 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 15 Oct 2018 16:31:54 +0100 Subject: [PATCH 30/46] Converts adapters tests to RSpec. --- spec/faraday/adapter/excon_spec.rb | 10 ++ spec/faraday/adapter/httpclient_spec.rb | 28 ++++++ .../adapter/net_http_persistent_spec.rb | 27 +++++- spec/faraday/adapter/net_http_spec.rb | 43 +++++++++ spec/faraday/adapter/patron_spec.rb | 11 +++ test/adapters/default_test.rb | 14 --- test/adapters/excon_test.rb | 30 ------ test/adapters/httpclient_test.rb | 34 ------- test/adapters/integration.rb | 38 -------- test/adapters/logger_test.rb | 13 --- test/adapters/net_http_persistent_test.rb | 92 ------------------- test/adapters/net_http_test.rb | 79 ---------------- test/adapters/patron_test.rb | 40 -------- test/adapters/typhoeus_test.rb | 38 -------- 14 files changed, 118 insertions(+), 379 deletions(-) delete mode 100644 test/adapters/default_test.rb delete mode 100644 test/adapters/excon_test.rb delete mode 100644 test/adapters/httpclient_test.rb delete mode 100644 test/adapters/logger_test.rb delete mode 100644 test/adapters/net_http_persistent_test.rb delete mode 100644 test/adapters/net_http_test.rb delete mode 100644 test/adapters/patron_test.rb delete mode 100644 test/adapters/typhoeus_test.rb diff --git a/spec/faraday/adapter/excon_spec.rb b/spec/faraday/adapter/excon_spec.rb index 13b93a53b..f3faed49e 100644 --- a/spec/faraday/adapter/excon_spec.rb +++ b/spec/faraday/adapter/excon_spec.rb @@ -2,4 +2,14 @@ features :body_on_get, :reason_phrase_parse it_behaves_like 'an adapter' + + it 'allows to provide adapter specific configs' do + url = URI('https://example.com:1234') + + adapter = Faraday::Adapter::Excon.new(nil, debug_request: true) + + conn = adapter.create_connection({ url: url }, {}) + + expect(conn.data[:debug_request]).to be_truthy + end end \ No newline at end of file diff --git a/spec/faraday/adapter/httpclient_spec.rb b/spec/faraday/adapter/httpclient_spec.rb index 9c16a3ab5..3167af3b7 100644 --- a/spec/faraday/adapter/httpclient_spec.rb +++ b/spec/faraday/adapter/httpclient_spec.rb @@ -2,4 +2,32 @@ features :body_on_get, :reason_phrase_parse, :compression it_behaves_like 'an adapter' + + it 'allows to provide adapter specific configs' do + adapter = Faraday::Adapter::HTTPClient.new do |client| + client.keep_alive_timeout = 20 + client.ssl_config.timeout = 25 + end + + client = adapter.client + adapter.configure_client + + expect(client.keep_alive_timeout).to eq(20) + expect(client.ssl_config.timeout).to eq(25) + end + + it 'binds local socket' do + stub_request(:get, 'http://example.com') + + host = '1.2.3.4' + port = 1234 + conn = Faraday.new('http://example.com', request: { bind: { host: host, port: port } }) do |f| + f.adapter :httpclient + end + + conn.get('/') + + expect(conn.options[:bind][:host]).to eq(host) + expect(conn.options[:bind][:port]).to eq(port) + end end \ No newline at end of file diff --git a/spec/faraday/adapter/net_http_persistent_spec.rb b/spec/faraday/adapter/net_http_persistent_spec.rb index 5e90fbbf3..826142586 100644 --- a/spec/faraday/adapter/net_http_persistent_spec.rb +++ b/spec/faraday/adapter/net_http_persistent_spec.rb @@ -2,4 +2,29 @@ features :body_on_get, :reason_phrase_parse, :compression it_behaves_like 'an adapter' -end \ No newline at end of file + + it 'allows to provide adapter specific configs' do + url = URI('https://example.com:1234') + + adapter = Faraday::Adapter::NetHttpPersistent.new do |http| + http.idle_timeout = 123 + end + + http = adapter.send(:net_http_connection, url: url, request: {}) + adapter.send(:configure_request, http, {}) + + expect(http.idle_timeout).to eq(123) + end + + it 'sets max_retries to 0' do + url = URI('http://example.com') + + adapter = Faraday::Adapter::NetHttpPersistent.new + + http = adapter.send(:net_http_connection, url: url, request: {}) + adapter.send(:configure_request, http, {}) + + # `max_retries=` is only present in Ruby 2.5 + expect(http.max_retries).to eq(0) if http.respond_to?(:max_retries=) + end +end diff --git a/spec/faraday/adapter/net_http_spec.rb b/spec/faraday/adapter/net_http_spec.rb index 2f094cdaa..df6d129e9 100644 --- a/spec/faraday/adapter/net_http_spec.rb +++ b/spec/faraday/adapter/net_http_spec.rb @@ -2,4 +2,47 @@ features :body_on_get, :reason_phrase_parse, :compression, :streaming it_behaves_like 'an adapter' + + context 'checking http' do + let(:url) { URI('http://example.com') } + let(:adapter) { Faraday::Adapter::NetHttp.new } + let(:http) { adapter.send(:net_http_connection, url: url, request: {}) } + + it { expect(http.port).to eq(80) } + it 'sets max_retries to 0' do + adapter.send(:configure_request, http, {}) + + expect(http.max_retries).to eq(0) if http.respond_to?(:max_retries=) + end + it 'supports write_timeout' do + adapter.send(:configure_request, http, { write_timeout: 10 }) + + expect(http.write_timeout).to eq(10) if http.respond_to?(:write_timeout=) + end + + context 'with https url' do + let(:url) { URI('https://example.com') } + + it { expect(http.port).to eq(443) } + end + + context 'with http url including port' do + let(:url) { URI('https://example.com:1234') } + + it { expect(http.port).to eq(1234) } + end + + context 'with custom adapter config' do + let(:adapter) do + Faraday::Adapter::NetHttp.new do |http| + http.continue_timeout = 123 + end + end + + it do + adapter.send(:configure_request, http, {}) + expect(http.continue_timeout).to eq(123) + end + end + end end \ No newline at end of file diff --git a/spec/faraday/adapter/patron_spec.rb b/spec/faraday/adapter/patron_spec.rb index b344326d2..e5bc00651 100644 --- a/spec/faraday/adapter/patron_spec.rb +++ b/spec/faraday/adapter/patron_spec.rb @@ -2,4 +2,15 @@ features :reason_phrase_parse it_behaves_like 'an adapter' + + it 'allows to provide adapter specific configs' do + conn = Faraday.new do |f| + f.adapter :patron do |session| + session.max_redirects = 10 + raise RuntimeError, 'Configuration block called' + end + end + + expect { conn.get('/') }.to raise_error(RuntimeError, 'Configuration block called') + end end \ No newline at end of file diff --git a/test/adapters/default_test.rb b/test/adapters/default_test.rb deleted file mode 100644 index b0036a329..000000000 --- a/test/adapters/default_test.rb +++ /dev/null @@ -1,14 +0,0 @@ -require File.expand_path('../integration', __FILE__) - -module Adapters - class DefaultTest < Faraday::TestCase - - def adapter() :default end - - Integration.apply(self, :NonParallel, :Streaming) do - # default stack is not configured with Multipart - # undef :test_POST_sends_files - end - - end -end diff --git a/test/adapters/excon_test.rb b/test/adapters/excon_test.rb deleted file mode 100644 index dd1b82fdc..000000000 --- a/test/adapters/excon_test.rb +++ /dev/null @@ -1,30 +0,0 @@ -require File.expand_path('../integration', __FILE__) - -module Adapters - class ExconTest < Faraday::TestCase - - def adapter() :excon end - - Integration.apply(self, :NonParallel, :NonStreaming) do - # https://github.com/geemus/excon/issues/126 ? - undef :test_timeout if ssl_mode? - - # Excon lets OpenSSL::SSL::SSLError be raised without any way to - # distinguish whether it happened because of a 407 proxy response - undef :test_proxy_auth_fail if ssl_mode? - - # https://github.com/geemus/excon/issues/358 - undef :test_connection_error if RUBY_VERSION >= '2.1.0' - end - - def test_custom_adapter_config - url = URI('https://example.com:1234') - - adapter = Faraday::Adapter::Excon.new nil, debug_request: true - - conn = adapter.create_connection({url: url}, {}) - - assert_equal true, conn.data[:debug_request] - end - end -end diff --git a/test/adapters/httpclient_test.rb b/test/adapters/httpclient_test.rb deleted file mode 100644 index e8b87f281..000000000 --- a/test/adapters/httpclient_test.rb +++ /dev/null @@ -1,34 +0,0 @@ -require File.expand_path('../integration', __FILE__) - -module Adapters - class HttpclientTest < Faraday::TestCase - - def adapter() :httpclient end - - Integration.apply(self, :NonParallel, :Compression) do - def setup - require 'httpclient' unless defined?(HTTPClient) - HTTPClient::NO_PROXY_HOSTS.delete('localhost') - end - - def test_binds_local_socket - host = '1.2.3.4' - conn = create_connection :request => { :bind => { :host => host } } - assert_equal host, conn.options[:bind][:host] - end - - def test_custom_adapter_config - adapter = Faraday::Adapter::HTTPClient.new do |client| - client.keep_alive_timeout = 20 - client.ssl_config.timeout = 25 - end - - client = adapter.client - adapter.configure_client - - assert_equal 20, client.keep_alive_timeout - assert_equal 25, client.ssl_config.timeout - end - end - end -end diff --git a/test/adapters/integration.rb b/test/adapters/integration.rb index f52baeb2c..d36227c1e 100644 --- a/test/adapters/integration.rb +++ b/test/adapters/integration.rb @@ -106,44 +106,6 @@ module Common extend Forwardable def_delegators :create_connection, :get, :head, :put, :post, :patch, :delete, :run_request - # This needs reimplementation: see https://github.com/lostisland/faraday/issues/718 - def test_timeout - conn = create_connection(:request => { :timeout => 1, :open_timeout => 1 }) - assert_raises Faraday::Error::TimeoutError do - conn.get '/slow' - end - end - - # def test_proxy - # proxy_uri = URI(ENV['LIVE_PROXY']) - # conn = create_connection(:proxy => proxy_uri) - # - # res = conn.get '/echo' - # assert_equal 'get', res.body - # - # unless self.class.ssl_mode? - # # proxy can't append "Via" header for HTTPS responses - # assert_match(/:#{proxy_uri.port}$/, res['via']) - # end - # end - - # def test_proxy_auth_fail - # proxy_uri = URI(ENV['LIVE_PROXY']) - # proxy_uri.password = 'WRONG' - # conn = create_connection(:proxy => proxy_uri) - # - # err = assert_raises Faraday::Error::ConnectionFailed do - # conn.get '/echo' - # end - # - # unless self.class.ssl_mode? && (self.class.jruby? || - # adapter == :em_http || adapter == :em_synchrony) - # # JRuby raises "End of file reached" which cannot be distinguished from a 407 - # # EM raises "connection closed by server" due to https://github.com/igrigorik/em-socksify/pull/19 - # assert_equal %{407 "Proxy Authentication Required "}, err.message - # end - # end - def adapter raise NotImplementedError.new("Need to override #adapter") end diff --git a/test/adapters/logger_test.rb b/test/adapters/logger_test.rb deleted file mode 100644 index 98d105033..000000000 --- a/test/adapters/logger_test.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Adapters - class LoggerTest < Faraday::TestCase - def test_logs_method_and_url - @conn.get '/hello', nil, :accept => 'text/html' - assert_match 'request: GET http:/hello', @io.string - end - - def test_logs_status_code - @conn.get '/hello', nil, :accept => 'text/html' - assert_match 'response: Status 200', @io.string - end - end -end diff --git a/test/adapters/net_http_persistent_test.rb b/test/adapters/net_http_persistent_test.rb deleted file mode 100644 index 254cef376..000000000 --- a/test/adapters/net_http_persistent_test.rb +++ /dev/null @@ -1,92 +0,0 @@ -require File.expand_path('../integration', __FILE__) - -module Adapters - class NetHttpPersistentTest < Faraday::TestCase - - def adapter() :net_http_persistent end - - Integration.apply(self, :NonParallel) do - def setup - if defined?(Net::HTTP::Persistent) - # work around problems with mixed SSL certificates - # https://github.com/drbrain/net-http-persistent/issues/45 - if Net::HTTP::Persistent.instance_method(:initialize).parameters.first == [:key, :name] - Net::HTTP::Persistent.new(name: 'Faraday').reconnect_ssl - else - Net::HTTP::Persistent.new('Faraday').ssl_cleanup(4) - end - end - end if ssl_mode? - - def test_reuses_tcp_sockets - # Ensure that requests are not reused from previous tests - Thread.current.keys - .select { |key| key.to_s =~ /\Anet_http_persistent_Faraday_/ } - .each { |key| Thread.current[key] = nil } - - sockets = [] - tcp_socket_open_wrapped = Proc.new do |*args, &block| - socket = TCPSocket.__minitest_stub__open(*args, &block) - sockets << socket - socket - end - - TCPSocket.stub :open, tcp_socket_open_wrapped do - conn = create_connection - conn.post("/echo", :foo => "bar") - conn.post("/echo", :foo => "baz") - end - - assert_equal 1, sockets.count - end - - def test_does_not_reuse_tcp_sockets_when_proxy_changes - # Ensure that requests are not reused from previous tests - Thread.current.keys - .select { |key| key.to_s =~ /\Anet_http_persistent_Faraday_/ } - .each { |key| Thread.current[key] = nil } - - sockets = [] - tcp_socket_open_wrapped = Proc.new do |*args, &block| - socket = TCPSocket.__minitest_stub__open(*args, &block) - sockets << socket - socket - end - - TCPSocket.stub :open, tcp_socket_open_wrapped do - conn = create_connection - conn.post("/echo", :foo => "bar") - conn.proxy = URI(ENV["LIVE_PROXY"]) - conn.post("/echo", :foo => "bar") - end - - assert_equal 2, sockets.count - end - end - - def test_custom_adapter_config - url = URI('https://example.com:1234') - - adapter = Faraday::Adapter::NetHttpPersistent.new do |http| - http.idle_timeout = 123 - end - - http = adapter.send(:net_http_connection, :url => url, :request => {}) - adapter.send(:configure_request, http, {}) - - assert_equal 123, http.idle_timeout - end - - def test_max_retries - url = URI('http://example.com') - - adapter = Faraday::Adapter::NetHttpPersistent.new - - http = adapter.send(:net_http_connection, :url => url, :request => {}) - adapter.send(:configure_request, http, {}) - - # `max_retries=` is only present in Ruby 2.5 - assert_equal 0, http.max_retries if http.respond_to?(:max_retries=) - end - end -end diff --git a/test/adapters/net_http_test.rb b/test/adapters/net_http_test.rb deleted file mode 100644 index ead1a5a10..000000000 --- a/test/adapters/net_http_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -require File.expand_path('../integration', __FILE__) -require 'ostruct' -require 'uri' - -module Adapters - class NetHttpTest < Faraday::TestCase - - def adapter() :net_http end - - behaviors = [:NonParallel, :Compression, :Streaming] - - Integration.apply(self, *behaviors) - - def test_no_explicit_http_port_number - url = URI('http://example.com') - url.port = nil - - adapter = Faraday::Adapter::NetHttp.new - http = adapter.send(:net_http_connection, :url => url, :request => {}) - - assert_equal 80, http.port - end - - def test_no_explicit_https_port_number - url = URI('https://example.com') - url.port = nil - - adapter = Faraday::Adapter::NetHttp.new - http = adapter.send(:net_http_connection, :url => url, :request => {}) - - assert_equal 443, http.port - end - - def test_explicit_port_number - url = URI('https://example.com:1234') - - adapter = Faraday::Adapter::NetHttp.new - http = adapter.send(:net_http_connection, :url => url, :request => {}) - - assert_equal 1234, http.port - end - - def test_custom_adapter_config - url = URI('https://example.com:1234') - - adapter = Faraday::Adapter::NetHttp.new do |http| - http.continue_timeout = 123 - end - - http = adapter.send(:net_http_connection, :url => url, :request => {}) - adapter.send(:configure_request, http, {}) - - assert_equal 123, http.continue_timeout - end - - def test_no_retries - url = URI('http://example.com') - - adapter = Faraday::Adapter::NetHttp.new - - http = adapter.send(:net_http_connection, :url => url, :request => {}) - adapter.send(:configure_request, http, {}) - - # `max_retries=` is only present in Ruby 2.5 - assert_equal 0, http.max_retries if http.respond_to?(:max_retries=) - end - - def test_write_timeout - url = URI('http://example.com') - - adapter = Faraday::Adapter::NetHttp.new - - http = adapter.send(:net_http_connection, :url => url, :request => {}) - adapter.send(:configure_request, http, { write_timeout: 10 }) - - assert_equal 10, http.write_timeout if http.respond_to?(:write_timeout=) - end - end -end diff --git a/test/adapters/patron_test.rb b/test/adapters/patron_test.rb deleted file mode 100644 index 537ace1bc..000000000 --- a/test/adapters/patron_test.rb +++ /dev/null @@ -1,40 +0,0 @@ -require File.expand_path('../integration', __FILE__) - -module Adapters - class Patron < Faraday::TestCase - - def adapter() :patron end - - unless jruby? - Integration.apply(self, :NonParallel, :NonStreaming) do - # https://github.com/toland/patron/issues/34 - # undef :test_PATCH_send_url_encoded_params - - # https://github.com/toland/patron/issues/52 - # undef :test_GET_with_body - - # no support for SSL peer verification - undef :test_GET_ssl_fails_with_bad_cert if ssl_mode? - end - - def test_custom_adapter_config - conn = create_connection do |session| - assert_kind_of ::Patron::Session, session - session.max_redirects = 10 - throw :config_block_called - end - - assert_throws(:config_block_called) do - conn.get 'http://8.8.8.8:88' - end - end - - def test_connection_timeout - conn = create_connection(:request => {:timeout => 10, :open_timeout => 1}) - assert_raises Faraday::Error::ConnectionFailed do - conn.get 'http://8.8.8.8:88' - end - end - end - end -end diff --git a/test/adapters/typhoeus_test.rb b/test/adapters/typhoeus_test.rb deleted file mode 100644 index d85f4afe1..000000000 --- a/test/adapters/typhoeus_test.rb +++ /dev/null @@ -1,38 +0,0 @@ -require File.expand_path('../integration', __FILE__) - -module Adapters - class TyphoeusTest < Faraday::TestCase - - def adapter() :typhoeus end - - Integration.apply(self, :Parallel) do - # inconsistent outcomes ranging from successful response to connection error - undef :test_proxy_auth_fail if ssl_mode? - - # Typhoeus adapter not supporting Faraday::SSLError - undef :test_GET_ssl_fails_with_bad_cert if ssl_mode? - - def test_binds_local_socket - host = '1.2.3.4' - conn = create_connection :request => { :bind => { :host => host } } - assert_equal host, conn.options[:bind][:host] - end - - # Typhoeus::Response doesn't provide an easy way to access the reason phrase, - # so override the shared test from Common. - def test_GET_reason_phrase - response = get('echo') - assert_nil response.reason_phrase - end - end - - def test_custom_adapter_config - adapter = Faraday::Adapter::Typhoeus.new(nil, { :forbid_reuse => true, :maxredirs => 1 }) - - request = adapter.method(:typhoeus_request).call({}) - - assert_equal true, request.options[:forbid_reuse] - assert_equal 1, request.options[:maxredirs] - end - end -end From 6f46db6105d30842dccc3f3eb334a0c29cffc83c Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 15 Oct 2018 16:54:13 +0100 Subject: [PATCH 31/46] Converts instrumentation tests to RSpec. --- spec/faraday/request/instrumentation_spec.rb | 74 ++++++++++++++++ test/middleware/instrumentation_test.rb | 88 -------------------- 2 files changed, 74 insertions(+), 88 deletions(-) create mode 100644 spec/faraday/request/instrumentation_spec.rb delete mode 100644 test/middleware/instrumentation_test.rb diff --git a/spec/faraday/request/instrumentation_spec.rb b/spec/faraday/request/instrumentation_spec.rb new file mode 100644 index 000000000..a90f6f0f5 --- /dev/null +++ b/spec/faraday/request/instrumentation_spec.rb @@ -0,0 +1,74 @@ +RSpec.describe Faraday::Request::Instrumentation do + class FakeInstrumenter + attr_reader :instrumentations + + def initialize + @instrumentations = [] + end + + def instrument(name, env) + @instrumentations << [name, env] + yield + end + end + + let(:config) { {} } + let(:options) { Faraday::Request::Instrumentation::Options.from config } + let(:instrumenter) { FakeInstrumenter.new } + let(:conn) do + Faraday.new do |f| + f.request :instrumentation, config.merge(instrumenter: instrumenter) + f.adapter :test do |stub| + stub.get '/' do + [200, {}, 'ok'] + end + end + end + end + + it { expect(options.name).to eq('request.faraday') } + it 'defaults to ActiveSupport::Notifications' do + begin + res = options.instrumenter + rescue NameError => err + expect(err.to_s).to match('ActiveSupport') + else + expect(res).to eq(ActiveSupport::Notifications) + end + end + + it 'instruments with default name' do + expect(instrumenter.instrumentations.size).to eq(0) + + res = conn.get '/' + expect(res.body).to eq('ok') + expect(instrumenter.instrumentations.size).to eq(1) + + name, env = instrumenter.instrumentations.first + expect(name).to eq('request.faraday') + expect(env[:url].path).to eq('/') + end + + context 'with custom name' do + let(:config) { { name: 'custom' } } + + it { expect(options.name).to eq('custom') } + it 'instruments with custom name' do + expect(instrumenter.instrumentations.size).to eq(0) + + res = conn.get '/' + expect(res.body).to eq('ok') + expect(instrumenter.instrumentations.size).to eq(1) + + name, env = instrumenter.instrumentations.first + expect(name).to eq('custom') + expect(env[:url].path).to eq('/') + end + end + + context 'with custom instrumenter' do + let(:config) { { instrumenter: :custom } } + + it { expect(options.instrumenter).to eq(:custom) } + end +end \ No newline at end of file diff --git a/test/middleware/instrumentation_test.rb b/test/middleware/instrumentation_test.rb deleted file mode 100644 index 364f13cb5..000000000 --- a/test/middleware/instrumentation_test.rb +++ /dev/null @@ -1,88 +0,0 @@ -require File.expand_path("../../helper", __FILE__) - -module Middleware - class InstrumentationTest < Faraday::TestCase - def setup - @instrumenter = FakeInstrumenter.new - end - - def test_default_name - assert_equal 'request.faraday', options.name - end - - def test_default_instrumenter - begin - instrumenter = options.instrumenter - rescue NameError => err - assert_match 'ActiveSupport', err.to_s - else - assert_equal ActiveSupport::Notifications, instrumenter - end - end - - def test_name - assert_equal 'booya', options(:name => 'booya').name - end - - def test_instrumenter - assert_equal :boom, options(:instrumenter => :boom).instrumenter - end - - def test_instrumentation_with_default_name - assert_equal 0, @instrumenter.instrumentations.size - - faraday = conn - res = faraday.get '/' - assert_equal 'ok', res.body - - assert_equal 1, @instrumenter.instrumentations.size - name, env = @instrumenter.instrumentations.first - assert_equal 'request.faraday', name - assert_equal '/', env[:url].path - end - - def test_instrumentation - assert_equal 0, @instrumenter.instrumentations.size - - faraday = conn :name => 'booya' - res = faraday.get '/' - assert_equal 'ok', res.body - - assert_equal 1, @instrumenter.instrumentations.size - name, env = @instrumenter.instrumentations.first - assert_equal 'booya', name - assert_equal '/', env[:url].path - end - - class FakeInstrumenter - attr_reader :instrumentations - - def initialize - @instrumentations = [] - end - - def instrument(name, env) - @instrumentations << [name, env] - yield - end - end - - def options(hash = nil) - Faraday::Request::Instrumentation::Options.from hash - end - - def conn(hash = nil) - hash ||= {} - hash[:instrumenter] = @instrumenter - - Faraday.new do |f| - f.request :instrumentation, hash - f.adapter :test do |stub| - stub.get '/' do - [200, {}, 'ok'] - end - end - end - end - end -end From 30fb4b76b8510bb86dab9e333620316122c159bc Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 29 Oct 2018 16:03:46 +0000 Subject: [PATCH 32/46] Converts retry middleware tests to RSpec. --- spec/faraday/request/retry_spec.rb | 226 +++++++++++++++++++++++ test/middleware/retry_test.rb | 282 ----------------------------- 2 files changed, 226 insertions(+), 282 deletions(-) create mode 100644 spec/faraday/request/retry_spec.rb delete mode 100644 test/middleware/retry_test.rb diff --git a/spec/faraday/request/retry_spec.rb b/spec/faraday/request/retry_spec.rb new file mode 100644 index 000000000..8d9841a7b --- /dev/null +++ b/spec/faraday/request/retry_spec.rb @@ -0,0 +1,226 @@ +RSpec.describe Faraday::Request::Retry do + let(:calls) { [] } + let(:times_called) { calls.size } + let(:options) { [] } + let(:conn) do + Faraday.new do |b| + b.request :retry, *options + + b.adapter :test do |stub| + %w(get post).each do |method| + stub.send(method, '/unstable') do |env| + calls << env.dup + env[:body] = nil # simulate blanking out response body + callback.call + end + end + end + end + end + + context 'when an unexpected error happens' do + let(:callback) { lambda { raise 'boom!' } } + + before { expect { conn.get('/unstable') }.to raise_error(RuntimeError) } + + it { expect(times_called).to eq(1) } + + context 'and this is passed as a custom exception' do + let(:options) { [{ exceptions: StandardError }] } + + it { expect(times_called).to eq(3) } + end + end + + context 'when an expected error happens' do + let(:callback) { lambda { raise Errno::ETIMEDOUT } } + + before do + @started = Time.now + expect { conn.get('/unstable') }.to raise_error(Errno::ETIMEDOUT) + end + + it { expect(times_called).to eq(3) } + + context 'and legacy max_retry set to 1' do + let(:options) { [1] } + + it { expect(times_called).to eq(2) } + end + + context 'and legacy max_retry set to -9' do + let(:options) { [-9] } + + it { expect(times_called).to eq(1) } + end + + context 'and new max_retry set to 3' do + let(:options) { [{ max: 3 }] } + + it { expect(times_called).to eq(4) } + end + + context 'and new max_retry set to -9' do + let(:options) { [{ max: -9 }] } + + it { expect(times_called).to eq(1) } + end + + context 'and both max_retry and interval are set' do + let(:options) { [{ max: 2, interval: 0.1 }] } + + it { expect(Time.now - @started).to be_within(0.04).of(0.2) } + end + end + + context 'when no exception raised' do + let(:options) { [{ max: 1, retry_statuses: 429 }] } + + before { conn.get('/unstable') } + + context 'and response code is in retry_statuses' do + let(:callback) { lambda { [429, {}, ''] } } + + it { expect(times_called).to eq(2) } + end + + context 'and response code is not in retry_statuses' do + let(:callback) { lambda { [503, {}, ''] } } + + it { expect(times_called).to eq(1) } + end + end + + describe '#calculate_retry_interval' do + context 'with exponential backoff' do + let(:options) { { max: 5, interval: 0.1, backoff_factor: 2 } } + let(:middleware) { Faraday::Request::Retry.new(nil, options) } + + it { expect(middleware.send(:calculate_retry_interval, 5)).to eq(0.1) } + it { expect(middleware.send(:calculate_retry_interval, 4)).to eq(0.2) } + it { expect(middleware.send(:calculate_retry_interval, 3)).to eq(0.4) } + end + + context 'with exponential backoff and max_interval' do + let(:options) { { max: 5, interval: 0.1, backoff_factor: 2, max_interval: 0.3 } } + let(:middleware) { Faraday::Request::Retry.new(nil, options) } + + it { expect(middleware.send(:calculate_retry_interval, 5)).to eq(0.1) } + it { expect(middleware.send(:calculate_retry_interval, 4)).to eq(0.2) } + it { expect(middleware.send(:calculate_retry_interval, 3)).to eq(0.3) } + it { expect(middleware.send(:calculate_retry_interval, 2)).to eq(0.3) } + end + + context 'with exponential backoff and interval_randomness' do + let(:options) { { max: 2, interval: 0.1, interval_randomness: 0.05 } } + let(:middleware) { Faraday::Request::Retry.new(nil, options) } + + it { expect(middleware.send(:calculate_retry_interval, 2)).to be_between(0.1, 0.15) } + end + end + + context 'when method is not idempotent' do + let(:callback) { lambda { raise Errno::ETIMEDOUT } } + + before { expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT) } + + it { expect(times_called).to eq(1) } + end + + describe 'retry_if option' do + let(:callback) { lambda { raise Errno::ETIMEDOUT } } + let(:options) { [{ retry_if: @check }] } + + it 'retries if retry_if block always returns true' do + body = { foo: :bar } + @check = lambda { |_, _| true } + expect { conn.post('/unstable', body) }.to raise_error(Errno::ETIMEDOUT) + expect(times_called).to eq(3) + expect(calls.all? { |env| env[:body] == body }).to be_truthy + end + + it 'does not retry if retry_if block returns false checking env' do + @check = lambda { |env, _| env[:method] != :post } + expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT) + expect(times_called).to eq(1) + end + + it 'does not retry if retry_if block returns false checking exception' do + @check = lambda { |_, exception| !exception.kind_of?(Errno::ETIMEDOUT) } + expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT) + expect(times_called).to eq(1) + end + + it 'should rewind files on retry' do + io = StringIO.new('Test data') + upload_io = Faraday::UploadIO.new(io, 'application/octet/stream') + + rewound = 0 + rewind = lambda { rewound += 1 } + + @check = lambda { |_, _| true } + allow(upload_io).to receive(:rewind, &rewind) + expect { conn.post('/unstable', { file: upload_io }) }.to raise_error(Errno::ETIMEDOUT) + expect(times_called).to eq(3) + expect(rewound).to eq(2) + end + + context 'when explicitly specifying methods to retry' do + let(:options) { [{ retry_if: @check, methods: [:post] }] } + + it 'does not call retry_if for specified methods' do + @check = lambda { |_, _| raise "this should have never been called" } + expect { conn.post('/unstable') }.to raise_error(Errno::ETIMEDOUT) + expect(times_called).to eq(3) + end + end + + context 'with empty list of methods to retry' do + let(:options) { [{ retry_if: @check, methods: [] }] } + + it 'calls retry_if for all methods' do + @check = lambda { |_, _| calls.size < 2 } + expect { conn.get('/unstable') }.to raise_error(Errno::ETIMEDOUT) + expect(times_called).to eq(2) + end + end + end + + describe 'retry_after header support' do + let(:callback) { lambda { [504, headers, ''] } } + let(:elapsed) { Time.now - @started } + + before do + @started = Time.now + conn.get('/unstable') + end + + context 'when retry_after bigger than interval' do + let(:headers) { { 'Retry-After' => '0.5' } } + let(:options) { [{ max: 1, interval: 0.1, retry_statuses: 504 }] } + + it { expect(elapsed).to be > 0.5 } + end + + context 'when retry_after smaller than interval' do + let(:headers) { { 'Retry-After' => '0.1' } } + let(:options) { [{ max: 1, interval: 0.2, retry_statuses: 504 }] } + + it { expect(elapsed).to be > 0.2 } + end + + context 'when retry_after is a timestamp' do + let(:headers) { { 'Retry-After' => (Time.now.utc + 2).strftime('%a, %d %b %Y %H:%M:%S GMT') } } + let(:options) { [{ max: 1, interval: 0.1, retry_statuses: 504 }] } + + it { expect(elapsed).to be > 1 } + end + + context 'when retry_after is bigger than max_interval' do + let(:headers) { { 'Retry-After' => (Time.now.utc + 20).strftime('%a, %d %b %Y %H:%M:%S GMT') } } + let(:options) { [{ max: 2, interval: 0.1, max_interval: 5, retry_statuses: 504 }] } + + it { expect(times_called).to eq(1) } + end + end +end diff --git a/test/middleware/retry_test.rb b/test/middleware/retry_test.rb deleted file mode 100644 index 2d7b86e0e..000000000 --- a/test/middleware/retry_test.rb +++ /dev/null @@ -1,282 +0,0 @@ -require File.expand_path("../../helper", __FILE__) - -module Middleware - class RetryTest < Faraday::TestCase - def setup - @times_called = 0 - @envs = [] - end - - def conn(*retry_args) - Faraday.new do |b| - b.request :retry, *retry_args - - b.adapter :test do |stub| - ['get', 'post'].each do |method| - stub.send(method, '/unstable') do |env| - @times_called += 1 - @envs << env.dup - env[:body] = nil # simulate blanking out response body - @explode.call @times_called - end - - stub.send(method, '/throttled') do |env| - @times_called += 1 - @envs << env.dup - - params = env[:params] - - status = (params['status'] || 429).to_i - headers = {} - - retry_after = params['retry_after'] - - headers['Retry-After'] = retry_after if retry_after - - [status, headers, ''] - end - end - end - end - end - - def test_unhandled_error - @explode = lambda {|n| raise "boom!" } - assert_raises(RuntimeError) { conn.get("/unstable") } - assert_equal 1, @times_called - end - - def test_handled_error - @explode = lambda {|n| raise Errno::ETIMEDOUT } - assert_raises(Errno::ETIMEDOUT) { conn.get("/unstable") } - assert_equal 3, @times_called - end - - def test_legacy_max_retries - @explode = lambda {|n| raise Errno::ETIMEDOUT } - assert_raises(Errno::ETIMEDOUT) { conn(1).get("/unstable") } - assert_equal 2, @times_called - end - - def test_legacy_max_negative_retries - @explode = lambda {|n| raise Errno::ETIMEDOUT } - assert_raises(Errno::ETIMEDOUT) { conn(-9).get("/unstable") } - assert_equal 1, @times_called - end - - def test_new_max_retries - @explode = lambda {|n| raise Errno::ETIMEDOUT } - assert_raises(Errno::ETIMEDOUT) { conn(:max => 3).get("/unstable") } - assert_equal 4, @times_called - end - - def test_new_max_negative_retries - @explode = lambda { |n| raise Errno::ETIMEDOUT } - assert_raises(Errno::ETIMEDOUT) { conn(:max => -9).get("/unstable") } - assert_equal 1, @times_called - end - - def test_interval - @explode = lambda {|n| raise Errno::ETIMEDOUT } - started = Time.now - assert_raises(Errno::ETIMEDOUT) { - conn(:max => 2, :interval => 0.1).get("/unstable") - } - assert_in_delta 0.2, Time.now - started, 0.04 - end - - def test_calls_calculate_sleep_amount - explode_app = MiniTest::Mock.new - explode_app.expect(:call, nil, [{:body=>nil}]) - def explode_app.call(env) - raise Errno::ETIMEDOUT - end - - retry_middleware = Faraday::Request::Retry.new(explode_app) - class << retry_middleware - attr_accessor :sleep_amount_retries - - def calculate_sleep_amount(retries, env) - self.sleep_amount_retries.delete(retries) - 0 - end - end - retry_middleware.sleep_amount_retries = [2, 1] - - assert_raises(Errno::ETIMEDOUT) { - retry_middleware.call({:method => :get}) - } - - assert_empty retry_middleware.sleep_amount_retries - end - - def test_exponential_backoff - middleware = Faraday::Request::Retry.new(nil, :max => 5, :interval => 0.1, :backoff_factor => 2) - assert_equal middleware.send(:calculate_retry_interval, 5), 0.1 - assert_equal middleware.send(:calculate_retry_interval, 4), 0.2 - assert_equal middleware.send(:calculate_retry_interval, 3), 0.4 - end - - def test_exponential_backoff_with_max_interval - middleware = Faraday::Request::Retry.new(nil, :max => 5, :interval => 1, :max_interval => 3, :backoff_factor => 2) - assert_equal middleware.send(:calculate_retry_interval, 5), 1 - assert_equal middleware.send(:calculate_retry_interval, 4), 2 - assert_equal middleware.send(:calculate_retry_interval, 3), 3 - assert_equal middleware.send(:calculate_retry_interval, 2), 3 - end - - def test_random_additional_interval_amount - middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 1.0) - sleep_amount = middleware.send(:calculate_retry_interval, 2) - assert_operator sleep_amount, :>=, 0.1 - assert_operator sleep_amount, :<=, 0.2 - middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 0.5) - sleep_amount = middleware.send(:calculate_retry_interval, 2) - assert_operator sleep_amount, :>=, 0.1 - assert_operator sleep_amount, :<=, 0.15 - middleware = Faraday::Request::Retry.new(nil, :max => 2, :interval => 0.1, :interval_randomness => 0.25) - sleep_amount = middleware.send(:calculate_retry_interval, 2) - assert_operator sleep_amount, :>=, 0.1 - assert_operator sleep_amount, :<=, 0.125 - end - - def test_custom_exceptions - @explode = lambda {|n| raise "boom!" } - assert_raises(RuntimeError) { - conn(:exceptions => StandardError).get("/unstable") - } - assert_equal 3, @times_called - end - - def test_should_retry_with_body_if_block_returns_true_for_non_idempotent_request - body = { :foo => :bar } - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| true } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check).post("/unstable", body) - } - assert_equal 3, @times_called - assert @envs.all? { |env| env[:body] === body } - end - - def test_should_stop_retrying_if_block_returns_false_checking_env - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| env[:method] != :post } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check).post("/unstable") - } - assert_equal 1, @times_called - end - - def test_should_stop_retrying_if_block_returns_false_checking_exception - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| !exception.kind_of?(Errno::ETIMEDOUT) } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check).post("/unstable") - } - assert_equal 1, @times_called - end - - def test_should_not_call_retry_if_for_idempotent_methods_if_methods_unspecified - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| raise "this should have never been called" } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check).get("/unstable") - } - assert_equal 3, @times_called - end - - def test_should_not_retry_for_non_idempotent_method_if_methods_unspecified - @explode = lambda {|n| raise Errno::ETIMEDOUT } - assert_raises(Errno::ETIMEDOUT) { - conn.post("/unstable") - } - assert_equal 1, @times_called - end - - def test_should_not_call_retry_if_for_specified_methods - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| raise "this should have never been called" } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check, :methods => [:post]).post("/unstable") - } - assert_equal 3, @times_called - end - - def test_should_call_retry_if_for_empty_method_list - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| @times_called < 2 } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check, :methods => []).get("/unstable") - } - assert_equal 2, @times_called - end - - def test_should_rewind_files_on_retry - io = StringIO.new("Test data") - upload_io = Faraday::UploadIO.new(io, "application/octet/stream") - - rewound = 0 - rewind = lambda { rewound += 1 } - - upload_io.stub :rewind, rewind do - @explode = lambda {|n| raise Errno::ETIMEDOUT } - check = lambda { |env,exception| true } - assert_raises(Errno::ETIMEDOUT) { - conn(:retry_if => check).post("/unstable", { :file => upload_io }) - } - end - assert_equal 3, @times_called - assert_equal 2, rewound - end - - def test_should_retry_retriable_response - params = { status: 429 } - response = conn(:max => 1, :retry_statuses => 429).get("/throttled", params) - - assert_equal 2, @times_called - assert_equal 429, response.status - end - - def test_should_not_retry_non_retriable_response - params = { status: 503 } - conn(:max => 1, :retry_statuses => 429).get("/throttled", params) - - assert_equal 1, @times_called - end - - def test_interval_if_retry_after_present - started = Time.now - - params = { :retry_after => 0.5 } - conn(:max => 1, :interval => 0.1, :retry_statuses => [429]).get("/throttled", params) - - assert Time.now - started > 0.5 - end - - def test_should_ignore_retry_after_if_less_then_calculated - started = Time.now - - params = { :retry_after => 0.1 } - conn(:max => 1, :interval => 0.2, :retry_statuses => [429]).get("/throttled", params) - - assert Time.now - started > 0.2 - end - - def test_interval_when_retry_after_is_timestamp - started = Time.now - - params = { :retry_after => (Time.now.utc + 2).strftime('%a, %d %b %Y %H:%M:%S GMT') } - conn(:max => 1, :interval => 0.1, :retry_statuses => [429]).get("/throttled", params) - - assert Time.now - started > 1 - end - - def test_should_not_retry_when_retry_after_greater_then_max_interval - params = { :retry_after => (Time.now.utc + 20).strftime('%a, %d %b %Y %H:%M:%S GMT') } - conn(:max => 2, :interval => 0.1, :retry_statuses => [429], max_interval: 5).get("/throttled", params) - - assert_equal 1, @times_called - end - end -end From 62d3f95a3d50d7214d5b5b5a9cccf9e173eabf4c Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 29 Oct 2018 16:14:22 +0000 Subject: [PATCH 33/46] Adds new parameters_tests to nested_spec --- spec/faraday/params_encoders/nested_spec.rb | 32 ++++++++++++++++----- test/parameters_test.rb | 17 ----------- 2 files changed, 25 insertions(+), 24 deletions(-) delete mode 100644 test/parameters_test.rb diff --git a/spec/faraday/params_encoders/nested_spec.rb b/spec/faraday/params_encoders/nested_spec.rb index 72d17da9e..52386ac20 100644 --- a/spec/faraday/params_encoders/nested_spec.rb +++ b/spec/faraday/params_encoders/nested_spec.rb @@ -21,49 +21,67 @@ expect(subject.decode(query)).to eq(expected) end - it 'decode_nested_array_mixed_types' do + it 'decodes nested array mixed types' do query = 'a[][one]=1&a[]=2&a[]=&a[]' expected = Rack::Utils.parse_nested_query(query) expect(subject.decode(query)).to eq(expected) end - it 'decode_nested_ignores_invalid_array' do + it 'decodes nested ignores invalid array' do query = '[][a]=1&b=2' expected = { "a" => "1", "b" => "2" } expect(subject.decode(query)).to eq(expected) end - it 'decode_nested_ignores_repeated_array_notation' do + it 'decodes nested ignores repeated array notation' do query = 'a[][][]=1' expected = { "a" => ["1"] } expect(subject.decode(query)).to eq(expected) end - it 'decode_nested_ignores_malformed_keys' do + it 'decodes nested ignores malformed keys' do query = '=1&[]=2' expected = {} expect(subject.decode(query)).to eq(expected) end - it 'decode_nested_subkeys_dont_have_to_be_in_brackets' do + it 'decodes nested subkeys dont have to be in brackets' do query = 'a[b]c[d]e=1' expected = { "a" => { "b" => { "c" => { "d" => { "e" => "1" } } } } } expect(subject.decode(query)).to eq(expected) end - it 'decode_nested_final_value_overrides_any_type' do + it 'decodes nested final value overrides any type' do query = 'a[b][c]=1&a[b]=2' expected = { "a" => { "b" => "2" } } expect(subject.decode(query)).to eq(expected) end - it 'encode_rack_compat' do + it 'encodes rack compat' do params = { :a => [{ :one => "1", :two => "2" }, "3", ""] } result = Faraday::Utils.unescape(Faraday::NestedParamsEncoder.encode(params)).split('&') expected = Rack::Utils.build_nested_query(params).split('&') expect(result).to match_array(expected) end + it 'encodes empty string array value' do + expected = 'baz=&foo%5Bbar%5D=' + result = Faraday::NestedParamsEncoder.encode(foo: {bar: ''}, baz: '') + expect(result).to eq(expected) + end + + it 'encodes nil array value' do + expected = 'baz&foo%5Bbar%5D' + result = Faraday::NestedParamsEncoder.encode(foo: {bar: nil}, baz: nil) + expect(result).to eq(expected) + end + + it 'encodes empty array value' do + expected = 'baz%5B%5D&foo%5Bbar%5D%5B%5D' + result = Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) + expect(result).to eq(expected) + end + shared_examples 'a wrong decoding' do it do expect { subject.decode(query) }.to raise_error(TypeError) do |e| diff --git a/test/parameters_test.rb b/test/parameters_test.rb deleted file mode 100644 index ad635615f..000000000 --- a/test/parameters_test.rb +++ /dev/null @@ -1,17 +0,0 @@ -class TestParameters < Faraday::TestCase - def test_encode_empty_string_array_value - expected = 'baz=&foo%5Bbar%5D=' - assert_equal expected, Faraday::NestedParamsEncoder.encode(foo: {bar: ''}, baz: '') - end - - def test_encode_nil_array_value - expected = 'baz&foo%5Bbar%5D' - assert_equal expected, Faraday::NestedParamsEncoder.encode(foo: {bar: nil}, baz: nil) - end - - def test_encode_empty_array_value - expected = 'baz%5B%5D&foo%5Bbar%5D%5B%5D' - Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) - assert_equal expected, Faraday::NestedParamsEncoder.encode(foo: { bar: [] }, baz: []) - end -end From 340122a207c6af26c4ca76dcc126e701a7cd1fac Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 29 Oct 2018 16:19:52 +0000 Subject: [PATCH 34/46] Merge branch '1.0' of https://github.com/lostisland/faraday into feature/#762-rspec # Conflicts: # lib/faraday/utils.rb --- lib/faraday/utils/headers.rb | 3 +++ lib/faraday/utils/params_hash.rb | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/faraday/utils/headers.rb b/lib/faraday/utils/headers.rb index 6881b3e57..4c22dd7ef 100644 --- a/lib/faraday/utils/headers.rb +++ b/lib/faraday/utils/headers.rb @@ -1,5 +1,8 @@ module Faraday module Utils + # A case-insensitive Hash that preserves the original case of a header + # when set. + # # Adapted from Rack::Utils::HeaderHash class Headers < ::Hash def self.from(value) diff --git a/lib/faraday/utils/params_hash.rb b/lib/faraday/utils/params_hash.rb index cf99ddb1a..8e6150800 100644 --- a/lib/faraday/utils/params_hash.rb +++ b/lib/faraday/utils/params_hash.rb @@ -1,6 +1,6 @@ module Faraday module Utils - # Hash with stringified keys + # A hash with stringified keys. class ParamsHash < Hash def [](key) super(convert_key(key)) From f0ff87ba78426b14d9edb0d4f9a6f1c7a3fc6592 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 29 Oct 2018 16:47:52 +0000 Subject: [PATCH 35/46] Converts composite_read_io tests into RSpec --- spec/faraday/composite_read_io_spec.rb | 78 ++++++++++++++++++ test/composite_read_io_test.rb | 109 ------------------------- test/multibyte.txt | 1 - 3 files changed, 78 insertions(+), 110 deletions(-) create mode 100644 spec/faraday/composite_read_io_spec.rb delete mode 100644 test/composite_read_io_test.rb delete mode 100644 test/multibyte.txt diff --git a/spec/faraday/composite_read_io_spec.rb b/spec/faraday/composite_read_io_spec.rb new file mode 100644 index 000000000..790febc79 --- /dev/null +++ b/spec/faraday/composite_read_io_spec.rb @@ -0,0 +1,78 @@ +require 'stringio' + +RSpec.describe Faraday::CompositeReadIO do + Part = Struct.new(:to_io) do + def length + to_io.string.length + end + end + + def part(str) + Part.new StringIO.new(str) + end + + def composite_io(*parts) + Faraday::CompositeReadIO.new(*parts) + end + + context 'with empty composite_io' do + subject { composite_io } + + it { expect(subject.length).to eq(0) } + it { expect(subject.read).to eq('') } + it { expect(subject.read(1)).to be_nil } + end + + context 'with empty parts' do + subject { composite_io(part(''), part('')) } + + it { expect(subject.length).to eq(0) } + it { expect(subject.read).to eq('') } + it { expect(subject.read(1)).to be_nil } + end + + context 'with 2 parts' do + subject { composite_io(part('abcd'), part('1234')) } + + it { expect(subject.length).to eq(8) } + it { expect(subject.read).to eq('abcd1234') } + it 'allows to read in chunks' do + expect(subject.read(3)).to eq('abc') + expect(subject.read(3)).to eq('d12') + expect(subject.read(3)).to eq('34') + expect(subject.read(3)).to be_nil + end + it 'allows to rewind while reading in chunks' do + expect(subject.read(3)).to eq('abc') + expect(subject.read(3)).to eq('d12') + subject.rewind + expect(subject.read(3)).to eq('abc') + expect(subject.read(5)).to eq('d1234') + expect(subject.read(3)).to be_nil + subject.rewind + expect(subject.read(2)).to eq('ab') + end + end + + context 'with mix of empty and non-empty parts' do + subject { composite_io(part(''), part('abcd'), part(''), part('1234'), part('')) } + + it 'allows to read in chunks' do + expect(subject.read(6)).to eq('abcd12') + expect(subject.read(6)).to eq('34') + expect(subject.read(6)).to be_nil + end + end + + context 'with utf8 multibyte part' do + subject { composite_io(part("\x86"), part("ファイル")) } + + it { expect(subject.read).to eq("\x86\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB".force_encoding("BINARY"))} + it 'allows to read in chunks' do + expect(subject.read(3)).to eq("\x86\xE3\x83".force_encoding("BINARY")) + expect(subject.read(3)).to eq("\x95\xE3\x82".force_encoding("BINARY")) + expect(subject.read(8)).to eq("\xA1\xE3\x82\xA4\xE3\x83\xAB".force_encoding("BINARY")) + expect(subject.read(3)).to be_nil + end + end +end \ No newline at end of file diff --git a/test/composite_read_io_test.rb b/test/composite_read_io_test.rb deleted file mode 100644 index 9338e2a1e..000000000 --- a/test/composite_read_io_test.rb +++ /dev/null @@ -1,109 +0,0 @@ -require File.expand_path(File.join(File.dirname(__FILE__), 'helper')) -require 'stringio' - -class CompositeReadIOTest < Faraday::TestCase - Part = Struct.new(:to_io) do - def length() to_io.string.length end - end - - def part(str) - Part.new StringIO.new(str) - end - - def composite_io(*parts) - Faraday::CompositeReadIO.new(*parts) - end - - def test_empty - io = composite_io - assert_equal 0, io.length - assert_equal "", io.read - end - - def test_empty_returns_nil_for_limited_read - assert_nil composite_io.read(1) - end - - def test_empty_parts_returns_nil_for_limited_read - io = composite_io(part(""), part("")) - assert_nil io.read(1) - end - - def test_multipart_read_all - io = composite_io(part("abcd"), part("1234")) - assert_equal 8, io.length - assert_equal "abcd1234", io.read - end - - def test_multipart_read_limited - io = composite_io(part("abcd"), part("1234")) - assert_equal "abc", io.read(3) - assert_equal "d12", io.read(3) - assert_equal "34", io.read(3) - assert_nil io.read(3) - assert_nil io.read(3) - end - - def test_multipart_read_limited_size_larger_than_part - io = composite_io(part("abcd"), part("1234")) - assert_equal "abcd12", io.read(6) - assert_equal "34", io.read(6) - assert_nil io.read(6) - end - - def test_multipart_read_with_blank_parts - io = composite_io(part(""), part("abcd"), part(""), part("1234"), part("")) - assert_equal "abcd12", io.read(6) - assert_equal "34", io.read(6) - assert_nil io.read(6) - end - - def test_multipart_rewind - io = composite_io(part("abcd"), part("1234")) - assert_equal "abc", io.read(3) - assert_equal "d12", io.read(3) - io.rewind - assert_equal "abc", io.read(3) - assert_equal "d1234", io.read(5) - assert_nil io.read(3) - io.rewind - assert_equal "ab", io.read(2) - end - - # JRuby enforces types to copy_stream to be String or IO - if IO.respond_to?(:copy_stream) && !jruby? - def test_compatible_with_copy_stream - target_io = StringIO.new - def target_io.ensure_open_and_writable - # Rubinius compatibility - end - io = composite_io(part("abcd"), part("1234")) - - Faraday::Timer.timeout(1) do - IO.copy_stream(io, target_io) - end - assert_equal "abcd1234", target_io.string - end - end - - def test_read_from_multibyte - File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8| - io = composite_io(part("\x86"), Part.new(utf8)) - assert_equal bin("\x86\xE3\x83\x95\xE3\x82\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read - end - end - - def test_limited_from_multibyte - File.open(File.dirname(__FILE__) + '/multibyte.txt') do |utf8| - io = composite_io(part("\x86"), Part.new(utf8)) - assert_equal bin("\x86\xE3\x83"), io.read(3) - assert_equal bin("\x95\xE3\x82"), io.read(3) - assert_equal bin("\xA1\xE3\x82\xA4\xE3\x83\xAB\n"), io.read(8) - end - end - - def bin(str) - str.force_encoding("BINARY") if str.respond_to?(:force_encoding) - str - end -end diff --git a/test/multibyte.txt b/test/multibyte.txt deleted file mode 100644 index 24a84b045..000000000 --- a/test/multibyte.txt +++ /dev/null @@ -1 +0,0 @@ -ファイル From 80fdc7ea179dc4bb8945a8c49c96eecc3c690e55 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 29 Oct 2018 17:11:53 +0000 Subject: [PATCH 36/46] Adds RSpec for Faraday::Error::ClientError --- lib/faraday/error.rb | 19 +++++++++------- spec/faraday/error_spec.rb | 45 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 8 deletions(-) create mode 100644 spec/faraday/error_spec.rb diff --git a/lib/faraday/error.rb b/lib/faraday/error.rb index 2052a53c7..ec004ceff 100644 --- a/lib/faraday/error.rb +++ b/lib/faraday/error.rb @@ -1,6 +1,7 @@ module Faraday # Faraday error base class. - class Error < StandardError; end + class Error < StandardError; + end # Faraday client error class. class ClientError < Error @@ -45,15 +46,18 @@ def inspect end # A unified client error for failed connections. - class ConnectionFailed < ClientError; end + class ConnectionFailed < ClientError; + end # A 404 error used in the RaiseError middleware # # @see Faraday::Response::RaiseError - class ResourceNotFound < ClientError; end - + class ResourceNotFound < ClientError; + end + # Raised by FaradayMiddleware::ResponseMiddleware - class ParsingError < ClientError; end + class ParsingError < ClientError; + end # A unified client error for timeouts. class TimeoutError < ClientError @@ -69,12 +73,11 @@ class SSLError < ClientError # Exception used to control the Retry middleware. # # @see Faraday::Request::Retry - class RetriableResponse < ClientError; end + class RetriableResponse < ClientError; + end [:ClientError, :ConnectionFailed, :ResourceNotFound, :ParsingError, :TimeoutError, :SSLError, :RetriableResponse].each do |const| Error.const_set(const, Faraday.const_get(const)) end - - end diff --git a/spec/faraday/error_spec.rb b/spec/faraday/error_spec.rb new file mode 100644 index 000000000..ef9e2337c --- /dev/null +++ b/spec/faraday/error_spec.rb @@ -0,0 +1,45 @@ +RSpec.describe Faraday::Error do + describe Faraday::Error::ClientError do + describe '.initialize' do + subject { described_class.new(exception, response) } + let(:response) { nil } + + context 'with exception only' do + let(:exception) { RuntimeError.new('test') } + + it { expect(subject.wrapped_exception).to eq(exception) } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq(exception.message) } + it { expect(subject.backtrace).to eq(exception.backtrace) } + it { expect(subject.inspect).to eq('#>') } + end + + context 'with response hash' do + let(:exception) { { status: 400 } } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to eq(exception) } + it { expect(subject.message).to eq('the server responded with status 400') } + it { expect(subject.inspect).to eq('#400}>') } + end + + context 'with string' do + let(:exception) { 'custom message' } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq('custom message') } + it { expect(subject.inspect).to eq('#>') } + end + + context 'with anything else #to_s' do + let(:exception) { %w(error1 error2) } + + it { expect(subject.wrapped_exception).to be_nil } + it { expect(subject.response).to be_nil } + it { expect(subject.message).to eq('["error1", "error2"]') } + it { expect(subject.inspect).to eq('#>') } + end + end + end +end \ No newline at end of file From d49ad8530e5ef5cffd6c0d02b71238bbe5de6644 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Tue, 30 Oct 2018 14:12:18 +0000 Subject: [PATCH 37/46] Converts RackBuilder tests into specs. Fixes an issue with `rack_builder#dup`. Adds new `unregister_middleware` method to Faraday::Middleware. --- lib/faraday.rb | 7 + lib/faraday/rack_builder.rb | 8 +- spec/faraday/rack_builder_spec.rb | 193 ++++++++++++++++++++++++++ test/connection_test.rb | 5 + test/middleware_stack_test.rb | 217 ------------------------------ test/strawberry.rb | 2 - 6 files changed, 209 insertions(+), 223 deletions(-) create mode 100644 spec/faraday/rack_builder_spec.rb delete mode 100644 test/middleware_stack_test.rb delete mode 100644 test/strawberry.rb diff --git a/lib/faraday.rb b/lib/faraday.rb index 403b59328..de91fc08a 100644 --- a/lib/faraday.rb +++ b/lib/faraday.rb @@ -200,6 +200,13 @@ def register_middleware(autoload_path = nil, mapping = nil) end end + # Unregister a previously registered middleware class. + # + # @param key [Symbol] key for the registered middleware. + def unregister_middleware(key) + @registered_middleware.delete(key) + end + # Lookup middleware class with a registered Symbol shortcut. # # @param key [Symbol] key for the registered middleware. diff --git a/lib/faraday/rack_builder.rb b/lib/faraday/rack_builder.rb index 513ce1275..85cef75b6 100644 --- a/lib/faraday/rack_builder.rb +++ b/lib/faraday/rack_builder.rb @@ -52,8 +52,8 @@ def build(app) end end - def initialize(handlers = []) - @adapter = nil + def initialize(handlers = [], adapter = nil) + @adapter = adapter @handlers = handlers if block_given? build(&Proc.new) @@ -176,11 +176,11 @@ def to_app(inner_app) end def ==(other) - other.is_a?(self.class) && @handlers == other.handlers + other.is_a?(self.class) && @handlers == other.handlers && @adapter == other.adapter end def dup - self.class.new(@handlers.dup) + self.class.new(@handlers.dup, @adapter.dup) end # ENV Keys diff --git a/spec/faraday/rack_builder_spec.rb b/spec/faraday/rack_builder_spec.rb new file mode 100644 index 000000000..62e4b896f --- /dev/null +++ b/spec/faraday/rack_builder_spec.rb @@ -0,0 +1,193 @@ +RSpec.describe Faraday::RackBuilder do + # mock handler classes + class Handler < Struct.new(:app) + def call(env) + (env[:request_headers]['X-Middleware'] ||= '') << ":#{self.class.name.split('::').last}" + app.call(env) + end + end + + class Apple < Handler; + end + class Orange < Handler; + end + class Banana < Handler; + end + + class Broken < Faraday::Middleware + dependency 'zomg/i_dont/exist' + end + + subject { conn.builder } + + context 'with default stack' do + let(:conn) { Faraday::Connection.new } + + it { expect(subject[0]).to eq(Faraday::Request.lookup_middleware(:url_encoded)) } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) } + end + + context 'with custom empty block' do + let(:conn) { Faraday::Connection.new {} } + + it { expect(subject[0]).to be_nil } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(Faraday.default_adapter)) } + end + + context 'with custom adapter only' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + it { expect(subject[0]).to be_nil } + it { expect(subject.adapter).to eq(Faraday::Adapter.lookup_middleware(:test)) } + end + + context 'with custom handler and adapter' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.use Apple + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + it 'locks the stack after making a request' do + expect(subject.locked?).to be_falsey + conn.get('/') + expect(subject.locked?).to be_truthy + expect { subject.use(Orange) }.to raise_error(Faraday::RackBuilder::StackLocked) + end + + it 'dup stack is unlocked' do + expect(subject.locked?).to be_falsey + subject.lock! + expect(subject.locked?).to be_truthy + dup = subject.dup + expect(dup).to eq(subject) + expect(dup.locked?).to be_falsey + end + + it 'allows to compare handlers' do + expect(subject.handlers.first).to eq(Faraday::RackBuilder::Handler.new(Apple)) + end + end + + context 'when having a single handler' do + let(:conn) { Faraday::Connection.new {} } + + before { subject.use(Apple) } + + it { expect(subject.handlers).to eq([Apple]) } + + it 'allows rebuilding' do + subject.build do |builder| + builder.use(Orange) + end + expect(subject.handlers).to eq([Orange]) + end + + it 'allows use' do + subject.use(Orange) + expect(subject.handlers).to eq([Apple, Orange]) + end + + it 'allows insert_before' do + subject.insert_before(Apple, Orange) + expect(subject.handlers).to eq([Orange, Apple]) + end + + it 'allows insert_after' do + subject.insert_after(Apple, Orange) + expect(subject.handlers).to eq([Apple, Orange]) + end + + it 'raises an error trying to use an unregistered symbol' do + expect { subject.use(:apple) }.to raise_error(Faraday::Error) do |err| + expect(err.message).to eq(':apple is not registered on Faraday::Middleware') + end + end + end + + context 'with custom registered middleware' do + let(:conn) { Faraday::Connection.new {} } + + after { Faraday::Middleware.unregister_middleware(:apple) } + + it 'allows to register with constant' do + Faraday::Middleware.register_middleware(apple: Apple) + subject.use(:apple) + expect(subject.handlers).to eq([Apple]) + end + + it 'allows to register with symbol' do + Faraday::Middleware.register_middleware(apple: :Apple) + subject.use(:apple) + expect(subject.handlers).to eq([Apple]) + end + + it 'allows to register with string' do + Faraday::Middleware.register_middleware(apple: 'Apple') + subject.use(:apple) + expect(subject.handlers).to eq([Apple]) + end + + it 'allows to register with Proc' do + Faraday::Middleware.register_middleware(apple: lambda { Apple }) + subject.use(:apple) + expect(subject.handlers).to eq([Apple]) + end + end + + context 'when having two handlers' do + let(:conn) { Faraday::Connection.new {} } + + before do + subject.use(Apple) + subject.use(Orange) + end + + it 'allows insert_before' do + subject.insert_before(Orange, Banana) + expect(subject.handlers).to eq([Apple, Banana, Orange]) + end + + it 'allows insert_after' do + subject.insert_after(Apple, Banana) + expect(subject.handlers).to eq([Apple, Banana, Orange]) + end + + it 'allows to swap handlers' do + subject.swap(Apple, Banana) + expect(subject.handlers).to eq([Banana, Orange]) + end + + it 'allows to delete a handler' do + subject.delete(Apple) + expect(subject.handlers).to eq([Orange]) + end + end + + context 'when having a handler with broken dependency' do + let(:conn) do + Faraday::Connection.new do |builder| + builder.adapter :test do |stub| + stub.get('/') { |_| [200, {}, ''] } + end + end + end + + before { subject.use(Broken) } + + it 'raises an error while making a request' do + expect { conn.get('/') }.to raise_error(RuntimeError) do |err| + expect(err.message).to eq('missing dependency for Broken: cannot load such file -- zomg/i_dont/exist') + end + end + end +end \ No newline at end of file diff --git a/test/connection_test.rb b/test/connection_test.rb index f6737ad29..20cf03f3b 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -53,6 +53,11 @@ def with_env(new_env) end end + def test_builder_is_passed_to_new_faraday_connection + new_conn = Faraday::Connection.new :builder => @builder + assert_equal @builder, new_conn.builder + end + def test_initialize_parses_host_out_of_given_url conn = Faraday::Connection.new 'http://sushi.com' assert_equal 'sushi.com', conn.host diff --git a/test/middleware_stack_test.rb b/test/middleware_stack_test.rb deleted file mode 100644 index 5ee5b5a45..000000000 --- a/test/middleware_stack_test.rb +++ /dev/null @@ -1,217 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class MiddlewareStackTest < Faraday::TestCase - # mock handler classes - class Handler < Struct.new(:app) - def call(env) - (env[:request_headers]['X-Middleware'] ||= '') << ":#{self.class.name.split('::').last}" - app.call(env) - end - end - class Apple < Handler; end - class Orange < Handler; end - class Banana < Handler; end - - class Broken < Faraday::Middleware - dependency 'zomg/i_dont/exist' - end - - def setup - @conn = Faraday::Connection.new - @builder = @conn.builder - end - - def test_default_stack - default_middleware = Faraday::Request.lookup_middleware(:url_encoded) - default_adapter_klass = Faraday::Adapter.lookup_middleware(Faraday.default_adapter) - assert @builder[0] == default_middleware - assert @builder.adapter == default_adapter_klass - end - - def test_sets_default_adapter_if_none_set - @conn = Faraday::Connection.new do |_| - # custom stack, but missing adapter - end - @builder = @conn.builder - default_adapter_klass = Faraday::Adapter.lookup_middleware(Faraday.default_adapter) - assert_nil @builder[0] - assert @builder.adapter == default_adapter_klass - end - - def test_use_provided_adapter - @conn = Faraday::Connection.new do |builder| - builder.adapter :test - end - @builder = @conn.builder - test_adapter_klass = Faraday::Adapter.lookup_middleware(:test) - assert_nil @builder[0] - assert @builder.adapter == test_adapter_klass - end - - def test_allows_rebuilding - build_stack Apple - build_stack Orange - assert_handlers %w[Orange] - end - - def test_allows_extending - build_handlers_stack Apple - @builder.use Orange - @builder.adapter :test, &test_adapter - assert_handlers %w[Apple Orange] - end - - def test_builder_is_passed_to_new_faraday_connection - new_conn = Faraday::Connection.new :builder => @builder - assert_equal @builder, new_conn.builder - end - - def test_insert_before - build_handlers_stack Apple, Orange - @builder.insert_before Apple, Banana - @builder.adapter :test, &test_adapter - assert_handlers %w[Banana Apple Orange] - end - - def test_insert_after - build_handlers_stack Apple, Orange - @builder.insert_after Apple, Banana - @builder.adapter :test, &test_adapter - assert_handlers %w[Apple Banana Orange] - end - - def test_swap_handlers - build_handlers_stack Apple, Orange - @builder.swap Apple, Banana - @builder.adapter :test, &test_adapter - assert_handlers %w[Banana Orange] - end - - def test_delete_handler - build_stack Apple, Orange - @builder.delete Apple - assert_handlers %w[Orange] - end - - def test_stack_is_locked_after_making_requests - build_stack Apple - assert !@builder.locked? - @conn.get('/') - assert @builder.locked? - - assert_raises Faraday::RackBuilder::StackLocked do - @conn.use Orange - end - end - - def test_duped_stack_is_unlocked - build_stack Apple - assert !@builder.locked? - @builder.lock! - assert @builder.locked? - - duped_connection = @conn.dup - assert_equal @builder, duped_connection.builder - assert !duped_connection.builder.locked? - end - - def test_handler_comparison - build_stack Apple - assert_equal @builder.handlers.first, Apple - assert_equal @builder.handlers[0,1], [Apple] - assert_equal @builder.handlers.first, Faraday::RackBuilder::Handler.new(Apple) - end - - def test_unregistered_symbol - err = assert_raises(Faraday::Error){ build_stack :apple } - assert_equal ":apple is not registered on Faraday::Middleware", err.message - end - - def test_registered_symbol - Faraday::Middleware.register_middleware :apple => Apple - begin - build_stack :apple - assert_handlers %w[Apple] - ensure - unregister_middleware Faraday::Middleware, :apple - end - end - - def test_registered_symbol_with_proc - Faraday::Middleware.register_middleware :apple => lambda { Apple } - begin - build_stack :apple - assert_handlers %w[Apple] - ensure - unregister_middleware Faraday::Middleware, :apple - end - end - - def test_registered_symbol_with_array - Faraday::Middleware.register_middleware File.expand_path("..", __FILE__), - :strawberry => [lambda { Strawberry }, 'strawberry'] - begin - build_stack :strawberry - assert_handlers %w[Strawberry] - ensure - unregister_middleware Faraday::Middleware, :strawberry - end - end - - def test_missing_dependencies - build_stack Broken - err = assert_raises RuntimeError do - @conn.get('/') - end - assert_match "missing dependency for MiddlewareStackTest::Broken: ", err.message - assert_match "zomg/i_dont/exist", err.message - end - - def test_env_stored_on_middleware_response_has_reference_to_the_response - env = response = nil - build_stack Struct.new(:app) { - define_method(:call) { |e| env, response = e, app.call(e) } - } - @conn.get("/") - assert_same env.response, response.env.response - end - - private - - # make a stack with test adapter that reflects the order of middleware - def build_stack(*handlers) - @builder.build do |b| - handlers.each { |handler| b.use(*handler) } - yield(b) if block_given? - - @builder.adapter :test, &test_adapter - end - end - - def build_handlers_stack(*handlers) - @builder.build do |b| - handlers.each { |handler| b.use(*handler) } - end - end - - def test_adapter - Proc.new do |stub| - stub.get '/' do |env| - # echo the "X-Middleware" request header in the body - [200, {}, env[:request_headers]['X-Middleware'].to_s] - end - end - end - - def assert_handlers(list) - echoed_list = @conn.get('/').body.to_s.split(':') - echoed_list.shift if echoed_list.first == '' - assert_equal list, echoed_list - end - - def unregister_middleware(component, key) - # TODO: unregister API? - component.instance_variable_get('@registered_middleware').delete(key) - end -end - diff --git a/test/strawberry.rb b/test/strawberry.rb deleted file mode 100644 index 80c8d0b34..000000000 --- a/test/strawberry.rb +++ /dev/null @@ -1,2 +0,0 @@ -class MiddlewareStackTest::Strawberry < MiddlewareStackTest::Handler -end From 393cb2cee693b85070c283098e64ef4ad1d14888 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Tue, 30 Oct 2018 14:20:29 +0000 Subject: [PATCH 38/46] No need to run tests twice anymore. SSL yes/no are now both tested. --- .travis.yml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.travis.yml b/.travis.yml index 28234a967..1a2470ac7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,6 @@ rvm: - 2.4 - 2.5 - ruby-head -env: - matrix: - - SSL=no - - SSL=yes deploy: provider: rubygems api_key: @@ -19,5 +15,4 @@ deploy: tags: true repo: lostisland/faraday rvm: 2.5 - condition: '"$SSL" = "yes"' From 27b5d5eea4d6a9a2cf614dc1395d331e12b612bc Mon Sep 17 00:00:00 2001 From: iMacTia Date: Wed, 7 Nov 2018 17:56:34 +0000 Subject: [PATCH 39/46] Partially convert connection tests into RSpec. --- spec/faraday/connection_spec.rb | 170 +++++++++++++++++++++++++++++++ test/connection_test.rb | 175 -------------------------------- 2 files changed, 170 insertions(+), 175 deletions(-) create mode 100644 spec/faraday/connection_spec.rb diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb new file mode 100644 index 000000000..af8a16127 --- /dev/null +++ b/spec/faraday/connection_spec.rb @@ -0,0 +1,170 @@ +shared_examples 'initializer with url' do + context 'with simple url' do + let(:address) { 'http://sushi.com' } + + it { expect(subject.host).to eq('sushi.com') } + it { expect(subject.port).to eq(80) } + it { expect(subject.scheme).to eq('http') } + it { expect(subject.path_prefix).to eq('/') } + it { expect(subject.params).to eq({}) } + end + + context 'with complex url' do + let(:address) { 'http://sushi.com:815/fish?a=1' } + + it { expect(subject.port).to eq(815) } + it { expect(subject.path_prefix).to eq('/fish') } + it { expect(subject.params).to eq({ 'a' => '1' }) } + end +end + +RSpec.describe Faraday::Connection do + describe '.new' do + subject { Faraday::Connection.new(url, options) } + + let(:url) { nil } + let(:options) { {} } + + context 'with implicit url param' do + # Faraday::Connection.new('http://sushi.com') + let(:url) { address } + + it_behaves_like 'initializer with url' + end + + context 'with explicit url param' do + # Faraday::Connection.new(url: 'http://sushi.com') + let(:url) { { url: address } } + + it_behaves_like 'initializer with url' + end + + context 'with custom builder' do + let(:custom_builder) { Faraday::RackBuilder.new } + let(:options) { { builder: custom_builder } } + + it { expect(subject.builder).to eq(custom_builder) } + end + + context 'with custom params' do + let(:options) { { params: { a: 1 } } } + + it { expect(subject.params).to eq({ 'a' => 1 }) } + end + + context 'with custom params and params in url' do + let(:url) { 'http://sushi.com/fish?a=1&b=2' } + let(:options) { { params: { a: 3 } } } + it { expect(subject.params).to eq({ 'a' => 3, 'b' => '2' }) } + end + + context 'with custom headers' do + let(:options) { { headers: { user_agent: 'Faraday' } } } + + it { expect(subject.headers['User-agent']).to eq('Faraday') } + end + + describe 'basic_auth' do + context 'calling the #basic_auth method' do + before { subject.basic_auth 'Aladdin', 'open sesame' } + + it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + end + + context 'adding basic auth info to url' do + let(:url) { 'http://Aladdin:open%20sesame@sushi.com/fish' } + + it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + end + end + + describe '#token_auth' do + before { subject.token_auth 'abcdef', nonce: 'abc' } + + it { expect(subject.headers['Authorization']).to eq('Token nonce="abc", token="abcdef"') } + end + + describe '#build_exclusive_url' do + let(:conn) { Faraday::Connection.new } + + context 'with relative path' do + subject { conn.build_exclusive_url('sake.html') } + + it 'uses connection host as default host' do + conn.host = 'sushi.com' + expect(subject.host).to eq('sushi.com') + expect(subject.scheme).to eq('http') + end + + it do + conn.path_prefix = '/fish' + expect(subject.path).to eq('/fish/sake.html') + end + + it do + conn.path_prefix = '/' + expect(subject.path).to eq('/sake.html') + end + + it do + conn.path_prefix = 'fish' + expect(subject.path).to eq('/fish/sake.html') + end + + it do + conn.path_prefix = '/fish/' + expect(subject.path).to eq('/fish/sake.html') + end + end + + context 'with absolute path' do + subject { conn.build_exclusive_url('/sake.html') } + + after { expect(subject.path).to eq('/sake.html') } + + it { conn.path_prefix = '/fish' } + it { conn.path_prefix = '/' } + it { conn.path_prefix = 'fish' } + it { conn.path_prefix = '/fish/' } + end + + it 'overrides connection port for absolute url' do + conn.port = 23 + uri = conn.build_exclusive_url('http://sushi.com') + expect(uri.port).to eq(80) + end + + it 'parses url params into path' do + uri = conn.build_exclusive_url('http://sushi.com/sake.html') + expect(uri.path).to eq('/sake.html') + end + + it 'does not add ending slash given nil url' do + conn.url_prefix = 'http://sushi.com/nigiri' + uri = conn.build_exclusive_url + expect(uri.path).to eq('/nigiri') + end + + it 'does not add ending slash given empty url' do + conn.url_prefix = 'http://sushi.com/nigiri' + uri = conn.build_exclusive_url('') + expect(uri.path).to eq('/nigiri') + end + + it 'does not use connection params' do + conn.url_prefix = 'http://sushi.com/nigiri' + conn.params = { :a => 1 } + expect(conn.build_exclusive_url.to_s).to eq('http://sushi.com/nigiri') + end + + it 'allows to provide params argument' do + conn.url_prefix = 'http://sushi.com/nigiri' + conn.params = { :a => 1 } + params = Faraday::Utils::ParamsHash.new + params[:a] = 2 + uri = conn.build_exclusive_url(nil, params) + expect(uri.to_s).to eq('http://sushi.com/nigiri?a=2') + end + end + end +end \ No newline at end of file diff --git a/test/connection_test.rb b/test/connection_test.rb index 20cf03f3b..4475779f4 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -53,181 +53,6 @@ def with_env(new_env) end end - def test_builder_is_passed_to_new_faraday_connection - new_conn = Faraday::Connection.new :builder => @builder - assert_equal @builder, new_conn.builder - end - - def test_initialize_parses_host_out_of_given_url - conn = Faraday::Connection.new 'http://sushi.com' - assert_equal 'sushi.com', conn.host - end - - def test_initialize_inherits_default_port_out_of_given_url - conn = Faraday::Connection.new 'http://sushi.com' - assert_equal 80, conn.port - end - - def test_initialize_parses_scheme_out_of_given_url - conn = Faraday::Connection.new 'http://sushi.com' - assert_equal 'http', conn.scheme - end - - def test_initialize_parses_port_out_of_given_url - conn = Faraday::Connection.new 'http://sushi.com:815' - assert_equal 815, conn.port - end - - def test_initialize_parses_nil_path_prefix_out_of_given_url - conn = Faraday::Connection.new 'http://sushi.com' - assert_equal '/', conn.path_prefix - end - - def test_initialize_parses_path_prefix_out_of_given_url - conn = Faraday::Connection.new 'http://sushi.com/fish' - assert_equal '/fish', conn.path_prefix - end - - def test_initialize_parses_path_prefix_out_of_given_url_option - conn = Faraday::Connection.new :url => 'http://sushi.com/fish' - assert_equal '/fish', conn.path_prefix - end - - def test_initialize_stores_default_params_from_options - conn = Faraday::Connection.new :params => {:a => 1} - assert_equal({'a' => 1}, conn.params) - end - - def test_initialize_stores_default_params_from_uri - conn = Faraday::Connection.new 'http://sushi.com/fish?a=1' - assert_equal({'a' => '1'}, conn.params) - end - - def test_initialize_stores_default_params_from_uri_and_options - conn = Faraday::Connection.new 'http://sushi.com/fish?a=1&b=2', :params => {'a' => 3} - assert_equal({'a' => 3, 'b' => '2'}, conn.params) - end - - def test_initialize_stores_default_headers_from_options - conn = Faraday::Connection.new :headers => {:user_agent => 'Faraday'} - assert_equal 'Faraday', conn.headers['User-agent'] - end - - def test_basic_auth_sets_header - conn = Faraday::Connection.new - assert_nil conn.headers['Authorization'] - - conn.basic_auth 'Aladdin', 'open sesame' - assert auth = conn.headers['Authorization'] - assert_equal 'Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==', auth - end - - def test_auto_parses_basic_auth_from_url_and_unescapes - conn = Faraday::Connection.new :url => 'http://foo%40bar.com:pass%20word@sushi.com/fish' - assert auth = conn.headers['Authorization'] - assert_equal Faraday::Request::BasicAuthentication.header('foo@bar.com', 'pass word'), auth - end - - def test_token_auth_sets_header - conn = Faraday::Connection.new - assert_nil conn.headers['Authorization'] - - conn.token_auth 'abcdef', :nonce => 'abc' - assert auth = conn.headers['Authorization'] - assert_match(/^Token /, auth) - assert_match(/token="abcdef"/, auth) - assert_match(/nonce="abc"/, auth) - end - - def test_build_exclusive_url_uses_connection_host_as_default_uri_host - conn = Faraday::Connection.new - conn.host = 'sushi.com' - uri = conn.build_exclusive_url('/sake.html') - assert_equal 'sushi.com', uri.host - end - - def test_build_exclusive_url_overrides_connection_port_for_absolute_urls - conn = Faraday::Connection.new - conn.port = 23 - uri = conn.build_exclusive_url('http://sushi.com') - assert_equal 80, uri.port - end - - def test_build_exclusive_url_uses_connection_scheme_as_default_uri_scheme - conn = Faraday::Connection.new 'http://sushi.com' - uri = conn.build_exclusive_url('/sake.html') - assert_equal 'http', uri.scheme - end - - def test_build_exclusive_url_uses_connection_path_prefix_to_customize_path - conn = Faraday::Connection.new - conn.path_prefix = '/fish' - uri = conn.build_exclusive_url('sake.html') - assert_equal '/fish/sake.html', uri.path - end - - def test_build_exclusive_url_uses_root_connection_path_prefix_to_customize_path - conn = Faraday::Connection.new - conn.path_prefix = '/' - uri = conn.build_exclusive_url('sake.html') - assert_equal '/sake.html', uri.path - end - - def test_build_exclusive_url_forces_connection_path_prefix_to_be_absolute - conn = Faraday::Connection.new - conn.path_prefix = 'fish' - uri = conn.build_exclusive_url('sake.html') - assert_equal '/fish/sake.html', uri.path - end - - def test_build_exclusive_url_ignores_connection_path_prefix_trailing_slash - conn = Faraday::Connection.new - conn.path_prefix = '/fish/' - uri = conn.build_exclusive_url('sake.html') - assert_equal '/fish/sake.html', uri.path - end - - def test_build_exclusive_url_allows_absolute_uri_to_ignore_connection_path_prefix - conn = Faraday::Connection.new - conn.path_prefix = '/fish' - uri = conn.build_exclusive_url('/sake.html') - assert_equal '/sake.html', uri.path - end - - def test_build_exclusive_url_parses_url_params_into_path - conn = Faraday::Connection.new - uri = conn.build_exclusive_url('http://sushi.com/sake.html') - assert_equal '/sake.html', uri.path - end - - def test_build_exclusive_url_doesnt_add_ending_slash_given_nil_url - conn = Faraday::Connection.new - conn.url_prefix = 'http://sushi.com/nigiri' - uri = conn.build_exclusive_url - assert_equal '/nigiri', uri.path - end - - def test_build_exclusive_url_doesnt_add_ending_slash_given_empty_url - conn = Faraday::Connection.new - conn.url_prefix = 'http://sushi.com/nigiri' - uri = conn.build_exclusive_url('') - assert_equal '/nigiri', uri.path - end - - def test_build_exclusive_url_doesnt_use_connection_params - conn = Faraday::Connection.new 'http://sushi.com/nigiri' - conn.params = {:a => 1} - assert_equal 'http://sushi.com/nigiri', conn.build_exclusive_url.to_s - end - - def test_build_exclusive_url_uses_argument_params - conn = Faraday::Connection.new 'http://sushi.com/nigiri' - conn.params = {:a => 1} - params = Faraday::Utils::ParamsHash.new - params[:a] = 2 - url = conn.build_exclusive_url(nil, params) - assert_equal 'http://sushi.com/nigiri?a=2', url.to_s - end def test_build_url_uses_params conn = Faraday::Connection.new 'http://sushi.com/nigiri' From 3bc27a2ed402f2b0db260080953cb13ad046e81b Mon Sep 17 00:00:00 2001 From: iMacTia Date: Thu, 8 Nov 2018 10:02:57 +0000 Subject: [PATCH 40/46] More connection tests converted into RSpec. --- spec/faraday/connection_spec.rb | 60 ++++++++++++++++++++++++++++++--- test/connection_test.rb | 52 ---------------------------- 2 files changed, 55 insertions(+), 57 deletions(-) diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb index af8a16127..b3951c77b 100644 --- a/spec/faraday/connection_spec.rb +++ b/spec/faraday/connection_spec.rb @@ -128,17 +128,22 @@ it { conn.path_prefix = '/fish/' } end + context 'with complete url' do + subject { conn.build_exclusive_url('http://sushi.com/sake.html?a=1') } + + it { expect(subject.scheme).to eq('http') } + it { expect(subject.host).to eq('sushi.com') } + it { expect(subject.port).to eq(80) } + it { expect(subject.path).to eq('/sake.html') } + it { expect(subject.query).to eq('a=1') } + end + it 'overrides connection port for absolute url' do conn.port = 23 uri = conn.build_exclusive_url('http://sushi.com') expect(uri.port).to eq(80) end - it 'parses url params into path' do - uri = conn.build_exclusive_url('http://sushi.com/sake.html') - expect(uri.path).to eq('/sake.html') - end - it 'does not add ending slash given nil url' do conn.url_prefix = 'http://sushi.com/nigiri' uri = conn.build_exclusive_url @@ -165,6 +170,51 @@ uri = conn.build_exclusive_url(nil, params) expect(uri.to_s).to eq('http://sushi.com/nigiri?a=2') end + + it 'handles uri instances' do + uri = conn.build_exclusive_url(URI('/sake.html')) + expect(uri.path).to eq('/sake.html') + end + + context 'with url_prefixed connection' do + let(:conn) { Faraday::Connection.new(url: 'http://sushi.com/sushi/') } + + it 'parses url and changes scheme' do + conn.scheme = 'https' + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('https://sushi.com/sushi/sake.html') + end + + it 'joins url to base with ending slash' do + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('http://sushi.com/sushi/sake.html') + end + + it 'used default base with ending slash' do + uri = conn.build_exclusive_url + expect(uri.to_s).to eq('http://sushi.com/sushi/') + end + + it 'overrides base' do + uri = conn.build_exclusive_url('/sake/') + expect(uri.to_s).to eq('http://sushi.com/sake/') + end + end + end + + describe '#build_url' do + let(:url) { 'http://sushi.com/nigiri' } + + it 'uses params' do + subject.params = { a: 1, b: 1 } + expect(subject.build_url.to_s).to eq('http://sushi.com/nigiri?a=1&b=1') + end + + it 'merges params' do + subject.params = { a: 1, b: 1 } + url = subject.build_url(nil, b: 2, c: 3) + expect(url.to_s).to eq('http://sushi.com/nigiri?a=1&b=2&c=3') + end end end end \ No newline at end of file diff --git a/test/connection_test.rb b/test/connection_test.rb index 4475779f4..a28013d41 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -54,19 +54,6 @@ def with_env(new_env) end - def test_build_url_uses_params - conn = Faraday::Connection.new 'http://sushi.com/nigiri' - conn.params = {:a => 1, :b => 1} - assert_equal 'http://sushi.com/nigiri?a=1&b=1', conn.build_url.to_s - end - - def test_build_url_merges_params - conn = Faraday::Connection.new 'http://sushi.com/nigiri' - conn.params = {:a => 1, :b => 1} - url = conn.build_url(nil, :b => 2, :c => 3) - assert_equal 'http://sushi.com/nigiri?a=1&b=2&c=3', url.to_s - end - def test_request_header_change_does_not_modify_connection_header connection = Faraday.new(:url => 'https://asushi.com/sake.html') connection.headers = {'Authorization' => 'token abc123'} @@ -108,45 +95,6 @@ def test_env_url_without_braketizing_repeated_params_in_query assert_equal 'a=1&a=2', uri.query end - def test_build_exclusive_url_parses_url - conn = Faraday::Connection.new - uri = conn.build_exclusive_url('http://sushi.com/sake.html') - assert_equal 'http', uri.scheme - assert_equal 'sushi.com', uri.host - assert_equal '/sake.html', uri.path - end - - def test_build_exclusive_url_parses_url_and_changes_scheme - conn = Faraday::Connection.new :url => 'http://sushi.com/sushi' - conn.scheme = 'https' - uri = conn.build_exclusive_url('sake.html') - assert_equal 'https://sushi.com/sushi/sake.html', uri.to_s - end - - def test_build_exclusive_url_joins_url_to_base_with_ending_slash - conn = Faraday::Connection.new :url => 'http://sushi.com/sushi/' - uri = conn.build_exclusive_url('sake.html') - assert_equal 'http://sushi.com/sushi/sake.html', uri.to_s - end - - def test_build_exclusive_url_used_default_base_with_ending_slash - conn = Faraday::Connection.new :url => 'http://sushi.com/sushi/' - uri = conn.build_exclusive_url - assert_equal 'http://sushi.com/sushi/', uri.to_s - end - - def test_build_exclusive_url_overrides_base - conn = Faraday::Connection.new :url => 'http://sushi.com/sushi/' - uri = conn.build_exclusive_url('/sake/') - assert_equal 'http://sushi.com/sake/', uri.to_s - end - - def test_build_exclusive_url_handles_uri_instances - conn = Faraday::Connection.new - uri = conn.build_exclusive_url(URI('/sake.html')) - assert_equal '/sake.html', uri.path - end - def test_proxy_accepts_string with_env 'http_proxy' => 'http://duncan.proxy.com:80' do conn = Faraday::Connection.new From f016ebb90d2b60a8efd321f3e268a8f43b3da214 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Thu, 8 Nov 2018 10:46:35 +0000 Subject: [PATCH 41/46] More connection tests converted into RSpec. --- spec/faraday/connection_spec.rb | 272 ++++++++++++++++++-------------- test/connection_test.rb | 27 ---- 2 files changed, 153 insertions(+), 146 deletions(-) diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb index b3951c77b..7563f9b34 100644 --- a/spec/faraday/connection_spec.rb +++ b/spec/faraday/connection_spec.rb @@ -19,11 +19,12 @@ end RSpec.describe Faraday::Connection do - describe '.new' do - subject { Faraday::Connection.new(url, options) } + let(:conn) { Faraday::Connection.new(url, options) } + let(:url) { nil } + let(:options) { {} } - let(:url) { nil } - let(:options) { {} } + describe '.new' do + subject { conn } context 'with implicit url param' do # Faraday::Connection.new('http://sushi.com') @@ -63,158 +64,191 @@ it { expect(subject.headers['User-agent']).to eq('Faraday') } end + end + + describe 'basic_auth' do + subject { conn } + + context 'calling the #basic_auth method' do + before { subject.basic_auth 'Aladdin', 'open sesame' } + + it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + end + + context 'adding basic auth info to url' do + let(:url) { 'http://Aladdin:open%20sesame@sushi.com/fish' } + + it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + end + end - describe 'basic_auth' do - context 'calling the #basic_auth method' do - before { subject.basic_auth 'Aladdin', 'open sesame' } + describe '#token_auth' do + before { subject.token_auth('abcdef', nonce: 'abc') } - it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + it { expect(subject.headers['Authorization']).to eq('Token nonce="abc", token="abcdef"') } + end + + describe '#build_exclusive_url' do + context 'with relative path' do + subject { conn.build_exclusive_url('sake.html') } + + it 'uses connection host as default host' do + conn.host = 'sushi.com' + expect(subject.host).to eq('sushi.com') + expect(subject.scheme).to eq('http') end - context 'adding basic auth info to url' do - let(:url) { 'http://Aladdin:open%20sesame@sushi.com/fish' } + it do + conn.path_prefix = '/fish' + expect(subject.path).to eq('/fish/sake.html') + end - it { expect(subject.headers['Authorization']).to eq('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==') } + it do + conn.path_prefix = '/' + expect(subject.path).to eq('/sake.html') end - end - describe '#token_auth' do - before { subject.token_auth 'abcdef', nonce: 'abc' } + it do + conn.path_prefix = 'fish' + expect(subject.path).to eq('/fish/sake.html') + end - it { expect(subject.headers['Authorization']).to eq('Token nonce="abc", token="abcdef"') } + it do + conn.path_prefix = '/fish/' + expect(subject.path).to eq('/fish/sake.html') + end end - describe '#build_exclusive_url' do - let(:conn) { Faraday::Connection.new } + context 'with absolute path' do + subject { conn.build_exclusive_url('/sake.html') } - context 'with relative path' do - subject { conn.build_exclusive_url('sake.html') } + after { expect(subject.path).to eq('/sake.html') } - it 'uses connection host as default host' do - conn.host = 'sushi.com' - expect(subject.host).to eq('sushi.com') - expect(subject.scheme).to eq('http') - end + it { conn.path_prefix = '/fish' } + it { conn.path_prefix = '/' } + it { conn.path_prefix = 'fish' } + it { conn.path_prefix = '/fish/' } + end - it do - conn.path_prefix = '/fish' - expect(subject.path).to eq('/fish/sake.html') - end + context 'with complete url' do + subject { conn.build_exclusive_url('http://sushi.com/sake.html?a=1') } - it do - conn.path_prefix = '/' - expect(subject.path).to eq('/sake.html') - end + it { expect(subject.scheme).to eq('http') } + it { expect(subject.host).to eq('sushi.com') } + it { expect(subject.port).to eq(80) } + it { expect(subject.path).to eq('/sake.html') } + it { expect(subject.query).to eq('a=1') } + end - it do - conn.path_prefix = 'fish' - expect(subject.path).to eq('/fish/sake.html') - end + it 'overrides connection port for absolute url' do + conn.port = 23 + uri = conn.build_exclusive_url('http://sushi.com') + expect(uri.port).to eq(80) + end - it do - conn.path_prefix = '/fish/' - expect(subject.path).to eq('/fish/sake.html') - end - end + it 'does not add ending slash given nil url' do + conn.url_prefix = 'http://sushi.com/nigiri' + uri = conn.build_exclusive_url + expect(uri.path).to eq('/nigiri') + end - context 'with absolute path' do - subject { conn.build_exclusive_url('/sake.html') } + it 'does not add ending slash given empty url' do + conn.url_prefix = 'http://sushi.com/nigiri' + uri = conn.build_exclusive_url('') + expect(uri.path).to eq('/nigiri') + end - after { expect(subject.path).to eq('/sake.html') } + it 'does not use connection params' do + conn.url_prefix = 'http://sushi.com/nigiri' + conn.params = { :a => 1 } + expect(conn.build_exclusive_url.to_s).to eq('http://sushi.com/nigiri') + end - it { conn.path_prefix = '/fish' } - it { conn.path_prefix = '/' } - it { conn.path_prefix = 'fish' } - it { conn.path_prefix = '/fish/' } - end + it 'allows to provide params argument' do + conn.url_prefix = 'http://sushi.com/nigiri' + conn.params = { :a => 1 } + params = Faraday::Utils::ParamsHash.new + params[:a] = 2 + uri = conn.build_exclusive_url(nil, params) + expect(uri.to_s).to eq('http://sushi.com/nigiri?a=2') + end + + it 'handles uri instances' do + uri = conn.build_exclusive_url(URI('/sake.html')) + expect(uri.path).to eq('/sake.html') + end - context 'with complete url' do - subject { conn.build_exclusive_url('http://sushi.com/sake.html?a=1') } + context 'with url_prefixed connection' do + let(:url) { 'http://sushi.com/sushi/' } - it { expect(subject.scheme).to eq('http') } - it { expect(subject.host).to eq('sushi.com') } - it { expect(subject.port).to eq(80) } - it { expect(subject.path).to eq('/sake.html') } - it { expect(subject.query).to eq('a=1') } + it 'parses url and changes scheme' do + conn.scheme = 'https' + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('https://sushi.com/sushi/sake.html') end - it 'overrides connection port for absolute url' do - conn.port = 23 - uri = conn.build_exclusive_url('http://sushi.com') - expect(uri.port).to eq(80) + it 'joins url to base with ending slash' do + uri = conn.build_exclusive_url('sake.html') + expect(uri.to_s).to eq('http://sushi.com/sushi/sake.html') end - it 'does not add ending slash given nil url' do - conn.url_prefix = 'http://sushi.com/nigiri' + it 'used default base with ending slash' do uri = conn.build_exclusive_url - expect(uri.path).to eq('/nigiri') + expect(uri.to_s).to eq('http://sushi.com/sushi/') end - it 'does not add ending slash given empty url' do - conn.url_prefix = 'http://sushi.com/nigiri' - uri = conn.build_exclusive_url('') - expect(uri.path).to eq('/nigiri') + it 'overrides base' do + uri = conn.build_exclusive_url('/sake/') + expect(uri.to_s).to eq('http://sushi.com/sake/') end + end + end - it 'does not use connection params' do - conn.url_prefix = 'http://sushi.com/nigiri' - conn.params = { :a => 1 } - expect(conn.build_exclusive_url.to_s).to eq('http://sushi.com/nigiri') - end + describe '#build_url' do + let(:url) { 'http://sushi.com/nigiri' } - it 'allows to provide params argument' do - conn.url_prefix = 'http://sushi.com/nigiri' - conn.params = { :a => 1 } - params = Faraday::Utils::ParamsHash.new - params[:a] = 2 - uri = conn.build_exclusive_url(nil, params) - expect(uri.to_s).to eq('http://sushi.com/nigiri?a=2') - end + it 'uses params' do + conn.params = { a: 1, b: 1 } + expect(conn.build_url.to_s).to eq('http://sushi.com/nigiri?a=1&b=1') + end - it 'handles uri instances' do - uri = conn.build_exclusive_url(URI('/sake.html')) - expect(uri.path).to eq('/sake.html') - end + it 'merges params' do + conn.params = { a: 1, b: 1 } + url = conn.build_url(nil, b: 2, c: 3) + expect(url.to_s).to eq('http://sushi.com/nigiri?a=1&b=2&c=3') + end + end - context 'with url_prefixed connection' do - let(:conn) { Faraday::Connection.new(url: 'http://sushi.com/sushi/') } - - it 'parses url and changes scheme' do - conn.scheme = 'https' - uri = conn.build_exclusive_url('sake.html') - expect(uri.to_s).to eq('https://sushi.com/sushi/sake.html') - end - - it 'joins url to base with ending slash' do - uri = conn.build_exclusive_url('sake.html') - expect(uri.to_s).to eq('http://sushi.com/sushi/sake.html') - end - - it 'used default base with ending slash' do - uri = conn.build_exclusive_url - expect(uri.to_s).to eq('http://sushi.com/sushi/') - end - - it 'overrides base' do - uri = conn.build_exclusive_url('/sake/') - expect(uri.to_s).to eq('http://sushi.com/sake/') - end - end + describe '#to_env' do + subject { conn.build_request(:get).to_env(conn).url } + + let(:url) { 'http://sushi.com/sake.html' } + let(:options) { { params: @params } } + + it 'parses url params into query' do + @params = { 'a[b]' => '1 + 2'} + expect(subject.query).to eq('a%5Bb%5D=1+%2B+2') end - describe '#build_url' do - let(:url) { 'http://sushi.com/nigiri' } + it 'escapes per spec' do + @params = { 'a' => '1+2 foo~bar.-baz' } + expect(subject.query).to eq('a=1%2B2+foo~bar.-baz') + end - it 'uses params' do - subject.params = { a: 1, b: 1 } - expect(subject.build_url.to_s).to eq('http://sushi.com/nigiri?a=1&b=1') - end + it 'bracketizes nested params in query' do + @params = { 'a' => { 'b' => 'c' } } + expect(subject.query).to eq('a%5Bb%5D=c') + end - it 'merges params' do - subject.params = { a: 1, b: 1 } - url = subject.build_url(nil, b: 2, c: 3) - expect(url.to_s).to eq('http://sushi.com/nigiri?a=1&b=2&c=3') - end + it 'bracketizes repeated params in query' do + @params = { 'a' => [1, 2] } + expect(subject.query).to eq('a%5B%5D=1&a%5B%5D=2') + end + + it 'without braketizing repeated params in query' do + @params = { 'a' => [1, 2] } + conn.options.params_encoder = Faraday::FlatParamsEncoder + expect(subject.query).to eq('a=1&a=2') end end end \ No newline at end of file diff --git a/test/connection_test.rb b/test/connection_test.rb index a28013d41..e84bc508a 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -68,33 +68,6 @@ def test_request_header_change_does_not_modify_connection_header assert !request.headers.include?('Authorization') end - def test_env_url_parses_url_params_into_query - uri = env_url('http://sushi.com/sake.html', 'a[b]' => '1 + 2') - assert_equal 'a%5Bb%5D=1+%2B+2', uri.query - end - - def test_env_url_escapes_per_spec - uri = env_url(nil, 'a' => '1+2 foo~bar.-baz') - assert_equal 'a=1%2B2+foo~bar.-baz', uri.query - end - - def test_env_url_bracketizes_nested_params_in_query - url = env_url nil, 'a' => {'b' => 'c'} - assert_equal 'a%5Bb%5D=c', url.query - end - - def test_env_url_bracketizes_repeated_params_in_query - uri = env_url('http://sushi.com/sake.html', 'a' => [1, 2]) - assert_equal 'a%5B%5D=1&a%5B%5D=2', uri.query - end - - def test_env_url_without_braketizing_repeated_params_in_query - uri = env_url 'http://sushi.com', 'a' => [1, 2] do |conn| - conn.options.params_encoder = Faraday::FlatParamsEncoder - end - assert_equal 'a=1&a=2', uri.query - end - def test_proxy_accepts_string with_env 'http_proxy' => 'http://duncan.proxy.com:80' do conn = Faraday::Connection.new From e31d7afe84deae64865ee8695eaf12ee23ad580a Mon Sep 17 00:00:00 2001 From: iMacTia Date: Thu, 8 Nov 2018 11:18:38 +0000 Subject: [PATCH 42/46] More connection tests converted into RSpec. --- spec/faraday/connection_spec.rb | 71 ++++++++++++++++++++++++ spec/support/helper_methods.rb | 21 +++++++ test/connection_test.rb | 97 --------------------------------- 3 files changed, 92 insertions(+), 97 deletions(-) diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb index 7563f9b34..ce206fb11 100644 --- a/spec/faraday/connection_spec.rb +++ b/spec/faraday/connection_spec.rb @@ -251,4 +251,75 @@ expect(subject.query).to eq('a=1&a=2') end end + + describe 'proxy support' do + it 'accepts string' do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + conn.proxy = 'http://proxy.com' + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'accepts uri' do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + conn.proxy = URI.parse('http://proxy.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'accepts hash with string uri' do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + conn.proxy = {:uri => 'http://proxy.com', :user => 'rick'} + expect(conn.proxy.host).to eq('proxy.com') + expect(conn.proxy.user).to eq('rick') + end + end + + it 'accepts hash' do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + conn.proxy = {:uri => URI.parse('http://proxy.com'), :user => 'rick'} + expect(conn.proxy.host).to eq('proxy.com') + expect(conn.proxy.user).to eq('rick') + end + end + + it 'accepts http env' do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + expect(conn.proxy.host).to eq('duncan.proxy.com') + end + end + + it 'accepts http env with auth' do + with_env 'http_proxy' => 'http://a%40b:my%20pass@duncan.proxy.com:80' do + expect(conn.proxy.user).to eq('a@b') + expect(conn.proxy.password).to eq('my pass') + end + end + + it 'accepts env without scheme' do + with_env 'http_proxy' => 'localhost:8888' do + uri = conn.proxy[:uri] + expect(uri.host).to eq('localhost') + expect(uri.port).to eq(8888) + end + end + + it 'fetches no proxy from nil env' do + with_env 'http_proxy' => nil do + expect(conn.proxy).to be_nil + end + end + + it 'fetches no proxy from blank env' do + with_env 'http_proxy' => '' do + expect(conn.proxy).to be_nil + end + end + + it 'does not accept uppercase env' do + with_env 'HTTP_PROXY' => 'http://localhost:8888/' do + expect(conn.proxy).to be_nil + end + end + end end \ No newline at end of file diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index 069eb522d..67908b0dd 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -40,6 +40,27 @@ def with_default_uri_parser(parser) end end + def with_env(new_env) + old_env = {} + + new_env.each do |key, value| + old_env[key] = ENV.fetch(key, false) + ENV[key] = value + end + + begin + yield + ensure + old_env.each do |key, value| + if value == false + ENV.delete key + else + ENV[key] = value + end + end + end + end + def capture_warnings old, $stderr = $stderr, StringIO.new begin diff --git a/test/connection_test.rb b/test/connection_test.rb index e84bc508a..d74d69bfa 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -32,26 +32,7 @@ def with_env_proxy_disabled end end - def with_env(new_env) - old_env = {} - new_env.each do |key, value| - old_env[key] = ENV.fetch(key, false) - ENV[key] = value - end - - begin - yield - ensure - old_env.each do |key, value| - if value == false - ENV.delete key - else - ENV[key] = value - end - end - end - end def test_request_header_change_does_not_modify_connection_header @@ -68,84 +49,6 @@ def test_request_header_change_does_not_modify_connection_header assert !request.headers.include?('Authorization') end - def test_proxy_accepts_string - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn = Faraday::Connection.new - conn.proxy = 'http://proxy.com' - assert_equal 'proxy.com', conn.proxy.host - end - end - - def test_proxy_accepts_uri - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn = Faraday::Connection.new - conn.proxy = URI.parse('http://proxy.com') - assert_equal 'proxy.com', conn.proxy.host - end - end - - def test_proxy_accepts_hash_with_string_uri - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn = Faraday::Connection.new - conn.proxy = {:uri => 'http://proxy.com', :user => 'rick'} - assert_equal 'proxy.com', conn.proxy.host - assert_equal 'rick', conn.proxy.user - end - end - - def test_proxy_accepts_hash - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn = Faraday::Connection.new - conn.proxy = {:uri => URI.parse('http://proxy.com'), :user => 'rick'} - assert_equal 'proxy.com', conn.proxy.host - assert_equal 'rick', conn.proxy.user - end - end - - def test_proxy_accepts_http_env - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn = Faraday::Connection.new - assert_equal 'duncan.proxy.com', conn.proxy.host - end - end - - def test_proxy_accepts_http_env_with_auth - with_env 'http_proxy' => 'http://a%40b:my%20pass@duncan.proxy.com:80' do - conn = Faraday::Connection.new - assert_equal 'a@b', conn.proxy.user - assert_equal 'my pass', conn.proxy.password - end - end - - def test_proxy_accepts_env_without_scheme - with_env 'http_proxy' => 'localhost:8888' do - uri = Faraday::Connection.new.proxy[:uri] - assert_equal 'localhost', uri.host - assert_equal 8888, uri.port - end - end - - def test_no_proxy_from_env - with_env 'http_proxy' => nil do - conn = Faraday::Connection.new - assert_nil conn.proxy - end - end - - def test_no_proxy_from_blank_env - with_env 'http_proxy' => '' do - conn = Faraday::Connection.new - assert_nil conn.proxy - end - end - - def test_proxy_doesnt_accept_uppercase_env - with_env 'HTTP_PROXY' => 'http://localhost:8888/' do - conn = Faraday::Connection.new - assert_nil conn.proxy - end - end - def test_dynamic_proxy with_test_conn do with_env 'http_proxy' => 'http://duncan.proxy.com:80' do From 8826971b17fa918843fd77fa2586fa409901e315 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Thu, 8 Nov 2018 11:47:51 +0000 Subject: [PATCH 43/46] More connection tests converted into RSpec. --- spec/faraday/connection_spec.rb | 110 +++++++++++++++++++++++++++++--- spec/support/helper_methods.rb | 10 +++ test/connection_test.rb | 90 +------------------------- 3 files changed, 111 insertions(+), 99 deletions(-) diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb index ce206fb11..62203c91a 100644 --- a/spec/faraday/connection_spec.rb +++ b/spec/faraday/connection_spec.rb @@ -226,7 +226,7 @@ let(:options) { { params: @params } } it 'parses url params into query' do - @params = { 'a[b]' => '1 + 2'} + @params = { 'a[b]' => '1 + 2' } expect(subject.query).to eq('a%5Bb%5D=1+%2B+2') end @@ -254,43 +254,43 @@ describe 'proxy support' do it 'accepts string' do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + with_env 'http_proxy' => 'http://proxy.com:80' do conn.proxy = 'http://proxy.com' expect(conn.proxy.host).to eq('proxy.com') end end it 'accepts uri' do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + with_env 'http_proxy' => 'http://proxy.com:80' do conn.proxy = URI.parse('http://proxy.com') expect(conn.proxy.host).to eq('proxy.com') end end it 'accepts hash with string uri' do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn.proxy = {:uri => 'http://proxy.com', :user => 'rick'} + with_env 'http_proxy' => 'http://proxy.com:80' do + conn.proxy = { :uri => 'http://proxy.com', :user => 'rick' } expect(conn.proxy.host).to eq('proxy.com') expect(conn.proxy.user).to eq('rick') end end it 'accepts hash' do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn.proxy = {:uri => URI.parse('http://proxy.com'), :user => 'rick'} + with_env 'http_proxy' => 'http://proxy.com:80' do + conn.proxy = { :uri => URI.parse('http://proxy.com'), :user => 'rick' } expect(conn.proxy.host).to eq('proxy.com') expect(conn.proxy.user).to eq('rick') end end it 'accepts http env' do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - expect(conn.proxy.host).to eq('duncan.proxy.com') + with_env 'http_proxy' => 'http://proxy.com:80' do + expect(conn.proxy.host).to eq('proxy.com') end end it 'accepts http env with auth' do - with_env 'http_proxy' => 'http://a%40b:my%20pass@duncan.proxy.com:80' do + with_env 'http_proxy' => 'http://a%40b:my%20pass@proxy.com:80' do expect(conn.proxy.user).to eq('a@b') expect(conn.proxy.password).to eq('my pass') end @@ -321,5 +321,95 @@ expect(conn.proxy).to be_nil end end + + it 'allows when url in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://example.com') + expect(conn.proxy).to be_nil + end + end + + it 'allows when prefixed url is not in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://prefixedexample.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'allows when subdomain url is in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday::Connection.new('http://subdomain.example.com') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url not in no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do + conn = Faraday::Connection.new('http://example.com') + expect(conn.proxy.host).to eq('proxy.com') + end + end + + it 'allows when ip address is not in no proxy list but url is' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do + conn = Faraday::Connection.new('http://127.0.0.1') + expect(conn.proxy).to be_nil + end + end + + it 'allows when url is not in no proxy list but ip address is' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do + conn = Faraday::Connection.new('http://localhost') + expect(conn.proxy).to be_nil + end + end + + it 'allows in multi element no proxy list' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do + expect(Faraday::Connection.new('http://example0.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example1.com').proxy).to be_nil + expect(Faraday::Connection.new('http://example2.com').proxy.host).to eq('proxy.com') + end + end + + it 'test proxy requires uri' do + expect { conn.proxy = { uri: :bad_uri, user: 'rick' } }.to raise_error(ArgumentError) + end + + it 'gives priority to manually set proxy' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do + conn = Faraday.new + conn.proxy = 'http://proxy2.com' + + expect(conn.instance_variable_get('@manual_proxy')).to be_truthy + expect(conn.proxy_for_request('https://google.co.uk').host).to eq('proxy2.com') + end + end + + context 'performing a request' do + before { stub_request(:get, 'http://example.com') } + + it 'dynamically checks proxy' do + with_env 'http_proxy' => 'http://proxy.com:80' do + conn = Faraday.new + conn.get('http://example.com') + expect(conn.instance_variable_get('@temp_proxy').host).to eq('proxy.com') + end + + conn.get('http://example.com') + expect(conn.instance_variable_get('@temp_proxy')).to be_nil + end + + it 'dynamically check no proxy' do + with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do + conn = Faraday.new + + expect(conn.instance_variable_get('@temp_proxy').host).to eq('proxy.com') + conn.get('http://example.com') + expect(conn.instance_variable_get('@temp_proxy')).to be_nil + end + end + end end end \ No newline at end of file diff --git a/spec/support/helper_methods.rb b/spec/support/helper_methods.rb index 67908b0dd..76319ea21 100644 --- a/spec/support/helper_methods.rb +++ b/spec/support/helper_methods.rb @@ -61,6 +61,16 @@ def with_env(new_env) end end + def with_env_proxy_disabled + Faraday.ignore_env_proxy = true + + begin + yield + ensure + Faraday.ignore_env_proxy = false + end + end + def capture_warnings old, $stderr = $stderr, StringIO.new begin diff --git a/test/connection_test.rb b/test/connection_test.rb index d74d69bfa..d75f2831e 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -49,16 +49,7 @@ def test_request_header_change_does_not_modify_connection_header assert !request.headers.include?('Authorization') end - def test_dynamic_proxy - with_test_conn do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - Faraday.get('http://google.co.uk') - assert_equal 'duncan.proxy.com', Faraday.default_connection.instance_variable_get('@temp_proxy').host - end - Faraday.get('http://google.co.uk') - assert_nil Faraday.default_connection.instance_variable_get('@temp_proxy') - end - end + def test_ignore_env_proxy with_env_proxy_disabled do @@ -69,85 +60,6 @@ def test_ignore_env_proxy end end - if URI.parse('').respond_to?(:find_proxy) - def test_proxy_allowed_when_url_in_no_proxy_list - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do - conn = Faraday::Connection.new('http://example.com') - assert_nil conn.proxy - end - end - - def test_proxy_allowed_when_prefixed_url_is_not_in_no_proxy_list - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do - conn = Faraday::Connection.new('http://prefixedexample.com') - assert_equal 'proxy.com', conn.proxy.host - end - end - - def test_proxy_allowed_when_subdomain_url_is_in_no_proxy_list - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example.com' do - conn = Faraday::Connection.new('http://subdomain.example.com') - assert_nil conn.proxy - end - end - - def test_proxy_allowed_when_url_not_in_no_proxy_list - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example2.com' do - conn = Faraday::Connection.new('http://example.com') - assert_equal 'proxy.com', conn.proxy.host - end - end - - def test_proxy_allowed_when_ip_address_is_not_in_no_proxy_list_but_url_is - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'localhost' do - conn = Faraday::Connection.new('http://127.0.0.1') - assert_nil conn.proxy - end - end - - def test_proxy_allowed_when_url_is_not_in_no_proxy_list_but_ip_address_is - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => '127.0.0.1' do - conn = Faraday::Connection.new('http://localhost') - assert_nil conn.proxy - end - end - - def test_proxy_allowed_in_multi_element_no_proxy_list - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'example0.com,example.com,example1.com' do - assert_nil Faraday::Connection.new('http://example0.com').proxy - assert_nil Faraday::Connection.new('http://example.com').proxy - assert_nil Faraday::Connection.new('http://example1.com').proxy - assert_equal 'proxy.com', Faraday::Connection.new('http://example2.com').proxy.host - end - end - - def test_dynamic_no_proxy - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do - conn = Faraday.new - - assert_equal 'proxy.com', conn.instance_variable_get('@temp_proxy').host - conn.get('https://google.co.uk') - assert_nil conn.instance_variable_get('@temp_proxy') - end - end - - def test_issue - with_env 'http_proxy' => 'http://proxy.com', 'no_proxy' => 'google.co.uk' do - conn = Faraday.new - conn.proxy = 'http://proxy2.com' - - assert_equal true, conn.instance_variable_get('@manual_proxy') - assert_equal 'proxy2.com', conn.proxy_for_request('https://google.co.uk').host - end - end - end - - def test_proxy_requires_uri - conn = Faraday::Connection.new - assert_raises ArgumentError do - conn.proxy = {:uri => :bad_uri, :user => 'rick'} - end - end def test_dups_connection_object conn = Faraday::Connection.new 'http://sushi.com/foo', From e5c7f83e7661b8b1c4bfe6006e72d3e5fe7a0917 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Thu, 8 Nov 2018 14:29:26 +0000 Subject: [PATCH 44/46] More connection tests converted into RSpec. --- spec/faraday/connection_spec.rb | 120 ++++++++++++++++++++++++++ test/connection_test.rb | 146 -------------------------------- 2 files changed, 120 insertions(+), 146 deletions(-) diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb index 62203c91a..de4cd6657 100644 --- a/spec/faraday/connection_spec.rb +++ b/spec/faraday/connection_spec.rb @@ -18,6 +18,41 @@ end end +shared_examples 'default connection options' do + after { Faraday.default_connection_options = nil } + + it 'works with implicit url' do + conn = Faraday.new 'http://sushi.com/foo' + expect(conn.options.timeout).to eq(10) + end + + it 'works with option url' do + conn = Faraday.new :url => 'http://sushi.com/foo' + expect(conn.options.timeout).to eq(10) + end + + it 'works with instance connection options' do + conn = Faraday.new 'http://sushi.com/foo', request: { open_timeout: 1 } + expect(conn.options.timeout).to eq(10) + expect(conn.options.open_timeout).to eq(1) + end + + it 'default connection options persist with an instance overriding' do + conn = Faraday.new 'http://nigiri.com/bar' + conn.options.timeout = 1 + expect(Faraday.default_connection_options.request.timeout).to eq(10) + + other = Faraday.new :url => 'https://sushi.com/foo' + other.options.timeout = 1 + + expect(Faraday.default_connection_options.request.timeout).to eq(10) + end + + it 'default connection uses default connection options' do + expect(Faraday.default_connection.options.timeout).to eq(10) + end +end + RSpec.describe Faraday::Connection do let(:conn) { Faraday::Connection.new(url, options) } let(:url) { nil } @@ -64,6 +99,30 @@ it { expect(subject.headers['User-agent']).to eq('Faraday') } end + + context 'with ssl false' do + let(:options) { { ssl: { verify: false } } } + + it { expect(subject.ssl.verify?).to be_falsey } + end + + context 'with empty block' do + let(:conn) { Faraday::Connection.new {} } + + it { expect(conn.builder.handlers.size).to eq(0) } + end + + context 'with block' do + let(:conn) do + Faraday::Connection.new(params: { 'a' => '1' }) do |faraday| + faraday.adapter :net_http + faraday.url_prefix = 'http://sushi.com/omnom' + end + end + + it { expect(conn.builder.handlers.size).to eq(0) } + it { expect(conn.path_prefix).to eq('/omnom') } + end end describe 'basic_auth' do @@ -387,6 +446,14 @@ end end + it 'ignores env proxy if set that way' do + with_env_proxy_disabled do + with_env 'http_proxy' => 'http://duncan.proxy.com:80' do + expect(conn.proxy).to be_nil + end + end + end + context 'performing a request' do before { stub_request(:get, 'http://example.com') } @@ -412,4 +479,57 @@ end end end + + describe '#dup' do + subject { conn.dup } + + let(:url) { 'http://sushi.com/foo' } + let(:options) do + { + ssl: { verify: :none }, + headers: { 'content-type' => 'text/plain' }, + params: { 'a' => '1' }, + request: { timeout: 5 } + } + end + + it { expect(subject.build_exclusive_url).to eq(conn.build_exclusive_url) } + it { expect(subject.headers['content-type']).to eq('text/plain') } + it { expect(subject.params['a']).to eq('1') } + + context 'after manual changes' do + before do + subject.basic_auth('', '') + subject.headers['content-length'] = 12 + subject.params['b'] = '2' + subject.options[:open_timeout] = 10 + end + + it { expect(subject.builder.handlers.size).to eq(1) } + it { expect(conn.builder.handlers.size).to eq(1) } + it { expect(conn.headers.key?('content-length')).to be_falsey } + it { expect(conn.params.key?('b')).to be_falsey } + it { expect(subject.options[:timeout]).to eq(5) } + it { expect(conn.options[:open_timeout]).to be_nil } + end + end + + describe '#respond_to?' do + it { expect(Faraday.respond_to?(:get)).to be_truthy } + it { expect(Faraday.respond_to?(:post)).to be_truthy } + end + + describe 'default_connection_options' do + context 'assigning a default value' do + before { Faraday.default_connection_options.request.timeout = 10 } + + it_behaves_like 'default connection options' + end + + context 'assigning a hash' do + before { Faraday.default_connection_options = { request: { timeout: 10 } } } + + it_behaves_like 'default connection options' + end + end end \ No newline at end of file diff --git a/test/connection_test.rb b/test/connection_test.rb index d75f2831e..764efc917 100644 --- a/test/connection_test.rb +++ b/test/connection_test.rb @@ -1,40 +1,6 @@ require File.expand_path('../helper', __FILE__) class TestConnection < Faraday::TestCase - def teardown - Faraday.default_connection_options = nil - end - - def with_test_conn - old_conn = Faraday.default_connection - Faraday.default_connection = Faraday::Connection.new do |builder| - builder.adapter :test do |stub| - stub.get('/') do |_| - [200, nil, nil] - end - end - end - - begin - yield - ensure - Faraday.default_connection = old_conn - end - end - - def with_env_proxy_disabled - Faraday.ignore_env_proxy = true - - begin - yield - ensure - Faraday.ignore_env_proxy = false - end - end - - - - def test_request_header_change_does_not_modify_connection_header connection = Faraday.new(:url => 'https://asushi.com/sake.html') connection.headers = {'Authorization' => 'token abc123'} @@ -50,118 +16,6 @@ def test_request_header_change_does_not_modify_connection_header end - - def test_ignore_env_proxy - with_env_proxy_disabled do - with_env 'http_proxy' => 'http://duncan.proxy.com:80' do - conn = Faraday::Connection.new(proxy: nil) - assert_nil conn.proxy - end - end - end - - - def test_dups_connection_object - conn = Faraday::Connection.new 'http://sushi.com/foo', - :ssl => { :verify => :none }, - :headers => {'content-type' => 'text/plain'}, - :params => {'a'=>'1'}, - :request => {:timeout => 5} - - other = conn.dup - - assert_equal conn.build_exclusive_url, other.build_exclusive_url - assert_equal 'text/plain', other.headers['content-type'] - assert_equal '1', other.params['a'] - - other.basic_auth('', '') - other.headers['content-length'] = 12 - other.params['b'] = '2' - other.options[:open_timeout] = 10 - - assert_equal 1, other.builder.handlers.size - assert_equal 1, conn.builder.handlers.size - assert !conn.headers.key?('content-length') - assert !conn.params.key?('b') - assert_equal 5, other.options[:timeout] - assert_nil conn.options[:open_timeout] - end - - def test_initialize_with_false_option - conn = Faraday::Connection.new :ssl => {:verify => false} - assert !conn.ssl.verify? - end - - def test_init_with_block - conn = Faraday::Connection.new { } - assert_equal 0, conn.builder.handlers.size - end - - def test_init_with_block_yields_connection - conn = Faraday::Connection.new(:params => {'a'=>'1'}) { |faraday| - faraday.adapter :net_http - faraday.url_prefix = 'http://sushi.com/omnom' - assert_equal '1', faraday.params['a'] - } - assert_equal 0, conn.builder.handlers.size - assert_equal '/omnom', conn.path_prefix - end - - def test_respond_to - assert Faraday.respond_to?(:get) - assert Faraday.respond_to?(:post) - end - - def test_default_connection_options - Faraday.default_connection_options.request.timeout = 10 - conn = Faraday.new 'http://sushi.com/foo' - assert_equal 10, conn.options.timeout - end - - def test_default_connection_options_without_url - Faraday.default_connection_options.request.timeout = 10 - conn = Faraday.new :url => 'http://sushi.com/foo' - assert_equal 10, conn.options.timeout - end - - def test_default_connection_options_as_hash - Faraday.default_connection_options = { request: { timeout: 10 } } - conn = Faraday.new 'http://sushi.com/foo' - assert_equal 10, conn.options.timeout - end - - def test_default_connection_options_as_hash_without_url - Faraday.default_connection_options = { request: { timeout: 10 } } - conn = Faraday.new :url => 'http://sushi.com/foo' - assert_equal 10, conn.options.timeout - end - - def test_default_connection_options_as_hash_with_instance_connection_options - Faraday.default_connection_options = { request: { timeout: 10 } } - conn = Faraday.new 'http://sushi.com/foo', request: { open_timeout: 1 } - assert_equal 1, conn.options.open_timeout - assert_equal 10, conn.options.timeout - end - - def test_default_connection_options_persist_with_an_instance_overriding - Faraday.default_connection_options.request.timeout = 10 - conn = Faraday.new 'http://nigiri.com/bar' - conn.options.timeout = 1 - assert_equal 10, Faraday.default_connection_options.request.timeout - - other = Faraday.new :url => 'https://sushi.com/foo' - other.options.timeout = 1 - - assert_equal 10, Faraday.default_connection_options.request.timeout - end - - def test_default_connection_uses_default_connection_options - Faraday.default_connection_options.request.timeout = 10 - default_conn = Faraday.default_connection - - assert_equal 10, default_conn.options.timeout - end - def env_url(url, params) conn = Faraday::Connection.new(url, :params => params) yield(conn) if block_given? From 8b1c857eacf0c6be8ec7264724e13e5b2ddd4b5f Mon Sep 17 00:00:00 2001 From: iMacTia Date: Fri, 9 Nov 2018 10:02:54 +0000 Subject: [PATCH 45/46] Complete connection tests convertion into RSpec. --- spec/faraday/connection_spec.rb | 114 ++++++++++++++++++++++- test/connection_test.rb | 158 -------------------------------- 2 files changed, 112 insertions(+), 160 deletions(-) delete mode 100644 test/connection_test.rb diff --git a/spec/faraday/connection_spec.rb b/spec/faraday/connection_spec.rb index de4cd6657..6415fb10c 100644 --- a/spec/faraday/connection_spec.rb +++ b/spec/faraday/connection_spec.rb @@ -56,7 +56,7 @@ RSpec.describe Faraday::Connection do let(:conn) { Faraday::Connection.new(url, options) } let(:url) { nil } - let(:options) { {} } + let(:options) { nil } describe '.new' do subject { conn } @@ -278,6 +278,21 @@ end end + describe '#build_request' do + let(:url) { 'https://asushi.com/sake.html' } + let(:request) { conn.build_request(:get) } + + before do + conn.headers = { 'Authorization' => 'token abc123' } + request.headers.delete('Authorization') + end + + it { expect(conn.headers.keys).to eq(['Authorization']) } + it { expect(conn.headers.include?('Authorization')).to be_truthy } + it { expect(request.headers.keys).to be_empty } + it { expect(request.headers.include?('Authorization')).to be_falsey } + end + describe '#to_env' do subject { conn.build_request(:get).to_env(conn).url } @@ -521,7 +536,10 @@ describe 'default_connection_options' do context 'assigning a default value' do - before { Faraday.default_connection_options.request.timeout = 10 } + before do + Faraday.default_connection_options = nil + Faraday.default_connection_options.request.timeout = 10 + end it_behaves_like 'default connection options' end @@ -532,4 +550,96 @@ it_behaves_like 'default connection options' end end + + describe 'request params' do + context 'with simple url' do + let(:url) { 'http://example.com' } + let!(:stubbed) { stub_request(:get, 'http://example.com?a=a&p=3') } + + after { expect(stubbed).to have_been_made.once } + + it 'test_overrides_request_params' do + conn.get('?p=2&a=a', p: 3) + end + + it 'test_overrides_request_params_block' do + conn.get('?p=1&a=a', p: 2) do |req| + req.params[:p] = 3 + end + end + + it 'test_overrides_request_params_block_url' do + conn.get(nil, p: 2) do |req| + req.url('?p=1&a=a', 'p' => 3) + end + end + end + + context 'with url and extra params' do + let(:url) { 'http://example.com?a=1&b=2' } + let(:options) { { params: { c: 3 } } } + + it 'merges connection and request params' do + stubbed = stub_request(:get, 'http://example.com?a=1&b=2&c=3&limit=5&page=1') + conn.get('?page=1', limit: 5) + expect(stubbed).to have_been_made.once + end + + it 'allows to override all params' do + stubbed = stub_request(:get, 'http://example.com?b=b') + conn.get('?p=1&a=a', p: 2) do |req| + expect(req.params[:a]).to eq('a') + expect(req.params['c']).to eq(3) + expect(req.params['p']).to eq(2) + req.params = { :b => 'b' } + expect(req.params['b']).to eq('b') + end + expect(stubbed).to have_been_made.once + end + + it 'allows to set params_encoder for single request' do + encoder = Object.new + def encoder.encode(params) + params.map { |k,v| "#{k.upcase}-#{v.to_s.upcase}" }.join(',') + end + stubbed = stub_request(:get, 'http://example.com/?A-1,B-2,C-3,FEELING-BLUE') + + conn.get('/', feeling: 'blue') do |req| + req.options.params_encoder = encoder + end + expect(stubbed).to have_been_made.once + end + end + + context 'with default params encoder' do + let!(:stubbed) { stub_request(:get, 'http://example.com?color%5B%5D=red&color%5B%5D=blue') } + after { expect(stubbed).to have_been_made.once } + + it 'supports array params in url' do + conn.get('http://example.com?color[]=red&color[]=blue') + end + + it 'supports array params in params' do + conn.get('http://example.com', { color: %w(red blue) }) + end + end + + context 'with flat params encoder' do + let(:options) { { request: { params_encoder: Faraday::FlatParamsEncoder } } } + let!(:stubbed) { stub_request(:get, 'http://example.com?color=blue') } + after { expect(stubbed).to have_been_made.once } + + it 'supports array params in params' do + conn.get('http://example.com', { color: %w(red blue) }) + end + + context 'with array param in url' do + let(:url) { 'http://example.com?color[]=red&color[]=blue' } + + it do + conn.get('/') + end + end + end + end end \ No newline at end of file diff --git a/test/connection_test.rb b/test/connection_test.rb deleted file mode 100644 index 764efc917..000000000 --- a/test/connection_test.rb +++ /dev/null @@ -1,158 +0,0 @@ -require File.expand_path('../helper', __FILE__) - -class TestConnection < Faraday::TestCase - def test_request_header_change_does_not_modify_connection_header - connection = Faraday.new(:url => 'https://asushi.com/sake.html') - connection.headers = {'Authorization' => 'token abc123'} - - request = connection.build_request(:get) - request.headers.delete('Authorization') - - assert_equal connection.headers.keys.sort, ['Authorization'] - assert connection.headers.include?('Authorization') - - assert_equal request.headers.keys.sort, [] - assert !request.headers.include?('Authorization') - end - - - def env_url(url, params) - conn = Faraday::Connection.new(url, :params => params) - yield(conn) if block_given? - req = conn.build_request(:get) - req.to_env(conn).url - end -end - -class TestRequestParams < Faraday::TestCase - def create_connection(*args) - @conn = Faraday::Connection.new(*args) do |conn| - yield(conn) if block_given? - class << conn.builder - undef app - def app() lambda { |env| env } end - end - end - end - - def assert_query_equal(expected, query) - assert_equal expected, query.split('&').sort - end - - def with_default_params_encoder(encoder) - old_encoder = Faraday::Utils.default_params_encoder - begin - Faraday::Utils.default_params_encoder = encoder - yield - ensure - Faraday::Utils.default_params_encoder = old_encoder - end - end - - def test_merges_connection_and_request_params - create_connection 'http://a.co/?token=abc', :params => {'format' => 'json'} - query = get '?page=1', :limit => 5 - assert_query_equal %w[format=json limit=5 page=1 token=abc], query - end - - def test_overrides_connection_params - create_connection 'http://a.co/?a=a&b=b&c=c', :params => {:a => 'A'} do |conn| - conn.params[:b] = 'B' - assert_equal 'c', conn.params[:c] - end - assert_query_equal %w[a=A b=B c=c], get - end - - def test_all_overrides_connection_params - create_connection 'http://a.co/?a=a', :params => {:c => 'c'} do |conn| - conn.params = {'b' => 'b'} - end - assert_query_equal %w[b=b], get - end - - def test_overrides_request_params - create_connection - query = get '?p=1&a=a', :p => 2 - assert_query_equal %w[a=a p=2], query - end - - def test_overrides_request_params_block - create_connection - query = get '?p=1&a=a', :p => 2 do |req| - req.params[:p] = 3 - end - assert_query_equal %w[a=a p=3], query - end - - def test_overrides_request_params_block_url - create_connection - query = get nil, :p => 2 do |req| - req.url '?p=1&a=a', 'p' => 3 - end - assert_query_equal %w[a=a p=3], query - end - - def test_overrides_all_request_params - create_connection :params => {:c => 'c'} - query = get '?p=1&a=a', :p => 2 do |req| - assert_equal 'a', req.params[:a] - assert_equal 'c', req.params['c'] - assert_equal 2, req.params['p'] - req.params = {:b => 'b'} - assert_equal 'b', req.params['b'] - end - assert_query_equal %w[b=b], query - end - - def test_array_params_in_url - with_default_params_encoder(nil) do - create_connection 'http://a.co/page1?color[]=red&color[]=blue' - query = get - assert_equal 'color%5B%5D=red&color%5B%5D=blue', query - end - end - - def test_array_params_in_params - with_default_params_encoder(nil) do - create_connection 'http://a.co/page1', :params => {:color => ['red', 'blue']} - query = get - assert_equal 'color%5B%5D=red&color%5B%5D=blue', query - end - end - - def test_array_params_in_url_with_flat_params - with_default_params_encoder(Faraday::FlatParamsEncoder) do - create_connection 'http://a.co/page1?color=red&color=blue' - query = get - assert_equal 'color=red&color=blue', query - end - end - - def test_array_params_in_params_with_flat_params - with_default_params_encoder(Faraday::FlatParamsEncoder) do - create_connection 'http://a.co/page1', :params => {:color => ['red', 'blue']} - query = get - assert_equal 'color=red&color=blue', query - end - end - - def test_params_with_connection_options - encoder = Object.new - def encoder.encode(params) - params.map { |k,v| "#{k.upcase}-#{v.upcase}" }.join(',') - end - - create_connection :params => {:color => 'red'} - query = get('', :feeling => 'blue') do |req| - req.options.params_encoder = encoder - end - assert_equal ['COLOR-RED', 'FEELING-BLUE'], query.split(',').sort - end - - def get(*args) - env = @conn.get(*args) do |req| - yield(req) if block_given? - end - env[:url].query - end -end From 4325812048ee97a68f9c800b203b6e06a8388b95 Mon Sep 17 00:00:00 2001 From: iMacTia Date: Mon, 26 Nov 2018 16:10:03 +0000 Subject: [PATCH 46/46] Converts new Net::HTTP::Persistent pool_size tests to Rspec. --- spec/faraday/adapter/net_http_persistent_spec.rb | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/spec/faraday/adapter/net_http_persistent_spec.rb b/spec/faraday/adapter/net_http_persistent_spec.rb index 826142586..6a702185d 100644 --- a/spec/faraday/adapter/net_http_persistent_spec.rb +++ b/spec/faraday/adapter/net_http_persistent_spec.rb @@ -4,7 +4,7 @@ it_behaves_like 'an adapter' it 'allows to provide adapter specific configs' do - url = URI('https://example.com:1234') + url = URI('https://example.com') adapter = Faraday::Adapter::NetHttpPersistent.new do |http| http.idle_timeout = 123 @@ -27,4 +27,15 @@ # `max_retries=` is only present in Ruby 2.5 expect(http.max_retries).to eq(0) if http.respond_to?(:max_retries=) end + + it 'allows to set pool_size on initialize' do + url = URI('https://example.com') + + adapter = Faraday::Adapter::NetHttpPersistent.new(nil, { pool_size: 5 }) + + http = adapter.send(:net_http_connection, url: url, request: {}) + + # `pool` is only present in net_http_persistent >= 3.0 + expect(http.pool.size).to eq(5) if http.respond_to?(:pool) + end end