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