ably 0.7.0 → 0.7.1

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.
Files changed (49) hide show
  1. data/.travis.yml +2 -2
  2. data/Rakefile +2 -0
  3. data/SPEC.md +230 -194
  4. data/ably.gemspec +2 -0
  5. data/lib/ably/auth.rb +7 -5
  6. data/lib/ably/models/idiomatic_ruby_wrapper.rb +5 -7
  7. data/lib/ably/models/paginated_resource.rb +14 -21
  8. data/lib/ably/models/protocol_message.rb +1 -1
  9. data/lib/ably/modules/ably.rb +4 -0
  10. data/lib/ably/modules/async_wrapper.rb +2 -2
  11. data/lib/ably/modules/channels_collection.rb +31 -8
  12. data/lib/ably/modules/conversions.rb +10 -0
  13. data/lib/ably/modules/enum.rb +2 -3
  14. data/lib/ably/modules/state_emitter.rb +8 -8
  15. data/lib/ably/modules/state_machine.rb +7 -3
  16. data/lib/ably/realtime/channel.rb +6 -5
  17. data/lib/ably/realtime/channel/channel_manager.rb +11 -10
  18. data/lib/ably/realtime/channel/channel_state_machine.rb +10 -9
  19. data/lib/ably/realtime/channels.rb +3 -0
  20. data/lib/ably/realtime/client/incoming_message_dispatcher.rb +11 -1
  21. data/lib/ably/realtime/connection.rb +55 -16
  22. data/lib/ably/realtime/connection/connection_manager.rb +25 -8
  23. data/lib/ably/realtime/connection/connection_state_machine.rb +9 -9
  24. data/lib/ably/realtime/connection/websocket_transport.rb +2 -2
  25. data/lib/ably/realtime/presence.rb +16 -17
  26. data/lib/ably/util/crypto.rb +1 -1
  27. data/lib/ably/version.rb +1 -1
  28. data/spec/acceptance/realtime/channel_history_spec.rb +6 -5
  29. data/spec/acceptance/realtime/connection_failures_spec.rb +103 -27
  30. data/spec/acceptance/realtime/connection_spec.rb +81 -17
  31. data/spec/acceptance/realtime/presence_spec.rb +82 -30
  32. data/spec/acceptance/rest/auth_spec.rb +22 -19
  33. data/spec/acceptance/rest/client_spec.rb +4 -4
  34. data/spec/acceptance/rest/presence_spec.rb +12 -6
  35. data/spec/rspec_config.rb +9 -0
  36. data/spec/shared/model_behaviour.rb +1 -1
  37. data/spec/spec_helper.rb +4 -1
  38. data/spec/support/event_machine_helper.rb +26 -37
  39. data/spec/support/markdown_spec_formatter.rb +96 -68
  40. data/spec/support/rest_testapp_before_retry.rb +15 -0
  41. data/spec/support/test_app.rb +4 -0
  42. data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +20 -2
  43. data/spec/unit/models/message_spec.rb +1 -1
  44. data/spec/unit/models/paginated_resource_spec.rb +15 -1
  45. data/spec/unit/modules/enum_spec.rb +10 -0
  46. data/spec/unit/realtime/channels_spec.rb +30 -0
  47. data/spec/unit/rest/channels_spec.rb +30 -0
  48. metadata +101 -35
  49. checksums.yaml +0 -7
@@ -192,7 +192,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
192
192
  it 'is reset to nil when :connected' do
193
193
  connection.once(:disconnected) do |error|
194
194
  # stub the host so that the connection connects
195
- allow(connection).to receive(:host).and_return(TestApp.instance.host)
195
+ allow(connection).to receive(:determine_host).and_yield(TestApp.instance.realtime_host)
196
196
  connection.once(:connected) do
197
197
  expect(connection.error_reason).to be_nil
198
198
  stop_reactor
@@ -393,6 +393,59 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
393
393
  end
394
394
  end
395
395
  end
396
+
397
+ context 'when failing to resume because the connection_key is not or no longer valid' do
398
+ def kill_connection_transport_and_prevent_valid_resume
399
+ connection.transport.close_connection_after_writing
400
+ connection.update_connection_id_and_key '0123456789abcdef', '0123456789abcdef' # force the resume connection key to be invalid
401
+ end
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
407
+
408
+ connection.once(:connected) do
409
+ expect(connection.key).to_not eql(previous_connection_key)
410
+ expect(connection.id).to_not eql(previous_connection_id)
411
+ stop_reactor
412
+ end
413
+
414
+ kill_connection_transport_and_prevent_valid_resume
415
+ end
416
+ end
417
+
418
+ it 'detaches all channels' do
419
+ channel_count = 10
420
+ channels = channel_count.times.map { |index| client.channel("channel-#{index}") }
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)
428
+ stop_reactor
429
+ end
430
+ end
431
+
432
+ kill_connection_transport_and_prevent_valid_resume
433
+ end
434
+ end
435
+
436
+ it 'emits an error on the channel and sets the error reason' do
437
+ client.channel(random_str).attach do |channel|
438
+ channel.on(:error) do |error|
439
+ expect(error.message).to match(/Invalid connection key/i)
440
+ expect(error.code).to eql(80008)
441
+ expect(channel.error_reason).to eql(error)
442
+ stop_reactor
443
+ end
444
+
445
+ kill_connection_transport_and_prevent_valid_resume
446
+ end
447
+ end
448
+ end
396
449
  end
397
450
 
398
451
  describe 'fallback host feature' do
@@ -456,41 +509,64 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
456
509
 
457
510
  let(:fallback_hosts_used) { Array.new }
458
511
 
459
- it 'uses a fallback host on every subsequent disconnected attempt until suspended' do
460
- request = 0
461
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_one_state).times do |host|
462
- if request == 0
512
+ context 'when the Internet is down' do
513
+ before do
514
+ allow(connection).to receive(:internet_up?).and_yield(false)
515
+ end
516
+
517
+ it 'never uses a fallback host' do
518
+ expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
463
519
  expect(host).to eql(expected_host)
464
- else
465
- expect(custom_hosts).to include(host)
466
- fallback_hosts_used << host
520
+ raise EventMachine::ConnectionError
467
521
  end
468
- request += 1
469
- raise EventMachine::ConnectionError
470
- end
471
522
 
472
- connection.on(:suspended) do
473
- expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
474
- stop_reactor
523
+ connection.on(:failed) do
524
+ stop_reactor
525
+ end
475
526
  end
476
527
  end
477
528
 
478
- it 'uses the primary host when suspended, and a fallback host on every subsequent suspended attempt' do
479
- request = 0
480
- expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
481
- if request == 0 || request == expected_retry_attempts + 1
482
- expect(host).to eql(expected_host)
483
- else
484
- expect(custom_hosts).to include(host)
485
- fallback_hosts_used << host
529
+ context 'when the Internet is up' do
530
+ before do
531
+ allow(connection).to receive(:internet_up?).and_yield(true)
532
+ end
533
+
534
+ it 'uses a fallback host on every subsequent disconnected attempt until suspended' do
535
+ request = 0
536
+ expect(EventMachine).to receive(:connect).exactly(retry_count_for_one_state).times do |host|
537
+ if request == 0
538
+ expect(host).to eql(expected_host)
539
+ else
540
+ expect(custom_hosts).to include(host)
541
+ fallback_hosts_used << host
542
+ end
543
+ request += 1
544
+ raise EventMachine::ConnectionError
545
+ end
546
+
547
+ connection.on(:suspended) do
548
+ expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
549
+ stop_reactor
486
550
  end
487
- request += 1
488
- raise EventMachine::ConnectionError
489
551
  end
490
552
 
491
- connection.on(:failed) do
492
- expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
493
- stop_reactor
553
+ it 'uses the primary host when suspended, and a fallback host on every subsequent suspended attempt' do
554
+ request = 0
555
+ expect(EventMachine).to receive(:connect).exactly(retry_count_for_all_states).times do |host|
556
+ if request == 0 || request == expected_retry_attempts + 1
557
+ expect(host).to eql(expected_host)
558
+ else
559
+ expect(custom_hosts).to include(host)
560
+ fallback_hosts_used << host
561
+ end
562
+ request += 1
563
+ raise EventMachine::ConnectionError
564
+ end
565
+
566
+ connection.on(:failed) do
567
+ expect(fallback_hosts_used.uniq).to match_array(custom_hosts)
568
+ stop_reactor
569
+ end
494
570
  end
495
571
  end
496
572
  end
@@ -93,7 +93,7 @@ describe Ably::Realtime::Connection, :event_machine do
93
93
  it 'renews the token on connect' do
94
94
  sleep ttl + 0.1
95
95
  expect(client.auth.current_token).to be_expired
96
- expect(client.auth).to receive(:authorise).once.and_call_original
96
+ expect(client.auth).to receive(:authorise).at_least(:once).and_call_original
97
97
  connection.once(:connected) do
98
98
  expect(client.auth.current_token).to_not be_expired
99
99
  stop_reactor
@@ -105,7 +105,7 @@ describe Ably::Realtime::Connection, :event_machine do
105
105
  let(:ttl) { 0.01 }
106
106
 
107
107
  it 'renews the token on connect, and only makes one subsequent attempt to obtain a new token' do
108
- expect(client.auth).to receive(:authorise).twice.and_call_original
108
+ expect(client.auth).to receive(:authorise).at_least(:twice).and_call_original
109
109
  connection.once(:disconnected) do
110
110
  connection.once(:failed) do |error|
111
111
  expect(error.code).to eql(40140) # token expired
@@ -117,7 +117,10 @@ describe Ably::Realtime::Connection, :event_machine do
117
117
  it 'uses the primary host for subsequent connection and auth requests' do
118
118
  EventMachine.add_timer(1) do # wait for token to expire
119
119
  connection.once(:disconnected) do
120
- expect(client.rest_client.connection).to receive(:post).with(/requestToken$/, anything).and_call_original
120
+ expect(client.rest_client.connection).to receive(:post).
121
+ with(/requestToken$/, anything).
122
+ at_least(:once).
123
+ and_call_original
121
124
 
122
125
  expect(client.rest_client).to_not receive(:fallback_connection)
123
126
  expect(client).to_not receive(:fallback_endpoint)
@@ -139,23 +142,25 @@ describe Ably::Realtime::Connection, :event_machine do
139
142
 
140
143
  context 'the server' do
141
144
  it 'disconnects the client, and the client automatically renews the token and then reconnects', em_timeout: 10 do
142
- expect(client.auth.current_token).to_not be_expired
143
-
144
- channel.attach
145
145
  original_token = client.auth.current_token
146
+ expect(original_token).to_not be_expired
146
147
 
147
148
  connection.once(:connected) do
148
149
  started_at = Time.now
149
150
  connection.once(:disconnected) do |error|
150
- expect(Time.now - started_at >= ttl)
151
- expect(original_token).to be_expired
152
- expect(error.code).to eql(40140) # token expired
153
- connection.once(:connected) do
154
- expect(client.auth.current_token).to_not be_expired
155
- stop_reactor
151
+ EventMachine.add_timer(1) do # allow 1 second
152
+ expect(Time.now - started_at >= ttl)
153
+ expect(original_token).to be_expired
154
+ expect(error.code).to eql(40140) # token expired
155
+ connection.once(:connected) do
156
+ expect(client.auth.current_token).to_not be_expired
157
+ stop_reactor
158
+ end
156
159
  end
157
160
  end
158
161
  end
162
+
163
+ channel.attach
159
164
  end
160
165
  end
161
166
 
@@ -601,13 +606,28 @@ describe Ably::Realtime::Connection, :event_machine do
601
606
  end
602
607
  end
603
608
 
604
- context 'with invalid value' do
605
- let(:client_options) { default_options.merge(recover: 'invalid:key', log_level: :fatal) }
609
+ context 'with invalid formatted value sent to server' do
610
+ let(:client_options) { default_options.merge(recover: 'not-a-valid-connection-key:1', log_level: :none) }
606
611
 
607
- skip 'triggers an error on the connection object, sets the #error_reason and connects anyway' do
608
- connection.on(:error) do |error|
612
+ it 'triggers a fatal error on the connection object, sets the #error_reason and disconnects' do
613
+ connection.once(:error) do |error|
614
+ expect(connection.state).to eq(:failed)
615
+ expect(connection.error_reason.message).to match(/Invalid connection key/)
616
+ expect(connection.error_reason.code).to eql(40006)
617
+ expect(connection.error_reason).to eql(error)
618
+ stop_reactor
619
+ end
620
+ end
621
+ end
622
+
623
+ context 'with expired (missing) value sent to server' do
624
+ let(:client_options) { default_options.merge(recover: '0123456789abcdef:0', log_level: :fatal) }
625
+
626
+ it 'triggers an error on the connection object, sets the #error_reason, yet will connect anyway' do
627
+ connection.once(:error) do |error|
609
628
  expect(connection.state).to eq(:connected)
610
- expect(connection.error_reason.message).to match(/Recover/)
629
+ expect(connection.error_reason.message).to match(/Invalid connection key/i)
630
+ expect(connection.error_reason.code).to eql(80008)
611
631
  expect(connection.error_reason).to eql(error)
612
632
  stop_reactor
613
633
  end
@@ -652,5 +672,49 @@ describe Ably::Realtime::Connection, :event_machine do
652
672
  end
653
673
  end
654
674
  end
675
+
676
+
677
+ context 'undocumented method' do
678
+ context '#internet_up?' do
679
+ it 'returns a Deferrable' do
680
+ expect(connection.internet_up?).to be_a(EventMachine::Deferrable)
681
+ stop_reactor
682
+ end
683
+
684
+ context 'when the Internet is up' do
685
+ it 'calls the block with true' do
686
+ connection.internet_up? do |result|
687
+ expect(result).to be_truthy
688
+ stop_reactor
689
+ end
690
+ end
691
+
692
+ it 'calls the success callback of the Deferrable' do
693
+ connection.internet_up?.callback do
694
+ stop_reactor
695
+ end
696
+ end
697
+ end
698
+
699
+ context 'when the Internet is down' do
700
+ before do
701
+ stub_const 'Ably::INTERNET_CHECK', { url: 'http://does.not.exist.com', ok_text: 'no.way.this.will.match' }
702
+ end
703
+
704
+ it 'calls the block with false' do
705
+ connection.internet_up? do |result|
706
+ expect(result).to be_falsey
707
+ stop_reactor
708
+ end
709
+ end
710
+
711
+ it 'calls the failure callback of the Deferrable' do
712
+ connection.internet_up?.errback do
713
+ stop_reactor
714
+ end
715
+ end
716
+ end
717
+ end
718
+ end
655
719
  end
656
720
  end
@@ -256,6 +256,16 @@ describe Ably::Realtime::Presence, :event_machine do
256
256
  end
257
257
  end
258
258
 
259
+ it 'updates the data to nil if :data argument is not provided (assumes nil value)' do
260
+ presence_client_one.enter(data: 'prior') do
261
+ presence_client_one.update
262
+ end
263
+ presence_client_one.subscribe(:update) do |message|
264
+ expect(message.data).to be_nil
265
+ stop_reactor
266
+ end
267
+ end
268
+
259
269
  it 'returns a Deferrable' do
260
270
  presence_client_one.enter do
261
271
  expect(presence_client_one.update).to be_a(EventMachine::Deferrable)
@@ -277,10 +287,11 @@ describe Ably::Realtime::Presence, :event_machine do
277
287
  context '#leave' do
278
288
  context ':data option' do
279
289
  let(:data) { random_str }
290
+ let(:enter_data) { random_str }
280
291
 
281
292
  context 'when set to a string' do
282
293
  it 'emits the new data for the leave event' do
283
- presence_client_one.enter data: random_str do
294
+ presence_client_one.enter data: enter_data do
284
295
  presence_client_one.leave data: data
285
296
  end
286
297
 
@@ -292,26 +303,26 @@ describe Ably::Realtime::Presence, :event_machine do
292
303
  end
293
304
 
294
305
  context 'when set to nil' do
295
- it 'emits nil data for the leave event' do
296
- presence_client_one.enter data: random_str do
306
+ it 'emits the previously defined value as a convenience' do
307
+ presence_client_one.enter data: enter_data do
297
308
  presence_client_one.leave data: nil
298
309
  end
299
310
 
300
311
  presence_client_one.subscribe(:leave) do |presence_message|
301
- expect(presence_message.data).to be_nil
312
+ expect(presence_message.data).to eql(enter_data)
302
313
  stop_reactor
303
314
  end
304
315
  end
305
316
  end
306
317
 
307
318
  context 'when not passed as an argument' do
308
- it 'emits the original data for the leave event' do
309
- presence_client_one.enter data: data do
319
+ it 'emits the previously defined value as a convenience' do
320
+ presence_client_one.enter data: enter_data do
310
321
  presence_client_one.leave
311
322
  end
312
323
 
313
324
  presence_client_one.subscribe(:leave) do |presence_message|
314
- expect(presence_message.data).to eql(data)
325
+ expect(presence_message.data).to eql(enter_data)
315
326
  stop_reactor
316
327
  end
317
328
  end
@@ -380,7 +391,7 @@ describe Ably::Realtime::Presence, :event_machine do
380
391
  presence_client_one.on(:entered) { raise 'Should not have entered' }
381
392
  next unless client_id == client_count - 1
382
393
 
383
- EventMachine.add_timer(0.5) do
394
+ EventMachine.add_timer(1) do
384
395
  expect(presence_client_one.state).to eq(:initialized)
385
396
  stop_reactor
386
397
  end
@@ -435,7 +446,7 @@ describe Ably::Realtime::Presence, :event_machine do
435
446
  clients << presence
436
447
  next unless clients.count == 5
437
448
 
438
- EventMachine.add_timer(0.5) do
449
+ wait_until(proc { updated_callback_count == 5 }) do
439
450
  expect(clients.map(&:client_id).uniq.count).to eql(5)
440
451
  expect(updated_callback_count).to eql(5)
441
452
  stop_reactor
@@ -443,6 +454,18 @@ describe Ably::Realtime::Presence, :event_machine do
443
454
  end
444
455
  end
445
456
 
457
+ it 'updates the data attribute to null for the member when :data option is not provided (assumed null)' do
458
+ presence_client_one.enter_client('client_1') do
459
+ presence_client_one.update_client('client_1')
460
+ end
461
+
462
+ presence_anonymous_client.subscribe(:update) do |presence|
463
+ expect(presence.client_id).to eql('client_1')
464
+ expect(presence.data).to be_nil
465
+ stop_reactor
466
+ end
467
+ end
468
+
446
469
  it 'enters if not already entered' do
447
470
  updated_callback_count = 0
448
471
 
@@ -457,7 +480,7 @@ describe Ably::Realtime::Presence, :event_machine do
457
480
  clients << presence
458
481
  next unless clients.count == 5
459
482
 
460
- EventMachine.add_timer(0.5) do
483
+ wait_until(proc { updated_callback_count == 5 }) do
461
484
  expect(clients.map(&:client_id).uniq.count).to eql(5)
462
485
  expect(updated_callback_count).to eql(5)
463
486
  stop_reactor
@@ -498,7 +521,7 @@ describe Ably::Realtime::Presence, :event_machine do
498
521
  clients << presence
499
522
  next unless clients.count == 5
500
523
 
501
- EventMachine.add_timer(0.5) do
524
+ wait_until(proc { left_callback_count == 5 }) do
502
525
  expect(clients.map(&:client_id).uniq.count).to eql(5)
503
526
  expect(left_callback_count).to eql(5)
504
527
  stop_reactor
@@ -520,7 +543,7 @@ describe Ably::Realtime::Presence, :event_machine do
520
543
  clients << presence
521
544
  next unless clients.count == 5
522
545
 
523
- EventMachine.add_timer(1) do
546
+ wait_until(proc { left_callback_count == 5 }) do
524
547
  expect(clients.map(&:client_id).uniq.count).to eql(5)
525
548
  expect(left_callback_count).to eql(5)
526
549
  stop_reactor
@@ -543,20 +566,20 @@ describe Ably::Realtime::Presence, :event_machine do
543
566
  end
544
567
 
545
568
  context 'with a nil value in :data option' do
546
- it 'emits the leave event with a nil value' do
569
+ it 'emits the leave event with the previous value as a convenience' do
547
570
  presence_client_one.enter_client("client:unique", data: data) do
548
571
  presence_client_one.leave_client("client:unique", data: nil)
549
572
  end
550
573
 
551
574
  presence_client_one.subscribe(:leave) do |presence_message|
552
- expect(presence_message.data).to be_nil
575
+ expect(presence_message.data).to eql(data)
553
576
  stop_reactor
554
577
  end
555
578
  end
556
579
  end
557
580
 
558
581
  context 'with no :data option' do
559
- it 'emits the leave event with the previous data value' do
582
+ it 'emits the leave event with the previous value as a convenience' do
560
583
  presence_client_one.enter_client("client:unique", data: data) do
561
584
  presence_client_one.leave_client("client:unique")
562
585
  end
@@ -612,7 +635,14 @@ describe Ably::Realtime::Presence, :event_machine do
612
635
  end
613
636
 
614
637
  it 'filters by connection_id option if provided' do
615
- when_all(presence_client_one.enter, presence_client_two.enter, and_wait: 0.5) do
638
+ presence_client_one.enter do
639
+ presence_client_two.enter
640
+ end
641
+
642
+ presence_client_one.subscribe(:enter) do |presence_message|
643
+ # wait until the client_two enter event has been sent to client_one
644
+ next unless presence_message.client_id == client_two.client_id
645
+
616
646
  presence_client_one.get(connection_id: client_one.connection.id) do |members|
617
647
  expect(members.count).to eq(1)
618
648
  expect(members.first.connection_id).to eql(client_one.connection.id)
@@ -627,7 +657,14 @@ describe Ably::Realtime::Presence, :event_machine do
627
657
  end
628
658
 
629
659
  it 'filters by client_id option if provided' do
630
- when_all(presence_client_one.enter(client_id: 'one'), presence_client_two.enter(client_id: 'two')) do
660
+ presence_client_one.enter(client_id: 'one') do
661
+ presence_client_two.enter client_id: 'two'
662
+ end
663
+
664
+ presence_client_one.subscribe(:enter) do |presence_message|
665
+ # wait until the client_two enter event has been sent to client_one
666
+ next unless presence_message.client_id == 'two'
667
+
631
668
  presence_client_one.get(client_id: 'one') do |members|
632
669
  expect(members.count).to eq(1)
633
670
  expect(members.first.client_id).to eql('one')
@@ -665,21 +702,36 @@ describe Ably::Realtime::Presence, :event_machine do
665
702
  end
666
703
  end
667
704
 
668
- it 'returns both members on both simultaneously connected clients' do
669
- when_all(presence_client_one.enter(data: data_payload), presence_client_two.enter) do
670
- EventMachine.add_timer(0.5) do
671
- presence_client_one.get do |client_one_members|
672
- presence_client_two.get do |client_two_members|
673
- expect(client_one_members.count).to eq(client_two_members.count)
705
+ context 'with lots of members on different clients' do
706
+ let(:members_per_client) { 10 }
707
+ let(:clients_entered) { Hash.new { |hash, key| hash[key] = 0 } }
708
+ let(:total_members) { members_per_client * 2 }
674
709
 
675
- member_client_one = client_one_members.find { |presence| presence.client_id == client_one.client_id }
676
- member_client_two = client_one_members.find { |presence| presence.client_id == client_two.client_id }
710
+ it 'returns a complete list of members on all clients' do
711
+ members_per_client.times do |index|
712
+ presence_client_one.enter_client("client_1:#{index}")
713
+ presence_client_two.enter_client("client_2:#{index}")
714
+ end
677
715
 
678
- expect(member_client_one).to be_a(Ably::Models::PresenceMessage)
679
- expect(member_client_one.data).to eql(data_payload)
680
- expect(member_client_two).to be_a(Ably::Models::PresenceMessage)
716
+ presence_client_one.subscribe(:enter) do
717
+ clients_entered[:client_one] += 1
718
+ end
681
719
 
682
- stop_reactor
720
+ presence_client_two.subscribe(:enter) do
721
+ clients_entered[:client_two] += 1
722
+ end
723
+
724
+ wait_until(proc { clients_entered[:client_one] + clients_entered[:client_two] == total_members * 2 }) do
725
+ presence_anonymous_client.get do |anonymous_members|
726
+ expect(anonymous_members.count).to eq(total_members)
727
+ expect(anonymous_members.map(&:client_id).uniq.count).to eq(total_members)
728
+
729
+ presence_client_one.get do |client_one_members|
730
+ presence_client_two.get do |client_two_members|
731
+ expect(client_one_members.count).to eq(total_members)
732
+ expect(client_one_members.count).to eq(client_two_members.count)
733
+ stop_reactor
734
+ end
683
735
  end
684
736
  end
685
737
  end
@@ -720,7 +772,7 @@ describe Ably::Realtime::Presence, :event_machine do
720
772
  presence_client_one.enter
721
773
  presence_client_one.update
722
774
  presence_client_one.leave do
723
- EventMachine.add_timer(0.5) do
775
+ EventMachine.add_timer(1) do
724
776
  stop_reactor
725
777
  end
726
778
  end