spec/acceptance/realtime/connection_failures_spec.rb in ably-0.8.13 vs spec/acceptance/realtime/connection_failures_spec.rb in ably-0.8.14
- old
+ new
@@ -60,11 +60,11 @@
let(:client_failure_options) do
default_options.merge(
log_level: :none,
disconnected_retry_timeout: retry_every_for_tests,
suspended_retry_timeout: retry_every_for_tests,
- connection_state_ttl: max_time_in_state_for_tests
+ max_connection_state_ttl: max_time_in_state_for_tests
)
end
# retry immediately after failure, then one retry every :retry_every_for_tests
let(:expected_retry_attempts) { 1 + (max_time_in_state_for_tests / retry_every_for_tests).round }
@@ -167,11 +167,11 @@
end
end
context 'for the first time' do
let(:client_options) do
- default_options.merge(suspended_retry_timeout: 2, connection_state_ttl: 0, log_level: :error)
+ default_options.merge(suspended_retry_timeout: 2, max_connection_state_ttl: 0, log_level: :error)
end
it 'waits suspended_retry_timeout before attempting to reconnect' do
expect(client.connection.defaults[:suspended_retry_timeout]).to eql(2)
connection.once(:connected) do
@@ -304,11 +304,11 @@
let(:client_options) do
default_options.merge(
log_level: :error,
disconnected_retry_timeout: 0.1,
suspended_retry_timeout: 0.1,
- connection_state_ttl: 0.2,
+ max_connection_state_ttl: 0.2,
realtime_host: 'non.existent.host'
)
end
it 'never calls the provided success block', em_timeout: 10 do
@@ -368,11 +368,11 @@
let(:client_options) do
default_options.merge(
log_level: :none,
disconnected_retry_timeout: retry_every,
suspended_retry_timeout: retry_every,
- connection_state_ttl: 60
+ max_connection_state_ttl: 60
)
end
it "retries every #{Ably::Realtime::Connection::DEFAULTS.fetch(:disconnected_retry_timeout)} seconds" do
fail_if_suspended_or_failed
@@ -414,11 +414,11 @@
end
end
end
end
- context 'when websocket transport is closed' do
+ context 'when websocket transport is abruptly disconnected' do
it 'reconnects automatically' do
fail_if_suspended_or_failed
connection.once(:connected) do
connection.once(:disconnected) do
@@ -429,10 +429,33 @@
end
end
connection.transport.close_connection_after_writing
end
end
+
+ context 'hosts used' do
+ it 'reconnects with the default host' do
+ fail_if_suspended_or_failed
+
+ connection.once(:connected) do
+ connection.once(:disconnected) do
+ hosts = []
+ expect(connection).to receive(:create_transport).once.and_wrap_original do |original_method, *args, &block|
+ hosts << args[0]
+ original_method.call(*args, &block)
+ end
+ connection.once(:connected) do
+ host = "#{"#{environment}-" if environment && environment.to_s != 'production'}#{Ably::Realtime::Client::DOMAIN}"
+ expect(hosts.first).to eql(host)
+ expect(hosts.length).to eql(1)
+ stop_reactor
+ end
+ end
+ connection.transport.close_connection_after_writing
+ end
+ end
+ end
end
context 'after successfully reconnecting and resuming' do
it 'retains connection_id and updates the connection_key' do
connection.once(:connected) do
@@ -597,10 +620,36 @@
expect(channel.error_reason).to eql(error)
stop_reactor
end
end
end
+
+ context 'as the DISCONNECTED window to resume has passed' do
+ let(:channel) { client.channel(random_str) }
+
+ def kill_connection_transport_and_prevent_valid_resume
+ connection.transport.close_connection_after_writing
+ end
+
+ it 'starts a new connection automatically and does not try and resume' do
+ connection.once(:connected) do
+ previous_connection_id = connection.id
+ previous_connection_key = connection.key
+
+ five_minutes_time = Time.now + 5 * 60
+ allow(Time).to receive(:now) { five_minutes_time }
+
+ connection.once(:connected) do
+ expect(connection.key).to_not eql(previous_connection_key)
+ expect(connection.id).to_not eql(previous_connection_id)
+ stop_reactor
+ end
+
+ kill_connection_transport_and_prevent_valid_resume
+ end
+ end
+ end
end
end
describe 'fallback host feature' do
let(:retry_every_for_tests) { 0.2 }
@@ -610,11 +659,11 @@
default_options.merge(
environment: :production,
log_level: :none,
disconnected_retry_timeout: retry_every_for_tests,
suspended_retry_timeout: retry_every_for_tests,
- connection_state_ttl: max_time_in_state_for_tests
+ max_connection_state_ttl: max_time_in_state_for_tests
)
end
# Retry immediately and then wait retry_every before every subsequent attempt
let(:expected_retry_attempts) { 1 + (max_time_in_state_for_tests / retry_every_for_tests).round }
@@ -625,11 +674,11 @@
context 'with custom realtime websocket host option' do
let(:expected_host) { 'this.host.does.not.exist' }
let(:client_options) { timeout_options.merge(realtime_host: expected_host) }
it 'never uses a fallback host' do
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
+ expect(connection).to receive(:create_transport).exactly(retry_count_for_all_states).times do |host|
expect(host).to eql(expected_host)
raise EventMachine::ConnectionError
end
connection.once(:suspended) do
@@ -643,11 +692,11 @@
context 'with custom realtime websocket port option' do
let(:custom_port) { 666}
let(:client_options) { timeout_options.merge(tls_port: custom_port) }
it 'never uses a fallback host' do
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host, port|
+ expect(connection).to receive(:create_transport).exactly(retry_count_for_all_states).times do |host, port|
expect(port).to eql(custom_port)
raise EventMachine::ConnectionError
end
connection.once(:suspended) do
@@ -661,22 +710,71 @@
context 'with non-production environment' do
let(:environment) { 'sandbox' }
let(:expected_host) { "#{environment}-#{Ably::Realtime::Client::DOMAIN}" }
let(:client_options) { timeout_options.merge(environment: environment) }
- it 'never uses a fallback host' do
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
+ it 'does not use a fallback host by default' do
+ expect(connection).to receive(:create_transport).exactly(retry_count_for_all_states).times do |host|
expect(host).to eql(expected_host)
raise EventMachine::ConnectionError
end
connection.once(:suspended) do
connection.once(:suspended) do
stop_reactor
end
end
end
+
+ context ':fallback_hosts_use_default is true' do
+ let(:max_time_in_state_for_tests) { 4 }
+ let(:fallback_hosts_used) { Array.new }
+ let(:client_options) { timeout_options.merge(environment: environment, fallback_hosts_use_default: true) }
+
+ it 'uses a fallback host on every subsequent disconnected attempt until suspended (#RTN17b, #TO3k7)' do
+ request = 0
+ allow(connection).to receive(:create_transport) do |host|
+ if request == 0
+ expect(host).to eql(expected_host)
+ else
+ fallback_hosts_used << host
+ end
+ request += 1
+ raise EventMachine::ConnectionError
+ end
+
+ connection.once(:suspended) do
+ expect(fallback_hosts_used.uniq).to match_array(Ably::FALLBACK_HOSTS + [expected_host])
+ stop_reactor
+ end
+ end
+ end
+
+ context ':fallback_hosts array is provided' do
+ let(:max_time_in_state_for_tests) { 4 }
+ let(:fallback_hosts) { %w(a.foo.com b.foo.com) }
+ let(:fallback_hosts_used) { Array.new }
+ let(:client_options) { timeout_options.merge(environment: environment, fallback_hosts: fallback_hosts) }
+
+ it 'uses a fallback host on every subsequent disconnected attempt until suspended (#RTN17b, #TO3k6)' do
+ request = 0
+ allow(connection).to receive(:create_transport) do |host|
+ if request == 0
+ expect(host).to eql(expected_host)
+ else
+ fallback_hosts_used << host
+ end
+ request += 1
+ raise EventMachine::ConnectionError
+ end
+
+ connection.once(:suspended) do
+ expect(fallback_hosts_used.uniq).to match_array(fallback_hosts + [expected_host])
+ stop_reactor
+ end
+ end
+ end
end
context 'with production environment' do
let(:custom_hosts) { %w(A.ably-realtime.com B.ably-realtime.com) }
before do
@@ -692,11 +790,11 @@
before do
allow(connection).to receive(:internet_up?).and_yield(false)
end
it 'never uses a fallback host' do
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
+ expect(connection).to receive(:create_transport).exactly(retry_count_for_all_states).times do |host|
expect(host).to eql(expected_host)
raise EventMachine::ConnectionError
end
connection.once(:suspended) do
@@ -711,46 +809,95 @@
before do
allow(connection).to receive(:internet_up?).and_yield(true)
@suspended = 0
end
- it 'uses a fallback host on every subsequent disconnected attempt until suspended' do
- request = 0
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_one_state).times do |host|
- if request == 0
- expect(host).to eql(expected_host)
- else
- fallback_hosts_used << host
+ context 'and default options' do
+ let(:max_time_in_state_for_tests) { 2 } # allow time for 3 attempts, 2 configured fallbacks + primary host
+
+ it 'uses a fallback host + the original host once on every subsequent disconnected attempt until suspended' do
+ request = 0
+ expect(connection).to receive(:create_transport).exactly(retry_count_for_one_state).times do |host|
+ if request == 0
+ expect(host).to eql(expected_host)
+ else
+ fallback_hosts_used << host
+ end
+ request += 1
+ raise EventMachine::ConnectionError
end
- request += 1
- raise EventMachine::ConnectionError
+
+ connection.once(:suspended) do
+ fallback_hosts_used.pop # remove suspended attempt host
+ expect(fallback_hosts_used.uniq).to match_array(custom_hosts + [expected_host])
+ stop_reactor
+ end
end
- connection.once(:suspended) do
- fallback_hosts_used.pop # remove suspended attempt host
- expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
- stop_reactor
+ it 'uses the primary host when suspended, and then every fallback host and the primary host again on every subsequent suspended attempt' do
+ request = 0
+ expect(connection).to receive(:create_transport).at_least(:once) do |host|
+ if request == 0 || request == expected_retry_attempts + 1
+ expect(host).to eql(expected_host)
+ else
+ expect(custom_hosts + [expected_host]).to include(host)
+ fallback_hosts_used << host if @suspended > 0
+ end
+ request += 1
+ raise EventMachine::ConnectionError
+ end
+
+ connection.on(:suspended) do
+ @suspended += 1
+
+ if @suspended > 4
+ expect(fallback_hosts_used.uniq).to match_array(custom_hosts + [expected_host])
+ stop_reactor
+ end
+ end
end
end
- it 'uses the primary host when suspended, and a fallback host on every subsequent suspended attempt' do
- request = 0
- expect(EventMachine).to receive(:connect).at_least(:once) do |host|
- if request == 0 || request == expected_retry_attempts + 1
- expect(host).to eql(expected_host)
- else
- expect(custom_hosts).to include(host)
- fallback_hosts_used << host if @suspended > 0
+ context ':fallback_hosts array is provided by an empty array' do
+ let(:max_time_in_state_for_tests) { 3 }
+ let(:fallback_hosts) { [] }
+ let(:hosts_used) { Array.new }
+ let(:client_options) { timeout_options.merge(environment: 'production', fallback_hosts: fallback_hosts) }
+
+ it 'uses a fallback host on every subsequent disconnected attempt until suspended (#RTN17b, #TO3k6)' do
+ allow(connection).to receive(:create_transport) do |host|
+ hosts_used << host
+ raise EventMachine::ConnectionError
end
- request += 1
- raise EventMachine::ConnectionError
+
+ connection.once(:suspended) do
+ expect(hosts_used.uniq.length).to eql(1)
+ expect(hosts_used.uniq.first).to eql(expected_host)
+ stop_reactor
+ end
end
+ end
- connection.on(:suspended) do
- @suspended += 1
+ context ':fallback_hosts array is provided' do
+ let(:max_time_in_state_for_tests) { 3 }
+ let(:fallback_hosts) { %w(a.foo.com b.foo.com) }
+ let(:fallback_hosts_used) { Array.new }
+ let(:client_options) { timeout_options.merge(environment: 'production', fallback_hosts: fallback_hosts) }
- if @suspended > 3
- expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
+ it 'uses a fallback host on every subsequent disconnected attempt until suspended (#RTN17b, #TO3k6)' do
+ request = 0
+ allow(connection).to receive(:create_transport) do |host|
+ if request == 0
+ expect(host).to eql(expected_host)
+ else
+ fallback_hosts_used << host
+ end
+ request += 1
+ raise EventMachine::ConnectionError
+ end
+
+ connection.once(:suspended) do
+ expect(fallback_hosts_used.uniq).to match_array(fallback_hosts + [expected_host])
stop_reactor
end
end
end
end