lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb in ably-rest-0.8.13 vs lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb in ably-rest-0.8.14
- old
+ new
@@ -1,7 +1,8 @@
# encoding: utf-8
require 'spec_helper'
+require 'webrick'
describe Ably::Rest::Client do
vary_by_protocol do
let(:default_options) { { environment: environment, protocol: protocol } }
let(:client_options) { default_options }
@@ -212,11 +213,11 @@
end
end
context 'connection transport' do
context 'defaults' do
- let(:client_options) { default_options.merge(key: api_key) }
+ let(:client_options) { default_options.merge(key: api_key, environment: 'production') }
context 'for default host' do
it "is configured to timeout connection opening in #{http_defaults.fetch(:open_timeout)} seconds" do
expect(client.connection.options.open_timeout).to eql(http_defaults.fetch(:open_timeout))
end
@@ -238,11 +239,11 @@
end
context 'with custom http_open_timeout and http_request_timeout options' do
let(:http_open_timeout) { 999 }
let(:http_request_timeout) { 666 }
- let(:client_options) { default_options.merge(key: api_key, http_open_timeout: http_open_timeout, http_request_timeout: http_request_timeout) }
+ let(:client_options) { default_options.merge(key: api_key, http_open_timeout: http_open_timeout, http_request_timeout: http_request_timeout, environment: 'production') }
context 'for default host' do
it 'is configured to use custom open timeout' do
expect(client.connection.options.open_timeout).to eql(http_open_timeout)
end
@@ -267,11 +268,11 @@
context 'fallback hosts', :webmock do
let(:path) { '/channels/test/publish' }
let(:publish_block) { proc { client.channel('test').publish('event', 'data') } }
context 'configured' do
- let(:client_options) { default_options.merge(key: api_key) }
+ let(:client_options) { default_options.merge(key: api_key, environment: 'production') }
it 'should make connection attempts to A.ably-realtime.com, B.ably-realtime.com, C.ably-realtime.com, D.ably-realtime.com, E.ably-realtime.com' do
hosts = []
5.times do
hosts << client.fallback_connection.host
@@ -409,10 +410,283 @@
expect(first_fallback_request_stub).to have_been_requested
expect(second_fallback_request_stub).to have_been_requested
end
end
end
+
+ context 'when environment is production and server returns a 50x error' do
+ let(:custom_hosts) { %w(A.foo.com B.foo.com) }
+ let(:max_retry_count) { 2 }
+ let(:max_retry_duration) { 0.5 }
+ let(:fallback_block) { Proc.new { raise Faraday::SSLError.new('ssl error message') } }
+ let(:production_options) do
+ default_options.merge(
+ environment: nil,
+ key: api_key,
+ http_max_retry_duration: max_retry_duration,
+ http_max_retry_count: max_retry_count
+ )
+ end
+
+ let(:status) { 502 }
+ let(:fallback_block) do
+ Proc.new do
+ {
+ headers: { 'Content-Type' => 'text/html' },
+ status: status
+ }
+ end
+ end
+ let!(:default_host_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{Ably::Rest::Client::DOMAIN}#{path}").to_return(&fallback_block)
+ end
+
+ context 'with custom fallback hosts provided' do
+ let!(:first_fallback_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{custom_hosts[0]}#{path}").to_return(&fallback_block)
+ end
+
+ let!(:second_fallback_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{custom_hosts[1]}#{path}").to_return(&fallback_block)
+ end
+
+ let(:client_options) {
+ production_options.merge(fallback_hosts: custom_hosts)
+ }
+
+ it 'attempts the fallback hosts as this is an authentication failure (#RSC15b, #TO3k6)' do
+ expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
+ expect(default_host_request_stub).to have_been_requested
+ expect(first_fallback_request_stub).to have_been_requested
+ expect(second_fallback_request_stub).to have_been_requested
+ end
+ end
+
+ context 'with an empty array of fallback hosts provided (#RSC15b, #TO3k6)' do
+ let(:client_options) {
+ production_options.merge(fallback_hosts: [])
+ }
+
+ it 'does not attempt the fallback hosts as this is an authentication failure' do
+ expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
+ expect(default_host_request_stub).to have_been_requested
+ end
+ end
+
+ context 'using a local web-server', webmock: false do
+ let(:primary_host) { 'local-rest.ably.io' }
+ let(:fallbacks) { ['local.ably.io', 'localhost'] }
+ let(:port) { rand(10000) + 2000 }
+ let(:channel_name) { 'foo' }
+ let(:request_timeout) { 3 }
+
+ after do
+ @web_server.shutdown
+ end
+
+ context 'and timing out the primary host' do
+ before do
+ @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false)
+ @web_server.mount_proc "/channels/#{channel_name}/publish" do |req, res|
+ if req.header["host"].first.include?(primary_host)
+ @primary_host_requested = true
+ sleep request_timeout + 0.5
+ else
+ @fallback_request_count ||= 0
+ @fallback_request_count += 1
+ if @fallback_request_count <= fail_fallback_request_count
+ sleep request_timeout + 0.5
+ else
+ res.status = 200
+ res['Content-Type'] = 'application/json'
+ res.body = '{}'
+ end
+ end
+ end
+ Thread.new do
+ @web_server.start
+ end
+ end
+
+ context 'with request timeout less than max_retry_duration' do
+ let(:client_options) do
+ default_options.merge(
+ rest_host: primary_host,
+ fallback_hosts: fallbacks,
+ token: 'fake.token',
+ port: port,
+ tls: false,
+ http_request_timeout: request_timeout,
+ max_retry_duration: request_timeout * 3
+ )
+ end
+ let(:fail_fallback_request_count) { 1 }
+
+ it 'tries one of the fallback hosts' do
+ client.channel(channel_name).publish('event', 'data')
+ expect(@primary_host_requested).to be_truthy
+ expect(@fallback_request_count).to eql(2)
+ end
+ end
+
+ context 'with request timeout less than max_retry_duration' do
+ let(:client_options) do
+ default_options.merge(
+ rest_host: primary_host,
+ fallback_hosts: fallbacks,
+ token: 'fake.token',
+ port: port,
+ tls: false,
+ http_request_timeout: request_timeout,
+ max_retry_duration: request_timeout / 2
+ )
+ end
+ let(:fail_fallback_request_count) { 0 }
+
+ it 'tries one of the fallback hosts' do
+ client.channel(channel_name).publish('event', 'data')
+ expect(@primary_host_requested).to be_truthy
+ expect(@fallback_request_count).to eql(1)
+ end
+ end
+ end
+
+ context 'and failing the primary host' do
+ before do
+ @web_server = WEBrick::HTTPServer.new(:Port => port, :SSLEnable => false)
+ @web_server.mount_proc "/channels/#{channel_name}/publish" do |req, res|
+ if req.header["host"].first.include?(primary_host)
+ @primary_host_requested = true
+ res.status = 500
+ else
+ @fallback_request_count ||= 0
+ @fallback_request_count += 1
+ if @fallback_request_count <= fail_fallback_request_count
+ res.status = 500
+ else
+ res.status = 200
+ res['Content-Type'] = 'application/json'
+ res.body = '{}'
+ end
+ end
+ end
+ Thread.new do
+ @web_server.start
+ end
+ end
+
+ let(:client_options) do
+ default_options.merge(
+ rest_host: primary_host,
+ fallback_hosts: fallbacks,
+ token: 'fake.token',
+ port: port,
+ tls: false
+ )
+ end
+ let(:fail_fallback_request_count) { 1 }
+
+ it 'tries one of the fallback hosts' do
+ client.channel(channel_name).publish('event', 'data')
+ expect(@primary_host_requested).to be_truthy
+ expect(@fallback_request_count).to eql(2)
+ end
+ end
+ end
+ end
+
+ context 'when environment is not production and server returns a 50x error' do
+ let(:custom_hosts) { %w(A.foo.com B.foo.com) }
+ let(:max_retry_count) { 2 }
+ let(:max_retry_duration) { 0.5 }
+ let(:fallback_block) { Proc.new { raise Faraday::SSLError.new('ssl error message') } }
+ let(:env) { 'custom-env' }
+ let(:production_options) do
+ default_options.merge(
+ environment: env,
+ key: api_key,
+ http_max_retry_duration: max_retry_duration,
+ http_max_retry_count: max_retry_count
+ )
+ end
+
+ let(:status) { 502 }
+ let(:fallback_block) do
+ Proc.new do
+ {
+ headers: { 'Content-Type' => 'text/html' },
+ status: status
+ }
+ end
+ end
+ let!(:default_host_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{env}-#{Ably::Rest::Client::DOMAIN}#{path}").to_return(&fallback_block)
+ end
+
+ context 'with custom fallback hosts provided (#RSC15b, #TO3k6)' do
+ let!(:first_fallback_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{custom_hosts[0]}#{path}").to_return(&fallback_block)
+ end
+
+ let!(:second_fallback_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{custom_hosts[1]}#{path}").to_return(&fallback_block)
+ end
+
+ let(:client_options) {
+ production_options.merge(fallback_hosts: custom_hosts)
+ }
+
+ it 'attempts the fallback hosts as this is an authentication failure' do
+ expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
+ expect(default_host_request_stub).to have_been_requested
+ expect(first_fallback_request_stub).to have_been_requested
+ expect(second_fallback_request_stub).to have_been_requested
+ end
+ end
+
+ context 'with an empty array of fallback hosts provided (#RSC15b, #TO3k6)' do
+ let(:client_options) {
+ production_options.merge(fallback_hosts: [])
+ }
+
+ it 'does not attempt the fallback hosts as this is an authentication failure' do
+ expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
+ expect(default_host_request_stub).to have_been_requested
+ end
+ end
+
+ context 'with fallback_hosts_use_default: true (#RSC15b, #TO3k7)' do
+ let(:custom_hosts) { Ably::FALLBACK_HOSTS[0...2] }
+
+ before do
+ stub_const 'Ably::FALLBACK_HOSTS', custom_hosts
+ end
+
+ let(:client_options) {
+ production_options.merge(fallback_hosts_use_default: true)
+ }
+
+ let!(:first_fallback_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{Ably::FALLBACK_HOSTS[0]}#{path}").to_return(&fallback_block)
+ end
+
+ let!(:second_fallback_request_stub) do
+ stub_request(:post, "https://#{api_key}@#{Ably::FALLBACK_HOSTS[1]}#{path}").to_return(&fallback_block)
+ end
+
+ let(:client_options) {
+ production_options.merge(fallback_hosts: custom_hosts)
+ }
+
+ it 'attempts the default fallback hosts as this is an authentication failure' do
+ expect { publish_block.call }.to raise_error(Ably::Exceptions::ServerError)
+ expect(default_host_request_stub).to have_been_requested
+ expect(first_fallback_request_stub).to have_been_requested
+ expect(second_fallback_request_stub).to have_been_requested
+ end
+ end
+ end
end
context 'with a custom host' do
let(:custom_host) { 'host.does.not.exist' }
let(:client_options) { default_options.merge(key: api_key, rest_host: custom_host) }
@@ -545,22 +819,41 @@
expect(client.auth.options[:auth_url]).to eql(dummy_auth_url)
end
end
context 'version headers', :webmock do
- let(:client_options) { default_options.merge(key: api_key) }
- let!(:publish_message_stub) do
- stub_request(:post, "#{client.endpoint.to_s.gsub('://', "://#{api_key}@")}/channels/foo/publish").
- with(headers: {
- 'X-Ably-Version' => Ably::PROTOCOL_VERSION,
- 'X-Ably-Lib' => "ruby-#{Ably::VERSION}"
- }).
- to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
- end
+ [nil, 'foo'].each do |variant|
+ context "with variant #{variant ? variant : 'none'}" do
+ if variant
+ before do
+ Ably.lib_variant = variant
+ end
- it 'sends a protocol version and lib version header' do
- client.channels.get('foo').publish("event")
- expect(publish_message_stub).to have_been_requested
+ after do
+ Ably.lib_variant = nil
+ end
+ end
+
+ let(:client_options) { default_options.merge(key: api_key) }
+ let!(:publish_message_stub) do
+ lib = ['ruby']
+ lib << variant if variant
+ lib << Ably::VERSION
+
+
+ stub_request(:post, "#{client.endpoint.to_s.gsub('://', "://#{api_key}@")}/channels/foo/publish").
+ with(headers: {
+ 'X-Ably-Version' => Ably::PROTOCOL_VERSION,
+ 'X-Ably-Lib' => lib.join('-')
+ }).
+ to_return(status: 201, body: '{}', headers: { 'Content-Type' => 'application/json' })
+ end
+
+ it 'sends a protocol version and lib version header' do
+ client.channels.get('foo').publish("event")
+ expect(publish_message_stub).to have_been_requested
+ end
+ end
end
end
end
end