spec/acceptance/realtime/connection_spec.rb in ably-1.1.7 vs spec/acceptance/realtime/connection_spec.rb in ably-1.1.8
- old
+ new
@@ -480,10 +480,122 @@
expect(been_disconnected).to be_truthy
stop_reactor
end
end
+ context 'when explicitly reconnecting disconnected/suspended connection in retry (#RTN11c)' do
+ let(:close_connection_proc) do
+ lambda do
+ EventMachine.add_timer(0.001) do
+ if connection.transport.nil?
+ close_connection_proc.call
+ else
+ connection.transport.close_connection_after_writing
+ end
+ end
+ end
+ end
+
+ context 'when suspended' do
+ let(:suspended_retry_timeout) { 60 }
+ let(:client_options) do
+ default_options.merge(
+ disconnected_retry_timeout: 0.02,
+ suspended_retry_timeout: suspended_retry_timeout,
+ connection_state_ttl: 0
+ )
+ end
+
+ it 'reconnects immediately' do
+ connection.once(:connecting) { close_connection_proc.call }
+
+ connection.on(:suspended) do |connection_state_change|
+ if connection_state_change.retry_in.zero?
+ # Exhausting immediate retries
+ connection.once(:connecting) { close_connection_proc.call }
+ else
+ suspended_at = Time.now.to_f
+ connection.on(:connected) do
+ expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
+ expect(connection.state).to eq(:connected)
+ expect(Time.now.to_f).to be_within(4).of(suspended_at)
+ stop_reactor
+ end
+ end
+
+ connection.connect
+ end
+
+ connection.connect
+ end
+ end
+
+ context 'when disconnected' do
+ let(:retry_timeout) { 60 }
+ let(:client_options) do
+ default_options.merge(
+ disconnected_retry_timeout: retry_timeout,
+ suspended_retry_timeout: retry_timeout,
+ connection_state_ttl: 0
+ )
+ end
+
+ it 'reconnects immediately' do
+ connection.once(:connected) do
+ connection.on(:disconnected) do |connection_state_change|
+ disconnected_at = Time.now.to_f
+ connection.on(:connected) do
+ if connection_state_change.retry_in.zero?
+ # Exhausting immediate retries
+ close_connection_proc.call
+ else
+ expect(connection_state_change.retry_in).to eq(retry_timeout)
+ expect(connection.state).to eq(:connected)
+ expect(Time.now.to_f).to be_within(4).of(disconnected_at)
+ stop_reactor
+ end
+ end
+ connection.connect
+ end
+
+ close_connection_proc.call
+ end
+
+ connection.connect
+ end
+ end
+ end
+
+ context 'when reconnecting a failed connection' do
+ let(:channel) { client.channel(random_str) }
+ let(:client_options) { default_options.merge(log_level: :none) }
+
+ it 'transitions all channels state to initialized and cleares error_reason' do
+ connection.on(:failed) do |connection_state_change|
+ expect(connection.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
+ expect(channel.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
+ expect(channel).to be_failed
+
+ connection.on(:connected) do
+ expect(connection.error_reason).to eq(nil)
+ expect(channel).to be_initialized
+ expect(channel.error_reason).to eq(nil)
+ stop_reactor
+ end
+
+ connection.connect
+ end
+
+ connection.connect do
+ channel.attach do
+ error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
+ client.connection.manager.error_received_from_server error
+ end
+ end
+ end
+ end
+
context 'with invalid auth details' do
let(:client_options) { default_options.merge(key: 'this.is:invalid', log_level: :none) }
it 'calls the Deferrable errback only once on connection failure' do
errback_called = false
@@ -691,10 +803,22 @@
end
end
end
context '#close' do
+ let(:close_connection_proc) do
+ lambda do
+ EventMachine.add_timer(0.001) do
+ if connection.transport.nil?
+ close_connection_proc.call
+ else
+ connection.transport.close_connection_after_writing
+ end
+ end
+ end
+ end
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
connection.connect do
expect(connection.close).to be_a(Ably::Util::SafeDeferrable)
stop_reactor
end
@@ -752,10 +876,60 @@
log_connection_changes
connection.close
end
end
+ context ':connecting RTN12f' do
+ context ":connected does not arrive when trying to close" do
+ it 'moves to closed' do
+ connection.on(:closed) do |state_change|
+ state_changes = connection.state_history.map { |t| t[:state].to_sym }
+
+ expect(state_changes).to eq([:connecting, :closing, :closed])
+ stop_reactor
+ end
+
+ connection.on(:connecting) do
+ connection.close
+ connection.__outgoing_protocol_msgbus__.unsubscribe
+ end
+
+ connection.connect
+ end
+ end
+
+ context ":connected arrive when trying to close" do
+ let(:protocol_message_attributes) do
+ {
+ action: Ably::Models::ProtocolMessage::ACTION.Connected.to_i,
+ connection_serial: 55,
+ connection_details: {
+ max_idle_interval: 2 * 1000
+ }
+ }
+ end
+
+ it 'moves to connected and then to closed' do
+ connection.on(:closed) do |state_change|
+ state_changes = connection.state_history.map { |t| t[:state].to_sym }
+
+ expect(state_changes).to eq([:connecting, :connected, :closing, :closed])
+ stop_reactor
+ end
+
+ connection.on(:connecting) do
+ connection.__outgoing_protocol_msgbus__.unsubscribe
+
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, Ably::Models::ProtocolMessage.new(protocol_message_attributes)
+ connection.close
+ end
+
+ connection.connect
+ end
+ end
+ end
+
context ':connected' do
it 'changes the connection state to :closing and waits for the server to confirm connection is :closed with a ProtocolMessage' do
connection.on(:connected) do
connection.on(:closed) do
EventMachine.add_timer(1) do # allow for all subscribers on incoming message bus
@@ -796,9 +970,84 @@
log_connection_changes
EventMachine.next_tick { connection.close }
end
end
+ end
+ end
+
+ context ':suspended RTN12d' do
+ let(:suspended_retry_timeout) { 60 }
+ let(:client_options) do
+ default_options.merge(
+ disconnected_retry_timeout: 0.02,
+ suspended_retry_timeout: suspended_retry_timeout,
+ connection_state_ttl: 0
+ )
+ end
+
+ it 'immediatly closes connection' do
+ connection.on(:connecting) { close_connection_proc.call }
+ connection.on(:suspended) do |connection_state_change|
+ if connection_state_change.retry_in.zero?
+ # Exhausting immediate retries
+ connection.once(:connecting) { close_connection_proc.call }
+ else
+ suspended_at = Time.now.to_f
+ connection.on(:closed) do
+ expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
+ expect(connection.state).to eq(:closed)
+ expect(Time.now.to_f).to be_within(4).of(suspended_at)
+ stop_reactor
+ end
+
+ connection.close
+ end
+
+ connection.connect
+ end
+
+ connection.connect
+ end
+ end
+
+ context ':disconnected RTN12d' do
+ let(:retry_timeout) { 60 }
+ let(:client_options) do
+ default_options.merge(
+ disconnected_retry_timeout: retry_timeout,
+ suspended_retry_timeout: retry_timeout,
+ connection_state_ttl: 0
+ )
+ end
+
+ it 'immediatly closes connection' do
+ connection.once(:connected) do
+ connection.on(:disconnected) do |connection_state_change|
+ disconnected_at = Time.now.to_f
+ connection.on(:connected) do
+ if connection_state_change.retry_in.zero?
+ # Exhausting immediate retries
+ close_connection_proc.call
+ else
+ connection.once(:closed) do
+ expect(connection_state_change.retry_in).to eq(retry_timeout)
+ expect(connection.state).to eq(:closed)
+ expect(Time.now.to_f).to be_within(4).of(disconnected_at)
+ stop_reactor
+ end
+
+ connection.close
+ end
+ end
+
+ connection.connect
+ end
+
+ close_connection_proc.call
+ end
+
+ connection.connect
end
end
end
end