ably-rest 0.8.3 → 0.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +1 -0
- data/lib/submodules/ably-ruby/CHANGELOG.md +10 -0
- data/lib/submodules/ably-ruby/README.md +1 -1
- data/lib/submodules/ably-ruby/Rakefile +1 -1
- data/lib/submodules/ably-ruby/lib/ably/auth.rb +24 -20
- data/lib/submodules/ably-ruby/lib/ably/exceptions.rb +3 -0
- data/lib/submodules/ably-ruby/lib/ably/models/channel_state_change.rb +41 -0
- data/lib/submodules/ably-ruby/lib/ably/models/connection_state_change.rb +43 -0
- data/lib/submodules/ably-ruby/lib/ably/models/message.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/presence_message.rb +1 -1
- data/lib/submodules/ably-ruby/lib/ably/models/protocol_message.rb +2 -1
- data/lib/submodules/ably-ruby/lib/ably/models/token_details.rb +8 -6
- data/lib/submodules/ably-ruby/lib/ably/modules/conversions.rb +4 -0
- data/lib/submodules/ably-ruby/lib/ably/modules/state_emitter.rb +4 -1
- data/lib/submodules/ably-ruby/lib/ably/modules/uses_state_machine.rb +35 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/auth.rb +13 -13
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel.rb +13 -5
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_manager.rb +27 -7
- data/lib/submodules/ably-ruby/lib/ably/realtime/channel/channel_state_machine.rb +22 -12
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/incoming_message_dispatcher.rb +16 -10
- data/lib/submodules/ably-ruby/lib/ably/realtime/client/outgoing_message_dispatcher.rb +6 -0
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection.rb +5 -4
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_manager.rb +42 -24
- data/lib/submodules/ably-ruby/lib/ably/realtime/connection/connection_state_machine.rb +25 -17
- data/lib/submodules/ably-ruby/lib/ably/realtime/presence.rb +4 -4
- data/lib/submodules/ably-ruby/lib/ably/rest/channel.rb +3 -2
- data/lib/submodules/ably-ruby/lib/ably/rest/client.rb +2 -2
- data/lib/submodules/ably-ruby/lib/ably/util/crypto.rb +15 -0
- data/lib/submodules/ably-ruby/lib/ably/version.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/auth_spec.rb +9 -9
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channel_spec.rb +168 -21
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/channels_spec.rb +6 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/client_spec.rb +6 -5
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_failures_spec.rb +29 -19
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/connection_spec.rb +150 -35
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/message_spec.rb +146 -23
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_history_spec.rb +2 -2
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/presence_spec.rb +44 -24
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/stats_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/realtime/time_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/acceptance/rest/auth_spec.rb +77 -46
- data/lib/submodules/ably-ruby/spec/acceptance/rest/channel_spec.rb +31 -3
- data/lib/submodules/ably-ruby/spec/acceptance/rest/client_spec.rb +15 -5
- data/lib/submodules/ably-ruby/spec/acceptance/rest/presence_spec.rb +9 -7
- data/lib/submodules/ably-ruby/spec/support/event_machine_helper.rb +30 -4
- data/lib/submodules/ably-ruby/spec/support/protocol_helper.rb +9 -6
- data/lib/submodules/ably-ruby/spec/unit/auth_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/models/channel_state_change_spec.rb +44 -0
- data/lib/submodules/ably-ruby/spec/unit/models/connection_state_change_spec.rb +54 -0
- data/lib/submodules/ably-ruby/spec/unit/models/token_details_spec.rb +8 -0
- data/lib/submodules/ably-ruby/spec/unit/modules/async_wrapper_spec.rb +1 -1
- data/lib/submodules/ably-ruby/spec/unit/util/crypto_spec.rb +18 -0
- metadata +6 -2
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
# encoding: utf-8
|
|
2
2
|
require 'spec_helper'
|
|
3
3
|
|
|
4
|
-
describe Ably::Realtime::Channels do
|
|
4
|
+
describe Ably::Realtime::Channels, :event_machine do
|
|
5
5
|
shared_examples 'a channel' do
|
|
6
6
|
it 'returns a channel object' do
|
|
7
7
|
expect(channel).to be_a Ably::Realtime::Channel
|
|
8
8
|
expect(channel.name).to eql(channel_name)
|
|
9
|
+
stop_reactor
|
|
9
10
|
end
|
|
10
11
|
|
|
11
12
|
it 'returns channel object and passes the provided options' do
|
|
12
13
|
expect(channel_with_options.options).to eql(options)
|
|
14
|
+
stop_reactor
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
vary_by_protocol do
|
|
17
19
|
let(:client) do
|
|
18
|
-
Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
|
|
20
|
+
auto_close Ably::Realtime::Client.new(key: api_key, environment: environment, protocol: protocol)
|
|
19
21
|
end
|
|
20
22
|
let(:channel_name) { random_str }
|
|
21
23
|
let(:options) { { key: 'value' } }
|
|
@@ -41,6 +43,7 @@ describe Ably::Realtime::Channels do
|
|
|
41
43
|
new_channel = client.channels.get(channel_name, new_channel_options)
|
|
42
44
|
expect(new_channel).to be_a(Ably::Realtime::Channel)
|
|
43
45
|
expect(new_channel.options[:encrypted]).to eql(true)
|
|
46
|
+
stop_reactor
|
|
44
47
|
end
|
|
45
48
|
end
|
|
46
49
|
|
|
@@ -52,6 +55,7 @@ describe Ably::Realtime::Channels do
|
|
|
52
55
|
new_channel = client.channels.get(channel_name)
|
|
53
56
|
expect(new_channel).to be_a(Ably::Realtime::Channel)
|
|
54
57
|
expect(original_channel.options).to eql(options)
|
|
58
|
+
stop_reactor
|
|
55
59
|
end
|
|
56
60
|
end
|
|
57
61
|
|
|
@@ -11,7 +11,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
|
11
11
|
let(:connection) { subject.connection }
|
|
12
12
|
let(:auth_params) { subject.auth.auth_params_sync }
|
|
13
13
|
|
|
14
|
-
subject { Ably::Realtime::Client.new(client_options) }
|
|
14
|
+
subject { auto_close Ably::Realtime::Client.new(client_options) }
|
|
15
15
|
|
|
16
16
|
context 'initialization' do
|
|
17
17
|
context 'basic auth' do
|
|
@@ -31,8 +31,8 @@ describe Ably::Realtime::Client, :event_machine do
|
|
|
31
31
|
it 'fails to connect because a private key cannot be sent over a non-secure connection' do
|
|
32
32
|
connection.on(:connected) { raise 'Should not have connected' }
|
|
33
33
|
|
|
34
|
-
connection.on(:failed) do |
|
|
35
|
-
expect(
|
|
34
|
+
connection.on(:failed) do |connection_state_change|
|
|
35
|
+
expect(connection_state_change.reason).to be_a(Ably::Exceptions::InsecureRequest)
|
|
36
36
|
stop_reactor
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -44,7 +44,8 @@ describe Ably::Realtime::Client, :event_machine do
|
|
|
44
44
|
[true, false].each do |tls_enabled|
|
|
45
45
|
context "with TLS #{tls_enabled ? 'enabled' : 'disabled'}" do
|
|
46
46
|
let(:capability) { { :foo => ["publish"] } }
|
|
47
|
-
let(:
|
|
47
|
+
let(:token_client) { auto_close Ably::Realtime::Client.new(default_options) }
|
|
48
|
+
let(:token_details) { token_client.auth.request_token_sync(capability: capability) }
|
|
48
49
|
let(:client_options) { default_options.merge(token: token_details.token) }
|
|
49
50
|
|
|
50
51
|
context 'and a pre-generated Token provided with the :token option' do
|
|
@@ -93,7 +94,7 @@ describe Ably::Realtime::Client, :event_machine do
|
|
|
93
94
|
let(:auth) { subject.auth }
|
|
94
95
|
|
|
95
96
|
subject do
|
|
96
|
-
Ably::Realtime::Client.new(client_options.merge(auth_callback: Proc.new do
|
|
97
|
+
auto_close Ably::Realtime::Client.new(client_options.merge(auth_callback: Proc.new do
|
|
97
98
|
@block_called = true
|
|
98
99
|
auth.create_token_request_sync(client_id: client_id)
|
|
99
100
|
end))
|
|
@@ -11,7 +11,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
11
11
|
|
|
12
12
|
let(:client_options) { default_options }
|
|
13
13
|
let(:client) do
|
|
14
|
-
Ably::Realtime::Client.new(client_options)
|
|
14
|
+
auto_close Ably::Realtime::Client.new(client_options)
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
context 'authentication failure' do
|
|
@@ -24,7 +24,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
24
24
|
let(:invalid_key) { 'not_an_app.invalid_key_name:invalid_key_value' }
|
|
25
25
|
|
|
26
26
|
it 'enters the failed state and returns a not found error' do
|
|
27
|
-
connection.on(:failed) do |
|
|
27
|
+
connection.on(:failed) do |connection_state_change|
|
|
28
|
+
error = connection_state_change.reason
|
|
28
29
|
expect(connection.state).to eq(:failed)
|
|
29
30
|
# TODO: Check error type is an InvalidToken exception
|
|
30
31
|
expect(error.status).to eq(404)
|
|
@@ -38,7 +39,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
38
39
|
let(:invalid_key) { "#{app_id}.invalid_key_name:invalid_key_value" }
|
|
39
40
|
|
|
40
41
|
it 'enters the failed state and returns an authorization error' do
|
|
41
|
-
connection.on(:failed) do |
|
|
42
|
+
connection.on(:failed) do |connection_state_change|
|
|
43
|
+
error = connection_state_change.reason
|
|
42
44
|
expect(connection.state).to eq(:failed)
|
|
43
45
|
# TODO: Check error type is a TokenNotFOund exception
|
|
44
46
|
expect(error.status).to eq(401)
|
|
@@ -182,7 +184,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
182
184
|
context '#error_reason' do
|
|
183
185
|
[:disconnected, :suspended, :failed].each do |state|
|
|
184
186
|
it "contains the error when state is #{state}" do
|
|
185
|
-
connection.on(state) do |
|
|
187
|
+
connection.on(state) do |connection_state_change|
|
|
188
|
+
error = connection_state_change.reason
|
|
186
189
|
expect(connection.error_reason).to eq(error)
|
|
187
190
|
expect(connection.error_reason.code).to eql(80000)
|
|
188
191
|
stop_reactor
|
|
@@ -278,7 +281,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
278
281
|
let(:channel_name) { random_str }
|
|
279
282
|
let(:channel) { client.channel(channel_name) }
|
|
280
283
|
let(:publishing_client) do
|
|
281
|
-
Ably::Realtime::Client.new(client_options)
|
|
284
|
+
auto_close Ably::Realtime::Client.new(client_options)
|
|
282
285
|
end
|
|
283
286
|
let(:publishing_client_channel) { publishing_client.channel(channel_name) }
|
|
284
287
|
let(:client_options) { default_options.merge(log_level: :none) }
|
|
@@ -389,7 +392,10 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
389
392
|
connection.transport.close_connection_after_writing
|
|
390
393
|
|
|
391
394
|
connection.once(:connected) do
|
|
392
|
-
|
|
395
|
+
# Connection key left part should match new connection key left part i.e.
|
|
396
|
+
# wVIsgTHAB1UvXh7z-1991d8586 becomes wVIsgTHAB1UvXh7z-1990d8586 after resume
|
|
397
|
+
expect(connection.key[/^\w{5,}-/, 0]).to_not be_nil
|
|
398
|
+
expect(connection.key[/^\w{5,}-/, 0]).to eql(previous_connection_key[/^\w{5,}-/, 0])
|
|
393
399
|
expect(connection.id).to eql(previous_connection_id)
|
|
394
400
|
stop_reactor
|
|
395
401
|
end
|
|
@@ -462,9 +468,11 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
462
468
|
|
|
463
469
|
context 'when failing to resume' do
|
|
464
470
|
context 'because the connection_key is not or no longer valid' do
|
|
471
|
+
let(:channel) { client.channel(random_str) }
|
|
472
|
+
|
|
465
473
|
def kill_connection_transport_and_prevent_valid_resume
|
|
466
474
|
connection.transport.close_connection_after_writing
|
|
467
|
-
connection.configure_new '0123456789abcdef', '
|
|
475
|
+
connection.configure_new '0123456789abcdef', 'wVIsgTHAB1UvXh7z-1991d8586', -1 # force the resume connection key to be invalid
|
|
468
476
|
end
|
|
469
477
|
|
|
470
478
|
it 'updates the connection_id and connection_key' do
|
|
@@ -488,8 +496,9 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
488
496
|
when_all(*channels.map(&:attach)) do
|
|
489
497
|
detached_channels = []
|
|
490
498
|
channels.each do |channel|
|
|
491
|
-
channel.on(:detached) do |
|
|
492
|
-
|
|
499
|
+
channel.on(:detached) do |channel_state_change|
|
|
500
|
+
error = channel_state_change.reason
|
|
501
|
+
expect(error.message).to match(/Unable to recover connection/i)
|
|
493
502
|
detached_channels << channel
|
|
494
503
|
next unless detached_channels.count == channel_count
|
|
495
504
|
expect(detached_channels.count).to eql(channel_count)
|
|
@@ -502,16 +511,16 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
502
511
|
end
|
|
503
512
|
|
|
504
513
|
it 'emits an error on the channel and sets the error reason' do
|
|
505
|
-
|
|
506
|
-
channel.on(:error) do |error|
|
|
507
|
-
expect(error.message).to match(/Invalid connection key/i)
|
|
508
|
-
expect(error.code).to eql(80008)
|
|
509
|
-
expect(channel.error_reason).to eql(error)
|
|
510
|
-
stop_reactor
|
|
511
|
-
end
|
|
512
|
-
|
|
514
|
+
channel.attach do
|
|
513
515
|
kill_connection_transport_and_prevent_valid_resume
|
|
514
516
|
end
|
|
517
|
+
|
|
518
|
+
channel.on(:error) do |error|
|
|
519
|
+
expect(error.message).to match(/Unable to recover connection/i)
|
|
520
|
+
expect(error.code).to eql(80008)
|
|
521
|
+
expect(channel.error_reason).to eql(error)
|
|
522
|
+
stop_reactor
|
|
523
|
+
end
|
|
515
524
|
end
|
|
516
525
|
end
|
|
517
526
|
end
|
|
@@ -620,11 +629,11 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
620
629
|
|
|
621
630
|
it 'uses a fallback host on every subsequent disconnected attempt until suspended' do
|
|
622
631
|
request = 0
|
|
623
|
-
|
|
632
|
+
# Expect retry attempts + 1 attempt for the next state
|
|
633
|
+
expect(EventMachine).to receive(:connect).exactly(retry_count_for_one_state + 1).times do |host|
|
|
624
634
|
if request == 0
|
|
625
635
|
expect(host).to eql(expected_host)
|
|
626
636
|
else
|
|
627
|
-
expect(custom_hosts).to include(host)
|
|
628
637
|
fallback_hosts_used << host
|
|
629
638
|
end
|
|
630
639
|
request += 1
|
|
@@ -632,6 +641,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
|
632
641
|
end
|
|
633
642
|
|
|
634
643
|
connection.on(:suspended) do
|
|
644
|
+
fallback_hosts_used.pop # remove suspended attempt host
|
|
635
645
|
expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
|
|
636
646
|
stop_reactor
|
|
637
647
|
end
|
|
@@ -11,7 +11,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
let(:client_options) { default_options }
|
|
14
|
-
let(:client) { Ably::Realtime::Client.new(client_options) }
|
|
14
|
+
let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
|
|
15
15
|
|
|
16
16
|
before(:example) do
|
|
17
17
|
EventMachine.add_shutdown_hook do
|
|
@@ -29,7 +29,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
29
29
|
|
|
30
30
|
context 'with :auto_connect option set to false' do
|
|
31
31
|
let(:client) do
|
|
32
|
-
Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
|
|
32
|
+
auto_close Ably::Realtime::Client.new(default_options.merge(auto_connect: false))
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
it 'does not connect automatically' do
|
|
@@ -96,7 +96,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
96
96
|
stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0)
|
|
97
97
|
|
|
98
98
|
# Authorise synchronously to ensure token has been issued
|
|
99
|
-
client.auth.authorise_sync(
|
|
99
|
+
client.auth.authorise_sync(ttl: ttl)
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
let(:original_renew_token_buffer) { @original_renew_token_buffer }
|
|
@@ -127,8 +127,8 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
127
127
|
it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
|
|
128
128
|
expect(client.rest_client.auth).to receive(:authorise).at_least(:twice).and_call_original
|
|
129
129
|
connection.once(:disconnected) do
|
|
130
|
-
connection.once(:failed) do |
|
|
131
|
-
expect(
|
|
130
|
+
connection.once(:failed) do |connection_state_change|
|
|
131
|
+
expect(connection_state_change.reason.code).to eql(40140) # token expired
|
|
132
132
|
stop_reactor
|
|
133
133
|
end
|
|
134
134
|
end
|
|
@@ -158,7 +158,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
158
158
|
context 'when connected with a valid non-expired token' do
|
|
159
159
|
context 'that then expires following the connection being opened' do
|
|
160
160
|
let(:ttl) { 5 }
|
|
161
|
-
let(:channel) { client.channel(
|
|
161
|
+
let(:channel) { client.channel(random_str) }
|
|
162
162
|
|
|
163
163
|
context 'the server' do
|
|
164
164
|
it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 15 do
|
|
@@ -167,8 +167,8 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
167
167
|
|
|
168
168
|
connection.once(:connected) do
|
|
169
169
|
started_at = Time.now
|
|
170
|
-
connection.once(:disconnected) do |
|
|
171
|
-
expect(
|
|
170
|
+
connection.once(:disconnected) do |connection_state_change|
|
|
171
|
+
expect(connection_state_change.reason.code).to eq(40140) # Token expired
|
|
172
172
|
|
|
173
173
|
# Token has expired, so now ensure it is not used again
|
|
174
174
|
stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', original_token_expiry_buffer
|
|
@@ -178,7 +178,6 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
178
178
|
expect(client.auth.current_token_details).to_not be_expired
|
|
179
179
|
expect(Time.now - started_at >= ttl)
|
|
180
180
|
expect(original_token).to be_expired
|
|
181
|
-
expect(error.code).to eql(40140) # token expired
|
|
182
181
|
stop_reactor
|
|
183
182
|
end
|
|
184
183
|
end
|
|
@@ -205,7 +204,8 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
205
204
|
|
|
206
205
|
let!(:expired_token_details) do
|
|
207
206
|
# Request a token synchronously
|
|
208
|
-
Ably::Realtime::Client.new(default_options)
|
|
207
|
+
token_client = auto_close Ably::Realtime::Client.new(default_options)
|
|
208
|
+
token_client.auth.request_token_sync(ttl: 0.01)
|
|
209
209
|
end
|
|
210
210
|
|
|
211
211
|
context 'opening a new connection' do
|
|
@@ -271,8 +271,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
271
271
|
end
|
|
272
272
|
|
|
273
273
|
it 'calls the Deferrable callback on success' do
|
|
274
|
-
connection.connect.callback do
|
|
275
|
-
expect(connection).to be_a(Ably::Realtime::Connection)
|
|
274
|
+
connection.connect.callback do
|
|
276
275
|
expect(connection.state).to eq(:connected)
|
|
277
276
|
stop_reactor
|
|
278
277
|
end
|
|
@@ -307,7 +306,8 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
307
306
|
end
|
|
308
307
|
|
|
309
308
|
describe 'once connected' do
|
|
310
|
-
let(:
|
|
309
|
+
let(:client2) { auto_close Ably::Realtime::Client.new(client_options) }
|
|
310
|
+
let(:connection2) { client2.connection }
|
|
311
311
|
|
|
312
312
|
describe 'connection#id' do
|
|
313
313
|
it 'is a string' do
|
|
@@ -437,7 +437,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
437
437
|
|
|
438
438
|
it 'calls the Deferrable callback on success' do
|
|
439
439
|
connection.connect do
|
|
440
|
-
connection.close.callback do
|
|
440
|
+
connection.close.callback do
|
|
441
441
|
expect(connection).to be_a(Ably::Realtime::Connection)
|
|
442
442
|
expect(connection.state).to eq(:closed)
|
|
443
443
|
stop_reactor
|
|
@@ -577,7 +577,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
577
577
|
let(:channel_name) { random_str }
|
|
578
578
|
let(:channel) { client.channel(channel_name) }
|
|
579
579
|
let(:publishing_client) do
|
|
580
|
-
Ably::Realtime::Client.new(client_options)
|
|
580
|
+
auto_close Ably::Realtime::Client.new(client_options)
|
|
581
581
|
end
|
|
582
582
|
let(:publishing_client_channel) { publishing_client.channel(channel_name) }
|
|
583
583
|
let(:client_options) { default_options.merge(log_level: :fatal) }
|
|
@@ -595,9 +595,10 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
595
595
|
def self.available_states
|
|
596
596
|
[:connecting, :connected, :disconnected, :suspended, :failed]
|
|
597
597
|
end
|
|
598
|
-
let(:available_states) { self.class.available_states}
|
|
598
|
+
let(:available_states) { self.class.available_states }
|
|
599
599
|
let(:states) { Hash.new }
|
|
600
600
|
let(:client_options) { default_options.merge(log_level: :none) }
|
|
601
|
+
let(:channel) { client.channel(random_str) }
|
|
601
602
|
|
|
602
603
|
it 'is composed of connection key and serial that is kept up to date with each message ACK received' do
|
|
603
604
|
connection.on(:connected) do
|
|
@@ -605,7 +606,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
605
606
|
expect(connection.key).to_not be_nil
|
|
606
607
|
expect(connection.serial).to eql(expected_serial)
|
|
607
608
|
|
|
608
|
-
|
|
609
|
+
channel.attach do
|
|
609
610
|
channel.publish('event', 'data') do
|
|
610
611
|
expected_serial += 1 # attach message received
|
|
611
612
|
expect(connection.serial).to eql(expected_serial)
|
|
@@ -671,9 +672,10 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
671
672
|
end
|
|
672
673
|
|
|
673
674
|
connection.once(:failed) do
|
|
674
|
-
recover_client = Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
|
675
|
+
recover_client = auto_close Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
|
675
676
|
recover_client.connection.on(:connected) do
|
|
676
|
-
expect(recover_client.connection.key).
|
|
677
|
+
expect(recover_client.connection.key[/^\w{5,}-/, 0]).to_not be_nil
|
|
678
|
+
expect(recover_client.connection.key[/^\w{5,}-/, 0]).to eql(previous_connection_key[/^\w{5,}-/, 0])
|
|
677
679
|
expect(recover_client.connection.id).to eql(previous_connection_id)
|
|
678
680
|
stop_reactor
|
|
679
681
|
end
|
|
@@ -686,7 +688,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
686
688
|
end
|
|
687
689
|
|
|
688
690
|
connection.once(:failed) do
|
|
689
|
-
recover_client = Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
|
691
|
+
recover_client = auto_close Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
|
690
692
|
recover_client.connection.on_resume do
|
|
691
693
|
raise 'Should not call the resume callback'
|
|
692
694
|
end
|
|
@@ -702,14 +704,15 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
702
704
|
let(:client_options) { default_options.merge(log_level: :none) }
|
|
703
705
|
|
|
704
706
|
it 'recovers server-side queued messages' do
|
|
705
|
-
channel.attach do
|
|
707
|
+
channel.attach do
|
|
706
708
|
connection.transition_state_machine! :failed
|
|
707
709
|
end
|
|
708
710
|
|
|
709
711
|
connection.on(:failed) do
|
|
710
712
|
publishing_client_channel.publish('event', 'message') do
|
|
711
|
-
recover_client = Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
|
712
|
-
recover_client.channel(channel_name)
|
|
713
|
+
recover_client = auto_close Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
|
714
|
+
recover_client_channel = recover_client.channel(channel_name)
|
|
715
|
+
recover_client_channel.attach do
|
|
713
716
|
recover_client_channel.subscribe('event') do |message|
|
|
714
717
|
expect(message.data).to eql('message')
|
|
715
718
|
stop_reactor
|
|
@@ -738,9 +741,9 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
738
741
|
it 'emits a fatal error on the connection object, sets the #error_reason and disconnects' do
|
|
739
742
|
connection.once(:error) do |error|
|
|
740
743
|
expect(connection.state).to eq(:failed)
|
|
741
|
-
expect(error.message).to match(/Invalid
|
|
742
|
-
expect(connection.error_reason.message).to match(/Invalid
|
|
743
|
-
expect(connection.error_reason.code).to eql(
|
|
744
|
+
expect(error.message).to match(/Invalid connectionKey/i)
|
|
745
|
+
expect(connection.error_reason.message).to match(/Invalid connectionKey/i)
|
|
746
|
+
expect(connection.error_reason.code).to eql(80018)
|
|
744
747
|
expect(connection.error_reason).to eql(error)
|
|
745
748
|
stop_reactor
|
|
746
749
|
end
|
|
@@ -748,13 +751,13 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
748
751
|
end
|
|
749
752
|
|
|
750
753
|
context 'with expired (missing) value sent to server' do
|
|
751
|
-
let(:client_options) { default_options.merge(recover: '
|
|
754
|
+
let(:client_options) { default_options.merge(recover: 'wVIsgTHAB1UvXh7z-1991d8586:0', log_level: :fatal) }
|
|
752
755
|
|
|
753
756
|
it 'emits an error on the connection object, sets the #error_reason, yet will connect anyway' do
|
|
754
757
|
connection.once(:error) do |error|
|
|
755
758
|
expect(connection.state).to eq(:connected)
|
|
756
|
-
expect(error.message).to match(/
|
|
757
|
-
expect(connection.error_reason.message).to match(/
|
|
759
|
+
expect(error.message).to match(/Unable to recover connection/i)
|
|
760
|
+
expect(connection.error_reason.message).to match(/Unable to recover connection/i)
|
|
758
761
|
expect(connection.error_reason.code).to eql(80008)
|
|
759
762
|
expect(connection.error_reason).to eql(error)
|
|
760
763
|
stop_reactor
|
|
@@ -922,12 +925,12 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
922
925
|
expect(connection.__outgoing_message_queue__).to be_empty
|
|
923
926
|
channel.publish 'test'
|
|
924
927
|
|
|
925
|
-
EventMachine.
|
|
928
|
+
EventMachine.next_tick do
|
|
926
929
|
expect(connection.__outgoing_message_queue__).to_not be_empty
|
|
927
930
|
end
|
|
928
931
|
|
|
929
932
|
connection.once(:connected) do
|
|
930
|
-
EventMachine.add_timer(0.
|
|
933
|
+
EventMachine.add_timer(0.1) do
|
|
931
934
|
expect(connection.__outgoing_message_queue__).to be_empty
|
|
932
935
|
stop_reactor
|
|
933
936
|
end
|
|
@@ -940,6 +943,8 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
940
943
|
end
|
|
941
944
|
|
|
942
945
|
context 'when connection enters the :suspended state' do
|
|
946
|
+
let(:client_options) { default_options.merge(:log_level => :fatal) }
|
|
947
|
+
|
|
943
948
|
before do
|
|
944
949
|
# Reconfigure client library retry periods so that client stays in suspended state
|
|
945
950
|
stub_const 'Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG',
|
|
@@ -956,13 +961,21 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
956
961
|
stop_reactor
|
|
957
962
|
end
|
|
958
963
|
|
|
964
|
+
close_connection_proc = Proc.new do
|
|
965
|
+
EventMachine.add_timer(0.001) do
|
|
966
|
+
if connection.transport.nil?
|
|
967
|
+
close_connection_proc.call
|
|
968
|
+
else
|
|
969
|
+
connection.transport.close_connection_after_writing
|
|
970
|
+
end
|
|
971
|
+
end
|
|
972
|
+
end
|
|
973
|
+
|
|
959
974
|
# Keep disconnecting the websocket transport after it attempts reconnection
|
|
960
|
-
connection.transport.close_connection_after_writing
|
|
961
975
|
connection.on(:connecting) do
|
|
962
|
-
|
|
963
|
-
connection.transport.close_connection_after_writing
|
|
964
|
-
end
|
|
976
|
+
close_connection_proc.call
|
|
965
977
|
end
|
|
978
|
+
close_connection_proc.call
|
|
966
979
|
end
|
|
967
980
|
end
|
|
968
981
|
end
|
|
@@ -979,5 +992,107 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
|
979
992
|
end
|
|
980
993
|
end
|
|
981
994
|
end
|
|
995
|
+
|
|
996
|
+
context 'connection state change' do
|
|
997
|
+
it 'emits a ConnectionStateChange object' do
|
|
998
|
+
connection.on(:connected) do |connection_state_change|
|
|
999
|
+
expect(connection_state_change).to be_a(Ably::Models::ConnectionStateChange)
|
|
1000
|
+
stop_reactor
|
|
1001
|
+
end
|
|
1002
|
+
end
|
|
1003
|
+
|
|
1004
|
+
context 'ConnectionStateChange object' do
|
|
1005
|
+
it 'has current state' do
|
|
1006
|
+
connection.on(:connected) do |connection_state_change|
|
|
1007
|
+
expect(connection_state_change.current).to eq(:connected)
|
|
1008
|
+
stop_reactor
|
|
1009
|
+
end
|
|
1010
|
+
end
|
|
1011
|
+
|
|
1012
|
+
it 'has a previous state' do
|
|
1013
|
+
connection.on(:connected) do |connection_state_change|
|
|
1014
|
+
expect(connection_state_change.previous).to eq(:connecting)
|
|
1015
|
+
stop_reactor
|
|
1016
|
+
end
|
|
1017
|
+
end
|
|
1018
|
+
|
|
1019
|
+
it 'contains a private API protocol_message attribute that is used for special state change events', :api_private do
|
|
1020
|
+
connection.on(:connected) do |connection_state_change|
|
|
1021
|
+
expect(connection_state_change.protocol_message).to be_a(Ably::Models::ProtocolMessage)
|
|
1022
|
+
expect(connection_state_change.reason).to be_nil
|
|
1023
|
+
stop_reactor
|
|
1024
|
+
end
|
|
1025
|
+
end
|
|
1026
|
+
|
|
1027
|
+
it 'has an empty reason when there is no error' do
|
|
1028
|
+
connection.on(:closed) do |connection_state_change|
|
|
1029
|
+
expect(connection_state_change.reason).to be_nil
|
|
1030
|
+
stop_reactor
|
|
1031
|
+
end
|
|
1032
|
+
connection.connect do
|
|
1033
|
+
connection.close
|
|
1034
|
+
end
|
|
1035
|
+
end
|
|
1036
|
+
|
|
1037
|
+
context 'on failure' do
|
|
1038
|
+
let(:client_options) { default_options.merge(log_level: :none) }
|
|
1039
|
+
|
|
1040
|
+
it 'has a reason Error object when there is an error on the connection' do
|
|
1041
|
+
connection.on(:failed) do |connection_state_change|
|
|
1042
|
+
expect(connection_state_change.reason).to be_a(Ably::Exceptions::BaseAblyException)
|
|
1043
|
+
stop_reactor
|
|
1044
|
+
end
|
|
1045
|
+
connection.connect do
|
|
1046
|
+
error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
|
|
1047
|
+
client.connection.manager.error_received_from_server error
|
|
1048
|
+
end
|
|
1049
|
+
end
|
|
1050
|
+
end
|
|
1051
|
+
|
|
1052
|
+
context 'retry_in' do
|
|
1053
|
+
let(:client_options) { default_options.merge(log_level: :error) }
|
|
1054
|
+
|
|
1055
|
+
it 'is nil when a retry is not required' do
|
|
1056
|
+
connection.on(:connected) do |connection_state_change|
|
|
1057
|
+
expect(connection_state_change.retry_in).to be_nil
|
|
1058
|
+
stop_reactor
|
|
1059
|
+
end
|
|
1060
|
+
end
|
|
1061
|
+
|
|
1062
|
+
it 'is 0 when first attempt to connect fails' do
|
|
1063
|
+
connection.once(:connecting) do
|
|
1064
|
+
connection.once(:disconnected) do |connection_state_change|
|
|
1065
|
+
expect(connection_state_change.retry_in).to eql(0)
|
|
1066
|
+
stop_reactor
|
|
1067
|
+
end
|
|
1068
|
+
EventMachine.add_timer(0.005) { connection.transport.unbind }
|
|
1069
|
+
end
|
|
1070
|
+
end
|
|
1071
|
+
|
|
1072
|
+
it 'is 0 when an immediate reconnect will occur' do
|
|
1073
|
+
connection.once(:connected) do
|
|
1074
|
+
connection.once(:disconnected) do |connection_state_change|
|
|
1075
|
+
expect(connection_state_change.retry_in).to eql(0)
|
|
1076
|
+
stop_reactor
|
|
1077
|
+
end
|
|
1078
|
+
connection.transport.unbind
|
|
1079
|
+
end
|
|
1080
|
+
end
|
|
1081
|
+
|
|
1082
|
+
it 'contains the next retry period when an immediate reconnect will not occur' do
|
|
1083
|
+
connection.once(:connected) do
|
|
1084
|
+
connection.once(:connecting) do
|
|
1085
|
+
connection.once(:disconnected) do |connection_state_change|
|
|
1086
|
+
expect(connection_state_change.retry_in).to be > 0
|
|
1087
|
+
stop_reactor
|
|
1088
|
+
end
|
|
1089
|
+
EventMachine.add_timer(0.005) { connection.transport.unbind }
|
|
1090
|
+
end
|
|
1091
|
+
connection.transport.unbind
|
|
1092
|
+
end
|
|
1093
|
+
end
|
|
1094
|
+
end
|
|
1095
|
+
end
|
|
1096
|
+
end
|
|
982
1097
|
end
|
|
983
1098
|
end
|