ably 0.7.2 → 0.7.4
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.txt +1 -1
- data/README.md +107 -24
- data/SPEC.md +531 -398
- data/lib/ably/auth.rb +23 -15
- data/lib/ably/exceptions.rb +9 -0
- data/lib/ably/models/message.rb +17 -9
- data/lib/ably/models/paginated_resource.rb +12 -8
- data/lib/ably/models/presence_message.rb +18 -10
- data/lib/ably/models/protocol_message.rb +15 -4
- data/lib/ably/modules/async_wrapper.rb +4 -3
- data/lib/ably/modules/event_emitter.rb +31 -2
- data/lib/ably/modules/message_emitter.rb +77 -0
- data/lib/ably/modules/safe_deferrable.rb +71 -0
- data/lib/ably/modules/safe_yield.rb +41 -0
- data/lib/ably/modules/state_emitter.rb +28 -8
- data/lib/ably/realtime.rb +0 -5
- data/lib/ably/realtime/channel.rb +24 -29
- data/lib/ably/realtime/channel/channel_manager.rb +54 -11
- data/lib/ably/realtime/channel/channel_state_machine.rb +21 -6
- data/lib/ably/realtime/client.rb +7 -2
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +29 -26
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +4 -4
- data/lib/ably/realtime/connection.rb +41 -9
- data/lib/ably/realtime/connection/connection_manager.rb +72 -24
- data/lib/ably/realtime/connection/connection_state_machine.rb +26 -4
- data/lib/ably/realtime/connection/websocket_transport.rb +19 -6
- data/lib/ably/realtime/presence.rb +74 -208
- data/lib/ably/realtime/presence/members_map.rb +264 -0
- data/lib/ably/realtime/presence/presence_manager.rb +59 -0
- data/lib/ably/realtime/presence/presence_state_machine.rb +64 -0
- data/lib/ably/rest/channel.rb +1 -1
- data/lib/ably/rest/client.rb +6 -2
- data/lib/ably/rest/presence.rb +1 -1
- data/lib/ably/util/pub_sub.rb +3 -1
- data/lib/ably/util/safe_deferrable.rb +18 -0
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +2 -2
- data/spec/acceptance/realtime/channel_spec.rb +28 -6
- data/spec/acceptance/realtime/connection_failures_spec.rb +116 -46
- data/spec/acceptance/realtime/connection_spec.rb +55 -10
- data/spec/acceptance/realtime/message_spec.rb +32 -0
- data/spec/acceptance/realtime/presence_spec.rb +456 -96
- data/spec/acceptance/realtime/stats_spec.rb +2 -2
- data/spec/acceptance/realtime/time_spec.rb +2 -2
- data/spec/acceptance/rest/auth_spec.rb +75 -7
- data/spec/shared/client_initializer_behaviour.rb +8 -0
- data/spec/shared/safe_deferrable_behaviour.rb +71 -0
- data/spec/support/api_helper.rb +1 -1
- data/spec/support/event_machine_helper.rb +1 -1
- data/spec/support/test_app.rb +13 -7
- data/spec/unit/models/message_spec.rb +15 -14
- data/spec/unit/models/paginated_resource_spec.rb +4 -4
- data/spec/unit/models/presence_message_spec.rb +17 -17
- data/spec/unit/models/stat_spec.rb +4 -4
- data/spec/unit/modules/async_wrapper_spec.rb +28 -9
- data/spec/unit/modules/event_emitter_spec.rb +50 -0
- data/spec/unit/modules/state_emitter_spec.rb +76 -2
- data/spec/unit/realtime/channel_spec.rb +51 -20
- data/spec/unit/realtime/channels_spec.rb +3 -3
- data/spec/unit/realtime/connection_spec.rb +30 -0
- data/spec/unit/realtime/presence_spec.rb +52 -26
- data/spec/unit/realtime/safe_deferrable_spec.rb +12 -0
- metadata +85 -39
- checksums.yaml +0 -7
- data/.ruby-version.old +0 -1
data/lib/ably/version.rb
CHANGED
@@ -17,10 +17,10 @@ describe Ably::Realtime::Channel, '#history', :event_machine do
|
|
17
17
|
|
18
18
|
let(:options) { { :protocol => :json } }
|
19
19
|
|
20
|
-
it 'returns a
|
20
|
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
21
21
|
channel.publish('event', payload) do |message|
|
22
22
|
history = channel.history
|
23
|
-
expect(history).to be_a(
|
23
|
+
expect(history).to be_a(Ably::Util::SafeDeferrable)
|
24
24
|
history.callback do |messages|
|
25
25
|
expect(messages.count).to eql(1)
|
26
26
|
expect(messages).to be_a(Ably::Models::PaginatedResource)
|
@@ -84,12 +84,12 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
it 'returns a
|
88
|
-
expect(channel.attach).to be_a(
|
87
|
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
88
|
+
expect(channel.attach).to be_a(Ably::Util::SafeDeferrable)
|
89
89
|
stop_reactor
|
90
90
|
end
|
91
91
|
|
92
|
-
it 'calls the
|
92
|
+
it 'calls the SafeDeferrable callback on success' do
|
93
93
|
channel.attach.callback do |channel|
|
94
94
|
expect(channel).to be_a(Ably::Realtime::Channel)
|
95
95
|
expect(channel.state).to eq(:attached)
|
@@ -195,6 +195,26 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
195
195
|
stop_reactor
|
196
196
|
end
|
197
197
|
end
|
198
|
+
|
199
|
+
context 'and subsequent authorisation with suitable permissions' do
|
200
|
+
it 'attaches to the channel successfully and resets the channel error_reason' do
|
201
|
+
restricted_channel.attach
|
202
|
+
restricted_channel.once(:failed) do
|
203
|
+
restricted_client.close do
|
204
|
+
# A direct call to #authorise is synchronous
|
205
|
+
restricted_client.auth.authorise(api_key: api_key)
|
206
|
+
|
207
|
+
restricted_client.connect do
|
208
|
+
restricted_channel.once(:attached) do
|
209
|
+
expect(restricted_channel.error_reason).to be_nil
|
210
|
+
stop_reactor
|
211
|
+
end
|
212
|
+
restricted_channel.attach
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
198
218
|
end
|
199
219
|
end
|
200
220
|
|
@@ -230,9 +250,11 @@ describe Ably::Realtime::Channel, :event_machine do
|
|
230
250
|
end
|
231
251
|
end
|
232
252
|
|
233
|
-
it 'returns a
|
234
|
-
|
235
|
-
|
253
|
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
254
|
+
channel.attach do
|
255
|
+
expect(channel.detach).to be_a(Ably::Util::SafeDeferrable)
|
256
|
+
stop_reactor
|
257
|
+
end
|
236
258
|
end
|
237
259
|
|
238
260
|
it 'calls the Deferrable callback on success' do
|
@@ -66,7 +66,8 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
66
66
|
)
|
67
67
|
end
|
68
68
|
|
69
|
-
|
69
|
+
# retry immediately after failure, then one retry every :retry_every_for_tests
|
70
|
+
let(:expected_retry_attempts) { 1 + (max_time_in_state_for_tests / retry_every_for_tests).round }
|
70
71
|
let(:state_changes) { Hash.new { |hash, key| hash[key] = 0 } }
|
71
72
|
let(:timer) { Hash.new }
|
72
73
|
|
@@ -288,21 +289,76 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
288
289
|
end
|
289
290
|
|
290
291
|
context 'when DISCONNECTED ProtocolMessage received from the server' do
|
291
|
-
it 'reconnects automatically' do
|
292
|
+
it 'reconnects automatically and immediately' do
|
292
293
|
fail_if_suspended_or_failed
|
293
294
|
|
294
295
|
connection.once(:connected) do
|
295
296
|
connection.once(:disconnected) do
|
296
|
-
|
297
|
-
|
298
|
-
expect(
|
299
|
-
|
297
|
+
disconnected_at = Time.now.to_f
|
298
|
+
connection.once(:connecting) do
|
299
|
+
expect(Time.now.to_f).to be_within(0.25).of(disconnected_at)
|
300
|
+
connection.once(:connected) do
|
301
|
+
state_history = connection.state_history.map { |transition| transition[:state].to_sym }
|
302
|
+
expect(state_history).to eql([:connecting, :connected, :disconnected, :connecting, :connected])
|
303
|
+
stop_reactor
|
304
|
+
end
|
300
305
|
end
|
301
306
|
end
|
302
307
|
protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Disconnected.to_i)
|
303
308
|
connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
304
309
|
end
|
305
310
|
end
|
311
|
+
|
312
|
+
context 'and subsequently fails to reconnect' do
|
313
|
+
let(:retry_every) { 1.5 }
|
314
|
+
|
315
|
+
before do
|
316
|
+
# Reconfigure client library retry periods and timeouts so that tests run quickly
|
317
|
+
stub_const 'Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG',
|
318
|
+
Ably::Realtime::Connection::ConnectionManager::CONNECT_RETRY_CONFIG.merge(
|
319
|
+
disconnected: { retry_every: retry_every, max_time_in_state: 60 })
|
320
|
+
end
|
321
|
+
|
322
|
+
it "retries every CONNECT_RETRY_CONFIG[:disconnected][:retry_every] seconds" do
|
323
|
+
fail_if_suspended_or_failed
|
324
|
+
|
325
|
+
stubbed_first_attempt = false
|
326
|
+
|
327
|
+
connection.once(:connected) do
|
328
|
+
connection.once(:disconnected) do
|
329
|
+
connection.once(:connecting) do
|
330
|
+
connection.once(:disconnected) do
|
331
|
+
disconnected_at = Time.now.to_f
|
332
|
+
connection.once(:connecting) do
|
333
|
+
expect(Time.now.to_f - disconnected_at).to be > retry_every
|
334
|
+
state_history = connection.state_history.map { |transition| transition[:state].to_sym }
|
335
|
+
expect(state_history).to eql([:connecting, :connected, :disconnected, :connecting, :disconnected, :connecting])
|
336
|
+
|
337
|
+
# allow one more recoonect when reactor stopped
|
338
|
+
expect(connection.manager).to receive(:reconnect_transport)
|
339
|
+
stop_reactor
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
# When reconnect called simply open the transport and close immediately
|
344
|
+
expect(connection.manager).to receive(:reconnect_transport) do
|
345
|
+
next if stubbed_first_attempt
|
346
|
+
|
347
|
+
connection.manager.setup_transport do
|
348
|
+
EventMachine.next_tick do
|
349
|
+
connection.transport.unbind
|
350
|
+
stubbed_first_attempt = true
|
351
|
+
end
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Disconnected.to_i)
|
358
|
+
connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
306
362
|
end
|
307
363
|
|
308
364
|
context 'when websocket transport is closed' do
|
@@ -359,6 +415,16 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
359
415
|
end
|
360
416
|
end
|
361
417
|
|
418
|
+
it 'triggers the resume callback', api_private: true do
|
419
|
+
channel.attach do
|
420
|
+
connection.transport.close_connection_after_writing
|
421
|
+
connection.on_resume do
|
422
|
+
expect(connection).to be_connected
|
423
|
+
stop_reactor
|
424
|
+
end
|
425
|
+
end
|
426
|
+
end
|
427
|
+
|
362
428
|
context 'when messages were published whilst the client was disconnected' do
|
363
429
|
it 'receives the messages published whilst offline' do
|
364
430
|
messages_received = false
|
@@ -394,55 +460,57 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
394
460
|
end
|
395
461
|
end
|
396
462
|
|
397
|
-
context 'when failing to resume
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
it 'updates the connection_id and connection_key' do
|
404
|
-
connection.once(:connected) do
|
405
|
-
previous_connection_id = connection.id
|
406
|
-
previous_connection_key = connection.key
|
463
|
+
context 'when failing to resume' do
|
464
|
+
context 'because the connection_key is not or no longer valid' do
|
465
|
+
def kill_connection_transport_and_prevent_valid_resume
|
466
|
+
connection.transport.close_connection_after_writing
|
467
|
+
connection.configure_new '0123456789abcdef', '0123456789abcdef', -1 # force the resume connection key to be invalid
|
468
|
+
end
|
407
469
|
|
470
|
+
it 'updates the connection_id and connection_key' do
|
408
471
|
connection.once(:connected) do
|
409
|
-
|
410
|
-
|
411
|
-
stop_reactor
|
412
|
-
end
|
413
|
-
|
414
|
-
kill_connection_transport_and_prevent_valid_resume
|
415
|
-
end
|
416
|
-
end
|
472
|
+
previous_connection_id = connection.id
|
473
|
+
previous_connection_key = connection.key
|
417
474
|
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
when_all(*channels.map(&:attach)) do
|
422
|
-
detached_channels = []
|
423
|
-
channels.each do |channel|
|
424
|
-
channel.on(:detached) do
|
425
|
-
detached_channels << channel
|
426
|
-
next unless detached_channels.count == channel_count
|
427
|
-
expect(detached_channels.count).to eql(channel_count)
|
475
|
+
connection.once(:connected) do
|
476
|
+
expect(connection.key).to_not eql(previous_connection_key)
|
477
|
+
expect(connection.id).to_not eql(previous_connection_id)
|
428
478
|
stop_reactor
|
429
479
|
end
|
430
|
-
end
|
431
480
|
|
432
|
-
|
481
|
+
kill_connection_transport_and_prevent_valid_resume
|
482
|
+
end
|
433
483
|
end
|
434
|
-
end
|
435
484
|
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
485
|
+
it 'detaches all channels' do
|
486
|
+
channel_count = 10
|
487
|
+
channels = channel_count.times.map { |index| client.channel("channel-#{index}") }
|
488
|
+
when_all(*channels.map(&:attach)) do
|
489
|
+
detached_channels = []
|
490
|
+
channels.each do |channel|
|
491
|
+
channel.on(:detached) do
|
492
|
+
detached_channels << channel
|
493
|
+
next unless detached_channels.count == channel_count
|
494
|
+
expect(detached_channels.count).to eql(channel_count)
|
495
|
+
stop_reactor
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
kill_connection_transport_and_prevent_valid_resume
|
443
500
|
end
|
501
|
+
end
|
444
502
|
|
445
|
-
|
503
|
+
it 'emits an error on the channel and sets the error reason' do
|
504
|
+
client.channel(random_str).attach do |channel|
|
505
|
+
channel.on(:error) do |error|
|
506
|
+
expect(error.message).to match(/Invalid connection key/i)
|
507
|
+
expect(error.code).to eql(80008)
|
508
|
+
expect(channel.error_reason).to eql(error)
|
509
|
+
stop_reactor
|
510
|
+
end
|
511
|
+
|
512
|
+
kill_connection_transport_and_prevent_valid_resume
|
513
|
+
end
|
446
514
|
end
|
447
515
|
end
|
448
516
|
end
|
@@ -461,7 +529,9 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
|
|
461
529
|
)
|
462
530
|
end
|
463
531
|
|
464
|
-
|
532
|
+
# Retry immediately and then wait retry_every before every subsequent attempt
|
533
|
+
let(:expected_retry_attempts) { 1 + (max_time_in_state_for_tests / retry_every_for_tests).round }
|
534
|
+
|
465
535
|
let(:retry_count_for_one_state) { 1 + expected_retry_attempts } # initial connect then disconnected
|
466
536
|
let(:retry_count_for_all_states) { 1 + expected_retry_attempts * 2 } # initial connection, disconnected & then suspended
|
467
537
|
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'spec_helper'
|
3
|
+
require 'ostruct'
|
3
4
|
|
4
5
|
describe Ably::Realtime::Connection, :event_machine do
|
5
6
|
let(:connection) { client.connection }
|
@@ -148,14 +149,12 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
148
149
|
connection.once(:connected) do
|
149
150
|
started_at = Time.now
|
150
151
|
connection.once(:disconnected) do |error|
|
151
|
-
|
152
|
+
connection.once(:connected) do
|
153
|
+
expect(client.auth.current_token).to_not be_expired
|
152
154
|
expect(Time.now - started_at >= ttl)
|
153
155
|
expect(original_token).to be_expired
|
154
156
|
expect(error.code).to eql(40140) # token expired
|
155
|
-
|
156
|
-
expect(client.auth.current_token).to_not be_expired
|
157
|
-
stop_reactor
|
158
|
-
end
|
157
|
+
stop_reactor
|
159
158
|
end
|
160
159
|
end
|
161
160
|
end
|
@@ -234,8 +233,8 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
234
233
|
end
|
235
234
|
|
236
235
|
context '#connect' do
|
237
|
-
it 'returns a
|
238
|
-
expect(connection.connect).to be_a(
|
236
|
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
237
|
+
expect(connection.connect).to be_a(Ably::Util::SafeDeferrable)
|
239
238
|
stop_reactor
|
240
239
|
end
|
241
240
|
|
@@ -372,9 +371,9 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
372
371
|
end
|
373
372
|
|
374
373
|
context '#close' do
|
375
|
-
it 'returns a
|
374
|
+
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
376
375
|
connection.connect do
|
377
|
-
expect(connection.close).to be_a(
|
376
|
+
expect(connection.close).to be_a(Ably::Util::SafeDeferrable)
|
378
377
|
stop_reactor
|
379
378
|
end
|
380
379
|
end
|
@@ -504,6 +503,17 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
504
503
|
stop_reactor
|
505
504
|
end
|
506
505
|
end
|
506
|
+
|
507
|
+
context 'with a success block that raises an exception' do
|
508
|
+
it 'catches the exception and logs the error' do
|
509
|
+
connection.on(:connected) do
|
510
|
+
expect(connection.logger).to receive(:error).with(/Forced exception/) do
|
511
|
+
stop_reactor
|
512
|
+
end
|
513
|
+
connection.ping { raise 'Forced exception' }
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
507
517
|
end
|
508
518
|
|
509
519
|
context 'recovery' do
|
@@ -591,7 +601,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
591
601
|
context 'connection#id and connection#key after recovery' do
|
592
602
|
let(:client_options) { default_options.merge(log_level: :none) }
|
593
603
|
|
594
|
-
it '
|
604
|
+
it 'remains the same' do
|
595
605
|
previous_connection_id = nil
|
596
606
|
previous_connection_key = nil
|
597
607
|
|
@@ -610,6 +620,22 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
610
620
|
end
|
611
621
|
end
|
612
622
|
end
|
623
|
+
|
624
|
+
it 'does not trigger a resume callback', api_private: true do
|
625
|
+
connection.once(:connected) do
|
626
|
+
connection.transition_state_machine! :failed
|
627
|
+
end
|
628
|
+
|
629
|
+
connection.once(:failed) do
|
630
|
+
recover_client = Ably::Realtime::Client.new(default_options.merge(recover: client.connection.recovery_key))
|
631
|
+
recover_client.connection.on_resume do
|
632
|
+
raise 'Should not trigger resume callback'
|
633
|
+
end
|
634
|
+
recover_client.connection.on(:connected) do
|
635
|
+
EventMachine.add_timer(0.5) { stop_reactor }
|
636
|
+
end
|
637
|
+
end
|
638
|
+
end
|
613
639
|
end
|
614
640
|
|
615
641
|
context 'when messages have been sent whilst the old connection is disconnected' do
|
@@ -653,6 +679,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
653
679
|
it 'triggers a fatal error on the connection object, sets the #error_reason and disconnects' do
|
654
680
|
connection.once(:error) do |error|
|
655
681
|
expect(connection.state).to eq(:failed)
|
682
|
+
expect(error.message).to match(/Invalid connection key/)
|
656
683
|
expect(connection.error_reason.message).to match(/Invalid connection key/)
|
657
684
|
expect(connection.error_reason.code).to eql(40006)
|
658
685
|
expect(connection.error_reason).to eql(error)
|
@@ -667,6 +694,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
667
694
|
it 'triggers an error on the connection object, sets the #error_reason, yet will connect anyway' do
|
668
695
|
connection.once(:error) do |error|
|
669
696
|
expect(connection.state).to eq(:connected)
|
697
|
+
expect(error.message).to match(/Invalid connection key/i)
|
670
698
|
expect(connection.error_reason.message).to match(/Invalid connection key/i)
|
671
699
|
expect(connection.error_reason.code).to eql(80008)
|
672
700
|
expect(connection.error_reason).to eql(error)
|
@@ -714,6 +742,23 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
714
742
|
end
|
715
743
|
end
|
716
744
|
|
745
|
+
context 'protocol failure' do
|
746
|
+
let(:client_options) { default_options.merge(protocol: :json) }
|
747
|
+
|
748
|
+
context 'receiving an invalid ProtocolMessage' do
|
749
|
+
it 'emits an error on the connection and logs a fatal error message' do
|
750
|
+
connection.connect do
|
751
|
+
connection.transport.send(:driver).emit 'message', OpenStruct.new(data: { action: 500 }.to_json)
|
752
|
+
end
|
753
|
+
|
754
|
+
expect(client.logger).to receive(:fatal).with(/Invalid Protocol Message/)
|
755
|
+
connection.on(:error) do |error|
|
756
|
+
expect(error.message).to match(/Invalid Protocol Message/)
|
757
|
+
stop_reactor
|
758
|
+
end
|
759
|
+
end
|
760
|
+
end
|
761
|
+
end
|
717
762
|
|
718
763
|
context 'undocumented method' do
|
719
764
|
context '#internet_up?' do
|
@@ -195,6 +195,38 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
198
|
+
context 'server incorrectly resends a message that was already received by the client library' do
|
199
|
+
let(:messages_received) { [] }
|
200
|
+
let(:connection) { client.connection }
|
201
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
202
|
+
|
203
|
+
it 'discards the message and logs it as an error to the channel' do
|
204
|
+
first_message_protocol_message = nil
|
205
|
+
connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
206
|
+
first_message_protocol_message ||= protocol_message unless protocol_message.messages.empty?
|
207
|
+
end
|
208
|
+
|
209
|
+
channel.subscribe do |message|
|
210
|
+
messages_received << message
|
211
|
+
if messages_received.count == 2
|
212
|
+
# simulate a duplicate protocol message being received
|
213
|
+
EventMachine.next_tick do
|
214
|
+
connection.__incoming_protocol_msgbus__.publish :protocol_message, first_message_protocol_message
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
2.times { |i| EventMachine.add_timer(i.to_f / 5) { channel.publish('event', 'data') } }
|
219
|
+
|
220
|
+
channel.on(:error) do |error|
|
221
|
+
expect(error.message).to match(/duplicate/)
|
222
|
+
EventMachine.add_timer(0.5) do
|
223
|
+
expect(messages_received.count).to eql(2)
|
224
|
+
stop_reactor
|
225
|
+
end
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
198
230
|
context 'encoding and decoding encrypted messages' do
|
199
231
|
shared_examples 'an Ably encrypter and decrypter' do |item, data|
|
200
232
|
let(:algorithm) { data['algorithm'].upcase }
|