ably 0.8.5 → 0.8.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG.md +42 -48
  4. data/SPEC.md +1099 -640
  5. data/ably.gemspec +10 -4
  6. data/lib/ably/auth.rb +155 -47
  7. data/lib/ably/exceptions.rb +2 -0
  8. data/lib/ably/models/channel_state_change.rb +2 -3
  9. data/lib/ably/models/connection_details.rb +54 -0
  10. data/lib/ably/models/protocol_message.rb +14 -4
  11. data/lib/ably/models/token_details.rb +13 -7
  12. data/lib/ably/models/token_request.rb +1 -2
  13. data/lib/ably/modules/ably.rb +3 -2
  14. data/lib/ably/modules/message_emitter.rb +1 -3
  15. data/lib/ably/modules/state_emitter.rb +2 -2
  16. data/lib/ably/realtime/auth.rb +6 -0
  17. data/lib/ably/realtime/channel/channel_manager.rb +2 -0
  18. data/lib/ably/realtime/channel.rb +15 -4
  19. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
  20. data/lib/ably/realtime/client.rb +10 -3
  21. data/lib/ably/realtime/connection/connection_manager.rb +58 -54
  22. data/lib/ably/realtime/connection.rb +62 -6
  23. data/lib/ably/realtime/presence.rb +18 -5
  24. data/lib/ably/rest/channel.rb +9 -1
  25. data/lib/ably/rest/client.rb +32 -14
  26. data/lib/ably/rest/presence.rb +1 -1
  27. data/lib/ably/version.rb +1 -1
  28. data/lib/ably.rb +2 -0
  29. data/spec/acceptance/realtime/auth_spec.rb +251 -11
  30. data/spec/acceptance/realtime/channel_history_spec.rb +12 -2
  31. data/spec/acceptance/realtime/channel_spec.rb +316 -24
  32. data/spec/acceptance/realtime/client_spec.rb +93 -1
  33. data/spec/acceptance/realtime/connection_failures_spec.rb +177 -86
  34. data/spec/acceptance/realtime/connection_spec.rb +284 -60
  35. data/spec/acceptance/realtime/message_spec.rb +45 -6
  36. data/spec/acceptance/realtime/presence_history_spec.rb +4 -0
  37. data/spec/acceptance/realtime/presence_spec.rb +181 -49
  38. data/spec/acceptance/realtime/time_spec.rb +13 -0
  39. data/spec/acceptance/rest/auth_spec.rb +222 -4
  40. data/spec/acceptance/rest/channel_spec.rb +132 -1
  41. data/spec/acceptance/rest/client_spec.rb +129 -28
  42. data/spec/acceptance/rest/presence_spec.rb +7 -7
  43. data/spec/acceptance/rest/time_spec.rb +10 -0
  44. data/spec/shared/client_initializer_behaviour.rb +41 -17
  45. data/spec/spec_helper.rb +1 -0
  46. data/spec/support/debug_failure_helper.rb +16 -0
  47. data/spec/unit/models/connection_details_spec.rb +60 -0
  48. data/spec/unit/models/protocol_message_spec.rb +45 -0
  49. data/spec/unit/modules/event_emitter_spec.rb +3 -1
  50. data/spec/unit/realtime/channel_spec.rb +6 -5
  51. data/spec/unit/realtime/client_spec.rb +5 -1
  52. data/spec/unit/realtime/connection_spec.rb +5 -1
  53. data/spec/unit/realtime/realtime_spec.rb +5 -1
  54. metadata +54 -7
@@ -84,7 +84,7 @@ describe Ably::Realtime::Connection, :event_machine do
84
84
  end
85
85
 
86
86
  context 'that expire' do
87
- let(:client_options) { default_options.merge(log_level: :none) }
87
+ let(:client_options) { default_options.merge(log_level: :error) }
88
88
 
89
89
  before do
90
90
  expect(client.rest_client.time.to_f).to be_within(2).of(Time.now.to_i), "Local clock is out of sync with Ably"
@@ -94,59 +94,97 @@ describe Ably::Realtime::Connection, :event_machine do
94
94
  # Ensure tokens issued expire immediately after issue
95
95
  @original_renew_token_buffer = Ably::Auth::TOKEN_DEFAULTS.fetch(:renew_token_buffer)
96
96
  stub_const 'Ably::Auth::TOKEN_DEFAULTS', Ably::Auth::TOKEN_DEFAULTS.merge(renew_token_buffer: 0)
97
-
98
- # Authorise synchronously to ensure token has been issued
99
- client.auth.authorise_sync(ttl: ttl)
100
97
  end
101
98
 
102
99
  let(:original_renew_token_buffer) { @original_renew_token_buffer }
103
100
 
104
101
  context 'opening a new connection' do
105
- context 'with recently expired token' do
102
+ context 'with almost expired tokens' do
103
+ before do
104
+ # Authorise synchronously to ensure token has been issued
105
+ client.auth.authorise_sync(ttl: ttl)
106
+ end
107
+
106
108
  let(:ttl) { 2 }
107
109
 
108
- it 'renews the token on connect without changing connection state' do
109
- connection.once(:connecting) do
110
- sleep ttl + 0.1
111
- expect(client.auth.current_token_details).to be_expired
112
- expect(client.rest_client.auth).to receive(:authorise).at_least(:once).and_call_original
113
- connection.once(:connected) do
114
- expect(client.auth.current_token_details).to_not be_expired
110
+ it 'renews token every time after it expires' do
111
+ started_at = Time.now.to_f
112
+ connected_times = 0
113
+ disconnected_times = 0
114
+ connection.on(:connected) do
115
+ connected_times += 1
116
+ end
117
+ connection.on(:disconnected) do
118
+ disconnected_times += 1
119
+ if disconnected_times == 3
120
+ expect(connected_times).to eql(3)
121
+ expect(Time.now.to_f - started_at).to be > ttl * 3
122
+ expect(Time.now.to_f - started_at).to be < (ttl * 2) * 3
115
123
  stop_reactor
116
124
  end
117
- connection.once_state_changed do
118
- raise "Invalid state #{connection.state}" unless connection.state == :connected
119
- end
120
125
  end
121
126
  end
122
127
  end
123
128
 
124
- context 'with immediately expiring token' do
129
+ context 'with immediately expired token' do
125
130
  let(:ttl) { 0.001 }
131
+ let(:auth_requests) { [] }
132
+ let(:token_callback) do
133
+ Proc.new do
134
+ auth_requests << Time.now
135
+ Ably::Rest::Client.new(default_options).auth.request_token(ttl: ttl).token
136
+ end
137
+ end
138
+ let(:client_options) { default_options.merge(auth_callback: token_callback) }
126
139
 
127
- it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
128
- expect(client.rest_client.auth).to receive(:authorise).at_least(:twice).and_call_original
140
+ it 'renews the token on connect, and makes one immediate subsequent attempt to obtain a new token' do
141
+ started_at = Time.now.to_f
129
142
  connection.once(:disconnected) do
130
- connection.once(:failed) do |connection_state_change|
143
+ connection.once(:disconnected) do |connection_state_change|
131
144
  expect(connection_state_change.reason.code).to eql(40140) # token expired
145
+ expect(Time.now.to_f - started_at).to be < 1000
146
+ expect(auth_requests.count).to eql(2)
132
147
  stop_reactor
133
148
  end
134
149
  end
135
150
  end
136
151
 
137
- it 'uses the primary host for subsequent connection and auth requests' do
138
- EventMachine.add_timer(1) do # wait for token to expire
152
+ context 'when disconnected_retry_timeout is 0.5 seconds' do
153
+ let(:client_options) { default_options.merge(disconnected_retry_timeout: 0.5, auth_callback: token_callback, log_level: :error) }
154
+
155
+ it 'renews the token on connect, and continues to attempt renew based on the retry schedule' do
156
+ started_at = Time.now.to_f
157
+ disconnect_count = 0
158
+ connection.on(:disconnected) do |connection_state_change|
159
+ expect(connection_state_change.reason.code).to eql(40140) # token expired
160
+ disconnect_count += 1
161
+ if disconnect_count == 6
162
+ expect(Time.now.to_f - started_at).to be > 4 * 0.5 # at least 4 0.5 second pauses should have happened
163
+ expect(Time.now.to_f - started_at).to be < 9 # allow 1.5 seconds for each authentication cycle
164
+ stop_reactor
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ context 'using implicit token auth' do
171
+ let(:client_options) { default_options.merge(use_token_auth: true, token_params: { ttl: ttl }) }
172
+
173
+ before do
174
+ stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', -10 # ensure client lib thinks token is still valid
175
+ end
176
+
177
+ it 'uses the primary host for subsequent connection and auth requests' do
139
178
  connection.once(:disconnected) do
140
179
  expect(client.rest_client.connection).to receive(:post).
141
180
  with(/requestToken$/, anything).
142
- at_least(:once).
181
+ exactly(:once).
143
182
  and_call_original
144
183
 
145
184
  expect(client.rest_client).to_not receive(:fallback_connection)
146
185
  expect(client).to_not receive(:fallback_endpoint)
147
186
 
148
- connection.once(:failed) do
149
- connection.off
187
+ connection.once(:disconnected) do
150
188
  stop_reactor
151
189
  end
152
190
  end
@@ -157,15 +195,16 @@ describe Ably::Realtime::Connection, :event_machine do
157
195
 
158
196
  context 'when connected with a valid non-expired token' do
159
197
  context 'that then expires following the connection being opened' do
160
- let(:ttl) { 5 }
161
- let(:channel) { client.channel(random_str) }
198
+ let(:ttl) { 5 }
199
+ let(:channel_name) { random_str }
200
+ let(:channel) { client.channel(channel_name) }
201
+ let(:client_options) { default_options.merge(use_token_auth: true, token_params: { ttl: ttl }) }
162
202
 
163
203
  context 'the server' do
164
204
  it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 15 do
165
- original_token = client.auth.current_token_details
166
- expect(original_token).to_not be_expired
167
-
168
205
  connection.once(:connected) do
206
+ original_token = client.auth.current_token_details
207
+ expect(original_token).to_not be_expired
169
208
  started_at = Time.now
170
209
  connection.once(:disconnected) do |connection_state_change|
171
210
  expect(connection_state_change.reason.code).to eq(40140) # Token expired
@@ -189,8 +228,86 @@ describe Ably::Realtime::Connection, :event_machine do
189
228
  end
190
229
  end
191
230
 
192
- skip 'retains connection state'
193
- skip 'changes state to failed if a new token cannot be issued'
231
+ context 'connection state' do
232
+ let(:ttl) { 4 }
233
+ let(:auth_requests) { [] }
234
+ let(:token_callback) do
235
+ Proc.new do
236
+ sleep 2
237
+ auth_requests << Time.now
238
+ Ably::Rest::Client.new(default_options).auth.request_token(ttl: ttl).token
239
+ end
240
+ end
241
+ let(:client_options) { default_options.merge(auth_callback: token_callback) }
242
+ let(:publishing_client) { auto_close Ably::Realtime::Client.new(default_options) }
243
+ let(:publishing_channel) { publishing_client.channels.get(channel_name) }
244
+ let(:messages_received) { [] }
245
+
246
+ def publish_and_check_first_disconnect
247
+ 10.times.each { |index| publishing_channel.publish('event', index.to_s) }
248
+ channel.subscribe('event') do |message|
249
+ messages_received << message.data.to_i
250
+ if messages_received.count == 10
251
+ expect(messages_received).to match(10.times)
252
+ expect(auth_requests.count).to eql(2)
253
+ EventMachine.add_timer(1) do
254
+ channel.unsubscribe 'event'
255
+ yield
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+ def publish_and_check_second_disconnect
262
+ 10.times.each { |index| publishing_channel.publish('event', (index + 10).to_s) }
263
+ channel.subscribe('event') do |message|
264
+ messages_received << message.data.to_i
265
+ if messages_received.count == 20
266
+ expect(messages_received).to match(20.times)
267
+ expect(auth_requests.count).to eql(3)
268
+ stop_reactor
269
+ end
270
+ end
271
+ end
272
+
273
+ it 'retains messages published when disconnected twice during authentication', em_timeout: 20 do
274
+ publishing_channel.attach do
275
+ channel.attach do
276
+ connection.once(:disconnected) do
277
+ publish_and_check_first_disconnect do
278
+ connection.once(:disconnected) do
279
+ publish_and_check_second_disconnect
280
+ end
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
286
+ end
287
+
288
+ context 'and subsequent token is invalid' do
289
+ let(:ttl) { 2 }
290
+ let(:token_callback) do
291
+ Proc.new do
292
+ if @token_issued
293
+ "#{app_id}.invalid-token-invalid-token-invalid-token"
294
+ else
295
+ @token_issued = true
296
+ Ably::Rest::Client.new(default_options).auth.request_token(ttl: ttl).token
297
+ end
298
+ end
299
+ end
300
+ let(:client_options) { default_options.merge(auth_callback: token_callback, log_level: :none) }
301
+
302
+ it 'transitions the connection to the failed state' do
303
+ connection.once(:disconnected) do
304
+ connection.once(:failed) do
305
+ expect(connection.error_reason.code).to eql(40101)
306
+ stop_reactor
307
+ end
308
+ end
309
+ end
310
+ end
194
311
  end
195
312
  end
196
313
  end
@@ -228,6 +345,50 @@ describe Ably::Realtime::Connection, :event_machine do
228
345
  end
229
346
  end
230
347
  end
348
+
349
+ context 'with opaque token string that contain an implicit client_id' do
350
+ let(:client_options) { default_options.merge(token: token_string, key: nil) }
351
+ let(:rest_auth_client) { Ably::Rest::Client.new(default_options.merge(key: api_key)) }
352
+ let(:token_string) { rest_auth_client.auth.request_token(client_id: client_id).token }
353
+
354
+ context 'string' do
355
+ let(:client_id) { random_str }
356
+
357
+ it 'sets the Client#client_id and Auth#client_id once CONNECTED' do
358
+ expect(client.client_id).to be_nil
359
+ client.connection.once(:connected) do
360
+ expect(client.client_id).to eql(client_id)
361
+ stop_reactor
362
+ end
363
+ end
364
+
365
+ context 'that is incompatible with the current client client_id' do
366
+ let(:client_id) { random_str }
367
+ let(:client_options) { default_options.merge(client_id: 'incompatible', token: token_string, key: nil, log_level: :none) }
368
+
369
+ it 'fails the connection' do
370
+ expect(client.client_id).to eql('incompatible')
371
+ client.connection.once(:failed) do
372
+ expect(client.client_id).to eql('incompatible')
373
+ expect(client.connection.error_reason.code).to eql(40101) # Invalid clientId for credentials
374
+ stop_reactor
375
+ end
376
+ end
377
+ end
378
+ end
379
+
380
+ context 'wildcard' do
381
+ let(:client_id) { '*' }
382
+
383
+ it 'configures the Client#client_id and Auth#client_id with a wildcard once CONNECTED' do
384
+ expect(client.client_id).to be_nil
385
+ client.connection.once(:connected) do
386
+ expect(client.client_id).to eql('*')
387
+ stop_reactor
388
+ end
389
+ end
390
+ end
391
+ end
231
392
  end
232
393
  end
233
394
 
@@ -277,6 +438,50 @@ describe Ably::Realtime::Connection, :event_machine do
277
438
  end
278
439
  end
279
440
 
441
+ it 'calls the provided block on success even if state changes to disconnected first' do
442
+ been_disconnected = false
443
+
444
+ connection.once(:disconnected) do
445
+ been_disconnected = true
446
+ end
447
+ connection.once(:connecting) do
448
+ close_if_transport_available = proc do
449
+ EventMachine.add_timer(0.001) do
450
+ if connection.transport
451
+ connection.transport.close_connection_after_writing
452
+ else
453
+ close_if_transport_available.call
454
+ end
455
+ end
456
+ end
457
+ close_if_transport_available.call
458
+ end
459
+
460
+ connection.connect do
461
+ expect(connection.state).to eq(:connected)
462
+ expect(been_disconnected).to be_truthy
463
+ stop_reactor
464
+ end
465
+ end
466
+
467
+ context 'with invalid auth details' do
468
+ let(:client_options) { default_options.merge(key: 'this.is:invalid', log_level: :none) }
469
+
470
+ it 'calls the Deferrable errback only once on connection failure' do
471
+ errback_called = false
472
+ connection.connect.errback do
473
+ expect(connection.state).to eq(:failed)
474
+
475
+ raise 'Errback already called' if errback_called
476
+ errback_called = true
477
+
478
+ connection.connect.errback do
479
+ EventMachine.add_timer(0.5) { stop_reactor }
480
+ end
481
+ end
482
+ end
483
+ end
484
+
280
485
  context 'when already connected' do
281
486
  it 'does nothing and no further state changes are emitted' do
282
487
  connection.once(:connected) do
@@ -509,12 +714,10 @@ describe Ably::Realtime::Connection, :event_machine do
509
714
  end
510
715
 
511
716
  context 'with an unresponsive connection' do
512
- let(:stubbed_timeout) { 2 }
717
+ let(:custom_timeout) { 2 }
718
+ let(:client_options) { default_options.merge(realtime_request_timeout: custom_timeout) }
513
719
 
514
720
  before do
515
- stub_const 'Ably::Realtime::Connection::ConnectionManager::TIMEOUTS',
516
- Ably::Realtime::Connection::ConnectionManager::TIMEOUTS.merge(close: stubbed_timeout)
517
-
518
721
  connection.on(:connected) do
519
722
  # Prevent all incoming & outgoing ProtocolMessages from being processed by the client library
520
723
  connection.__outgoing_protocol_msgbus__.unsubscribe
@@ -527,7 +730,7 @@ describe Ably::Realtime::Connection, :event_machine do
527
730
  close_requested_at = Time.now
528
731
 
529
732
  connection.on(:closed) do
530
- expect(Time.now - close_requested_at).to be >= stubbed_timeout
733
+ expect(Time.now - close_requested_at).to be >= custom_timeout
531
734
  expect(connection.state).to eq(:closed)
532
735
  expect(events[:error_emitted]).to_not eql(true)
533
736
  expect(events[:closed_message_from_server_received]).to_not eql(true)
@@ -571,6 +774,30 @@ describe Ably::Realtime::Connection, :event_machine do
571
774
  end
572
775
  end
573
776
  end
777
+
778
+ context 'when ping times out' do
779
+ let(:client_options) { default_options.merge(log_level: :error) }
780
+
781
+ it 'logs a warning' do
782
+ connection.once(:connected) do
783
+ allow(connection).to receive(:defaults).and_return(connection.defaults.merge(realtime_request_timeout: 0.0001))
784
+ expect(connection.logger).to receive(:warn).with(/Ping timed out/) do
785
+ stop_reactor
786
+ end
787
+ connection.ping
788
+ end
789
+ end
790
+
791
+ it 'yields to the block with a nil value' do
792
+ connection.once(:connected) do
793
+ allow(connection).to receive(:defaults).and_return(connection.defaults.merge(realtime_request_timeout: 0.0001))
794
+ connection.ping do |time_elapsed|
795
+ expect(time_elapsed).to be_nil
796
+ stop_reactor
797
+ end
798
+ end
799
+ end
800
+ end
574
801
  end
575
802
 
576
803
  context 'recovery' do
@@ -580,15 +807,14 @@ describe Ably::Realtime::Connection, :event_machine do
580
807
  auto_close Ably::Realtime::Client.new(client_options)
581
808
  end
582
809
  let(:publishing_client_channel) { publishing_client.channel(channel_name) }
583
- let(:client_options) { default_options.merge(log_level: :fatal) }
584
-
585
- before do
586
- # Reconfigure client library retry periods and timeouts so that tests run quickly
587
- stub_const 'Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG',
588
- Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG.merge(
589
- disconnected: { retry_every: 0.1, max_time_in_state: 0.2 },
590
- suspended: { retry_every: 0.1, max_time_in_state: 0.2 },
591
- )
810
+
811
+ let(:client_options) do
812
+ default_options.merge(
813
+ log_level: :none,
814
+ disconnected_retry_timeout: 0.1,
815
+ suspended_retry_timeout: 0.1,
816
+ connection_state_ttl: 0.2
817
+ )
592
818
  end
593
819
 
594
820
  describe '#recovery_key' do
@@ -597,7 +823,6 @@ describe Ably::Realtime::Connection, :event_machine do
597
823
  end
598
824
  let(:available_states) { self.class.available_states }
599
825
  let(:states) { Hash.new }
600
- let(:client_options) { default_options.merge(log_level: :none) }
601
826
  let(:channel) { client.channel(random_str) }
602
827
 
603
828
  it 'is composed of connection key and serial that is kept up to date with each message ACK received' do
@@ -641,6 +866,11 @@ describe Ably::Realtime::Connection, :event_machine do
641
866
  end
642
867
  end
643
868
 
869
+ connection.once(:suspended) do
870
+ error_message = Ably::Models::ProtocolMessage.new(action: 9, error: { message: 'force failure' })
871
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, error_message
872
+ end
873
+
644
874
  connection.once(:failed) do
645
875
  expect(states.keys).to match_array(available_states)
646
876
  stop_reactor
@@ -659,8 +889,6 @@ describe Ably::Realtime::Connection, :event_machine do
659
889
 
660
890
  context "opening a new connection using a recently disconnected connection's #recovery_key" do
661
891
  context 'connection#id and connection#key after recovery' do
662
- let(:client_options) { default_options.merge(log_level: :none) }
663
-
664
892
  it 'remains the same' do
665
893
  previous_connection_id = nil
666
894
  previous_connection_key = nil
@@ -701,8 +929,6 @@ describe Ably::Realtime::Connection, :event_machine do
701
929
 
702
930
  context 'when messages have been sent whilst the old connection is disconnected' do
703
931
  describe 'the new connection' do
704
- let(:client_options) { default_options.merge(log_level: :none) }
705
-
706
932
  it 'recovers server-side queued messages' do
707
933
  channel.attach do
708
934
  connection.transition_state_machine! :failed
@@ -774,7 +1000,7 @@ describe Ably::Realtime::Connection, :event_machine do
774
1000
 
775
1001
  it 'opens each with a unique connection#id and connection#key' do
776
1002
  connection_count.times.map do
777
- Ably::Realtime::Client.new(client_options)
1003
+ auto_close Ably::Realtime::Client.new(client_options)
778
1004
  end.each do |client|
779
1005
  client.connection.on(:connected) do
780
1006
  connection_ids << client.connection.id
@@ -943,15 +1169,13 @@ describe Ably::Realtime::Connection, :event_machine do
943
1169
  end
944
1170
 
945
1171
  context 'when connection enters the :suspended state' do
946
- let(:client_options) { default_options.merge(:log_level => :fatal) }
947
-
948
- before do
949
- # Reconfigure client library retry periods so that client stays in suspended state
950
- stub_const 'Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG',
951
- Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG.merge(
952
- disconnected: { retry_every: 0.01, max_time_in_state: 0.05 },
953
- suspended: { retry_every: 60, max_time_in_state: 60 }
954
- )
1172
+ let(:client_options) do
1173
+ default_options.merge(
1174
+ log_level: :fatal,
1175
+ disconnected_retry_timeout: 0.02,
1176
+ suspended_retry_timeout: 60,
1177
+ connection_state_ttl: 0.05
1178
+ )
955
1179
  end
956
1180
 
957
1181
  it 'detaches the channels and prevents publishing of messages on those channels' do
@@ -132,7 +132,6 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
132
132
  let(:client_options) { default_options.merge(client_id: client_id) }
133
133
 
134
134
  it 'contains a #client_id attribute' do
135
- skip 'Waiting for issue #256 to be resolved'
136
135
  when_all(channel.attach, other_client_channel.attach) do
137
136
  other_client_channel.subscribe('event') do |message|
138
137
  expect(message.client_id).to eql(client_id)
@@ -208,14 +207,26 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
208
207
  end
209
208
  end
210
209
 
211
- it 'will not echo messages to the client from other REST clients publishing using that connection_ID', em_timeout: 10 do
212
- skip 'Waiting on realtime#285 to be resolved'
210
+ it 'will not echo messages to the client from other REST clients publishing using that connection_key', em_timeout: 10 do
213
211
  no_echo_channel.attach do
214
212
  no_echo_channel.subscribe('test_event') do |message|
215
213
  fail "Message should not have been echoed back"
216
214
  end
217
215
 
218
- rest_client.channel(channel_name).publish('test_event', nil, connection_id: no_echo_client.connection.id)
216
+ rest_client.channel(channel_name).publish('test_event', nil, connection_key: no_echo_client.connection.key)
217
+ EventMachine.add_timer(1.5) do
218
+ stop_reactor
219
+ end
220
+ end
221
+ end
222
+
223
+ it 'will echo messages with a valid connection_id to the client from other REST clients publishing using that connection_key', em_timeout: 10 do
224
+ channel.attach do
225
+ channel.subscribe('test_event') do |message|
226
+ expect(message.connection_id).to eql(client.connection.id)
227
+ end
228
+
229
+ rest_client.channel(channel_name).publish('test_event', nil, connection_key: client.connection.key)
219
230
  EventMachine.add_timer(1.5) do
220
231
  stop_reactor
221
232
  end
@@ -599,7 +610,7 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
599
610
  let(:event_name) { random_str }
600
611
  let(:message_state) { [] }
601
612
  let(:connection) { client.connection }
602
- let(:client_options) { default_options.merge(:log_level => :none) }
613
+ let(:client_options) { default_options.merge(:log_level => :fatal) }
603
614
  let(:msgs_received) { [] }
604
615
 
605
616
  it 'publishes the message again, later receives the ACK and only one message is ever received from Ably' do
@@ -642,6 +653,32 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
642
653
  let(:connection) { client.connection }
643
654
  let(:event_name) { random_str }
644
655
 
656
+ describe 'the connection is not resumed' do
657
+ let(:client_options) { default_options.merge(:log_level => :fatal) }
658
+
659
+ it 'calls the errback for all messages' do
660
+ connection.once(:connected) do
661
+ connection.transport.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
662
+ if protocol_message.messages.find { |message| message.name == event_name }
663
+ EventMachine.add_timer(0.0001) do
664
+ connection.transport.unbind # trigger failure
665
+ connection.configure_new '0123456789abcdef', 'wVIsgTHAB1UvXh7z-1991d8586', -1 # force the resume connection key to be invalid
666
+ end
667
+ end
668
+ end
669
+ end
670
+
671
+ channel.publish(event_name).tap do |deferrable|
672
+ deferrable.callback do
673
+ raise 'Message delivery should not happen'
674
+ end
675
+ deferrable.errback do
676
+ stop_reactor
677
+ end
678
+ end
679
+ end
680
+ end
681
+
645
682
  describe 'the connection becomes suspended' do
646
683
  let(:client_options) { default_options.merge(:log_level => :fatal) }
647
684
 
@@ -649,8 +686,10 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
649
686
  connection.once(:connected) do
650
687
  connection.transport.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
651
688
  if protocol_message.messages.find { |message| message.name == event_name }
652
- EventMachine.add_timer(0.001) do
689
+ EventMachine.add_timer(0.0001) do
653
690
  connection.transition_state_machine :suspended
691
+ stub_const 'Ably::FALLBACK_HOSTS', []
692
+ allow(client).to receive(:endpoint).and_return(URI::Generic.build(scheme: 'wss', host: 'does.not.exist.com'))
654
693
  end
655
694
  end
656
695
  end
@@ -71,6 +71,10 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
71
71
  end
72
72
 
73
73
  context 'and two pages of messages' do
74
+ let(:wildcard_token) { Proc.new { Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') } }
75
+ let(:client_one) { auto_close Ably::Realtime::Client.new(default_options.merge(auth_callback: wildcard_token)) }
76
+ let(:client_two) { auto_close Ably::Realtime::Client.new(default_options.merge(auth_callback: wildcard_token)) }
77
+
74
78
  it 'retrieves two pages of messages before channel was attached' do
75
79
  when_all(*10.times.map { |i| presence_client_two.enter_client("client:#{i}", data: presence_data_before_attach) }) do
76
80
  when_all(*10.times.map { |i| presence_client_one.enter_client("client:#{i}", data: presence_data_after_attach) }) do