ably 1.1.7 → 1.1.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/COPYRIGHT +1 -1
  4. data/SPEC.md +0 -7
  5. data/ably.gemspec +1 -1
  6. data/lib/ably/models/connection_details.rb +8 -2
  7. data/lib/ably/models/delta_extras.rb +29 -0
  8. data/lib/ably/models/error_info.rb +6 -2
  9. data/lib/ably/models/message.rb +11 -0
  10. data/lib/ably/models/protocol_message.rb +5 -8
  11. data/lib/ably/realtime/channel/channel_state_machine.rb +5 -1
  12. data/lib/ably/realtime/channel/publisher.rb +3 -2
  13. data/lib/ably/realtime/channel.rb +2 -0
  14. data/lib/ably/realtime/connection/connection_manager.rb +13 -4
  15. data/lib/ably/realtime/connection/connection_state_machine.rb +4 -0
  16. data/lib/ably/realtime/connection.rb +0 -3
  17. data/lib/ably/rest/channel.rb +4 -3
  18. data/lib/ably/rest/client.rb +16 -4
  19. data/lib/ably/version.rb +1 -1
  20. data/spec/acceptance/realtime/channel_spec.rb +212 -7
  21. data/spec/acceptance/realtime/connection_failures_spec.rb +56 -1
  22. data/spec/acceptance/realtime/connection_spec.rb +249 -0
  23. data/spec/acceptance/realtime/presence_spec.rb +18 -1
  24. data/spec/acceptance/rest/channel_spec.rb +73 -11
  25. data/spec/acceptance/rest/channels_spec.rb +1 -1
  26. data/spec/support/test_app.rb +1 -1
  27. data/spec/unit/models/delta_extras_spec.rb +14 -0
  28. data/spec/unit/models/error_info_spec.rb +17 -1
  29. data/spec/unit/models/message_spec.rb +24 -0
  30. data/spec/unit/models/protocol_message_spec.rb +24 -20
  31. data/spec/unit/realtime/channel_spec.rb +2 -1
  32. data/spec/unit/realtime/channels_spec.rb +3 -3
  33. data/spec/unit/rest/channel_spec.rb +40 -7
  34. data/spec/unit/rest/client_spec.rb +27 -0
  35. metadata +7 -4
@@ -750,6 +750,107 @@ describe Ably::Realtime::Channel, :event_machine do
750
750
  end
751
751
  end
752
752
 
753
+ describe '#(RTL17)' do
754
+ context 'when channel is initialized' do
755
+ it 'sends messages only on attach' do
756
+ expect(channel).to be_initialized
757
+ channel.publish('event', payload)
758
+
759
+ channel.subscribe do |message|
760
+ stop_reactor if message.data == payload && channel.attached?
761
+ end
762
+
763
+ channel.attach
764
+ end
765
+ end
766
+
767
+ context 'when channel is attaching' do
768
+ it 'sends messages only on attach' do
769
+ channel.publish('event', payload)
770
+
771
+ sent_message = nil
772
+ channel.subscribe do |message|
773
+ return if message.data != payload
774
+ sent_message = message
775
+
776
+ stop_reactor if channel.attached?
777
+ end
778
+
779
+ channel.on(:attaching) do
780
+ expect(channel).to be_attaching
781
+ expect(sent_message).to be_nil
782
+ end
783
+
784
+ channel.attach
785
+ end
786
+ end
787
+
788
+ context 'when channel is detaching' do
789
+ it 'stops sending message' do
790
+ sent_message = nil
791
+ event_published = false
792
+ channel.subscribe do |message|
793
+ sent_message = message if message.data == payload
794
+ end
795
+
796
+ channel.on(:detaching) do
797
+ channel.publish('event', payload)
798
+ event_published = true
799
+ end
800
+
801
+ channel.on(:detaching) do
802
+ EventMachine.next_tick do
803
+ expect(sent_message).to be_nil
804
+ stop_reactor if event_published
805
+ end
806
+ end
807
+
808
+ channel.attach do
809
+ channel.detach
810
+ end
811
+ end
812
+ end
813
+
814
+ context 'when channel is detached' do
815
+ it 'stops sending message' do
816
+ sent_message = nil
817
+ event_published = false
818
+ channel.subscribe do |message|
819
+ sent_message = message if message.data == payload
820
+ end
821
+
822
+ channel.on(:detaching) do
823
+ channel.publish('event', payload)
824
+ event_published = true
825
+ end
826
+
827
+ channel.on(:detached) do
828
+ expect(sent_message).to be_nil
829
+ stop_reactor if event_published
830
+ end
831
+
832
+ channel.attach do
833
+ channel.detach
834
+ end
835
+ end
836
+ end
837
+
838
+ context 'when channel is failed' do
839
+ it 'errors when trying to send a message' do
840
+ channel.once(:failed) do
841
+ channel.publish('event', payload).errback do |error|
842
+ expect(error).to be_a(Ably::Exceptions::ChannelInactive)
843
+ stop_reactor
844
+ end
845
+ end
846
+
847
+ channel.attach do
848
+ channel.transition_state_machine(:failed)
849
+ end
850
+ end
851
+ end
852
+ end
853
+
753
854
  context 'when channel is not attached in state Initializing (#RTL6c1)' do
754
855
  it 'publishes messages immediately and does not implicitly attach (#RTL6c1)' do
755
856
  sub_channel.attach do
@@ -1181,7 +1282,7 @@ describe Ably::Realtime::Channel, :event_machine do
1181
1282
  end
1182
1283
 
1183
1284
  context 'with more than allowed messages in a single publish' do
1184
- let(:channel_name) { random_str }
1285
+ 65536
1185
1286
 
1186
1287
  it 'rejects the publish' do
1187
1288
  messages = (Ably::Realtime::Connection::MAX_PROTOCOL_MESSAGE_BATCH_SIZE + 1).times.map do
@@ -1405,15 +1506,98 @@ describe Ably::Realtime::Channel, :event_machine do
1405
1506
  end
1406
1507
 
1407
1508
  context 'message size exceeded (#TO3l8)' do
1408
- let(:message) { 'x' * 700000 }
1409
-
1410
1509
  let(:client) { auto_close Ably::Realtime::Client.new(client_options) }
1411
1510
  let(:channel) { client.channels.get(channel_name) }
1412
1511
 
1413
- it 'should not allow to send a message' do
1414
- channel.publish('event', message).errback do |error|
1415
- expect(error).to be_instance_of(Ably::Exceptions::MaxMessageSizeExceeded)
1416
- stop_reactor
1512
+ context 'and max_message_size is default (65536 bytes)' do
1513
+ let(:channel_name) { random_str }
1514
+ let(:max_message_size) { 65536 }
1515
+
1516
+ it 'should allow to send a message (32 bytes)' do
1517
+ client.connection.once(:connected) do
1518
+ channel.subscribe('event') do |msg|
1519
+ expect(msg.data).to eq('x' * 32)
1520
+ stop_reactor
1521
+ end
1522
+ channel.publish('event', 'x' * 32)
1523
+ end
1524
+ end
1525
+
1526
+ it 'should not allow to send a message (700000 bytes)' do
1527
+ client.connection.once(:connected) do
1528
+ connection_details = Ably::Models::ConnectionDetails.new(
1529
+ client.connection.details.attributes.attributes.merge('maxMessageSize' => max_message_size)
1530
+ )
1531
+ client.connection.set_connection_details(connection_details)
1532
+ expect(client.connection.details.max_message_size).to eq(65536)
1533
+ channel.publish('event', 'x' * 700000).errback do |error|
1534
+ expect(error).to be_instance_of(Ably::Exceptions::MaxMessageSizeExceeded)
1535
+ stop_reactor
1536
+ end
1537
+ end
1538
+ end
1539
+ end
1540
+
1541
+ context 'and max_message_size is customized (11 bytes)' do
1542
+ let(:max_message_size) { 11 }
1543
+
1544
+ context 'and the message size is 30 bytes' do
1545
+ let(:channel_name) { random_str }
1546
+
1547
+ it 'should not allow to send a message' do
1548
+ client.connection.once(:connected) do
1549
+ connection_details = Ably::Models::ConnectionDetails.new(
1550
+ client.connection.details.attributes.attributes.merge('maxMessageSize' => max_message_size)
1551
+ )
1552
+ client.connection.set_connection_details(connection_details)
1553
+ expect(client.connection.details.max_message_size).to eq(11)
1554
+ channel.publish('event', 'x' * 30).errback do |error|
1555
+ expect(error).to be_instance_of(Ably::Exceptions::MaxMessageSizeExceeded)
1556
+ stop_reactor
1557
+ end
1558
+ end
1559
+ end
1560
+ end
1561
+ end
1562
+
1563
+ context 'and max_message_size is nil' do
1564
+ let(:max_message_size) { nil }
1565
+
1566
+ context 'and the message size is 30 bytes' do
1567
+ let(:channel_name) { random_str }
1568
+
1569
+ it 'should allow to send a message' do
1570
+ client.connection.once(:connected) do
1571
+ connection_details = Ably::Models::ConnectionDetails.new(
1572
+ client.connection.details.attributes.attributes.merge('maxMessageSize' => max_message_size)
1573
+ )
1574
+ client.connection.set_connection_details(connection_details)
1575
+ expect(client.connection.details.max_message_size).to eq(65536)
1576
+ channel.subscribe('event') do |msg|
1577
+ expect(msg.data).to eq('x' * 30)
1578
+ stop_reactor
1579
+ end
1580
+ channel.publish('event', 'x' * 30)
1581
+ end
1582
+ end
1583
+ end
1584
+
1585
+ context 'and the message size is 65537 bytes' do
1586
+ let(:channel_name) { random_str }
1587
+
1588
+ it 'should not allow to send a message' do
1589
+ client.connection.once(:connected) do
1590
+ connection_details = Ably::Models::ConnectionDetails.new(
1591
+ client.connection.details.attributes.attributes.merge('maxMessageSize' => max_message_size)
1592
+ )
1593
+ client.connection.set_connection_details(connection_details)
1594
+ expect(client.connection.details.max_message_size).to eq(65536)
1595
+ channel.publish('event', 'x' * 65537).errback do |error|
1596
+ expect(error).to be_instance_of(Ably::Exceptions::MaxMessageSizeExceeded)
1597
+ stop_reactor
1598
+ end
1599
+ end
1600
+ end
1417
1601
  end
1418
1602
  end
1419
1603
  end
@@ -2224,6 +2408,27 @@ describe Ably::Realtime::Channel, :event_machine do
2224
2408
  channel.transition_state_machine! :suspended
2225
2409
  end
2226
2410
  end
2411
+
2412
+ context 'when connection is no longer connected' do
2413
+ it 'will not attempt to reattach (#RTL13c)' do
2414
+ channel.attach do
2415
+ connection.once(:closing) do
2416
+ channel.once(:attaching) do |state_change|
2417
+ raise 'Channel should not attempt to reattach'
2418
+ end
2419
+
2420
+ channel.transition_state_machine! :suspended
2421
+ end
2422
+
2423
+ connection.once(:closed) do
2424
+ expect(channel).to be_suspended
2425
+ stop_reactor
2426
+ end
2427
+
2428
+ connection.close
2429
+ end
2430
+ end
2431
+ end
2227
2432
  end
2228
2433
 
2229
2434
  context 'and channel is attaching' do
@@ -570,7 +570,7 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
570
570
  end
571
571
 
572
572
  context 'when DISCONNECTED ProtocolMessage received from the server' do
573
- it 'reconnects automatically and immediately' do
573
+ it 'reconnects automatically and immediately (#RTN15a)' do
574
574
  fail_if_suspended_or_failed
575
575
 
576
576
  connection.once(:connected) do
@@ -590,6 +590,61 @@ describe Ably::Realtime::Connection, 'failures', :event_machine do
590
590
  end
591
591
  end
592
592
 
593
+ context 'when protocolMessage contains token error' do
594
+ context "library does not have a means to renew the token (#RTN15h1)" do
595
+ let(:auth_url) { 'https://echo.ably.io/createJWT' }
596
+ let(:token) { Faraday.get("#{auth_url}?keyName=#{key_name}&keySecret=#{key_secret}").body }
597
+ let(:client_options) { default_options.merge(token: token, log_level: :none) }
598
+
599
+ let(:error_message) { 'error_message' }
600
+
601
+ it 'moves connection state to failed' do
602
+ connection.on(:failed) do |connection_state_change|
603
+ expect(connection.error_reason.message).to eq(error_message)
604
+ stop_reactor
605
+ end
606
+
607
+ connection.on(:connected) do
608
+ protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Disconnected.to_i, error: { code: 40140, message: error_message })
609
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
610
+ end
611
+
612
+ connection.connect
613
+ end
614
+ end
615
+
616
+ context "library have a means to renew the token (#RTN15h2)" do
617
+ let(:client_options) { default_options.merge(log_level: :none) }
618
+ let(:error_message) { 'error_message' }
619
+
620
+ def send_disconnect_message
621
+ protocol_message = Ably::Models::ProtocolMessage.new(action: Ably::Models::ProtocolMessage::ACTION.Disconnected.to_i, error: { code: 40140, message: error_message })
622
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
623
+ end
624
+
625
+ it 'attempts to reconnect' do
626
+ connection.on(:failed) do |connection_state_change|
627
+ raise "Connection shouldn't be failed"
628
+ end
629
+
630
+ connection.on(:connected) do
631
+ connection.once(:connecting) do
632
+ connection.once(:disconnected) do
633
+ expect(connection.error_reason.message).to eq(error_message)
634
+ stop_reactor
635
+ end
636
+
637
+ send_disconnect_message
638
+ end
639
+
640
+ send_disconnect_message
641
+ end
642
+
643
+ connection.connect
644
+ end
645
+ end
646
+ end
647
+
593
648
  context 'connection state freshness is monitored' do
594
649
  it 'resumes connections when disconnected within the connection_state_ttl period (#RTN15g)' do
595
650
  connection.once(:connected) do
@@ -482,6 +482,118 @@ describe Ably::Realtime::Connection, :event_machine do
482
482
  end
483
483
  end
484
484
 
485
+ context 'when explicitly reconnecting disconnected/suspended connection in retry (#RTN11c)' do
486
+ let(:close_connection_proc) do
487
+ lambda do
488
+ EventMachine.add_timer(0.001) do
489
+ if connection.transport.nil?
490
+ close_connection_proc.call
491
+ else
492
+ connection.transport.close_connection_after_writing
493
+ end
494
+ end
495
+ end
496
+ end
497
+
498
+ context 'when suspended' do
499
+ let(:suspended_retry_timeout) { 60 }
500
+ let(:client_options) do
501
+ default_options.merge(
502
+ disconnected_retry_timeout: 0.02,
503
+ suspended_retry_timeout: suspended_retry_timeout,
504
+ connection_state_ttl: 0
505
+ )
506
+ end
507
+
508
+ it 'reconnects immediately' do
509
+ connection.once(:connecting) { close_connection_proc.call }
510
+
511
+ connection.on(:suspended) do |connection_state_change|
512
+ if connection_state_change.retry_in.zero?
513
+ # Exhausting immediate retries
514
+ connection.once(:connecting) { close_connection_proc.call }
515
+ else
516
+ suspended_at = Time.now.to_f
517
+ connection.on(:connected) do
518
+ expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
519
+ expect(connection.state).to eq(:connected)
520
+ expect(Time.now.to_f).to be_within(4).of(suspended_at)
521
+ stop_reactor
522
+ end
523
+ end
524
+
525
+ connection.connect
526
+ end
527
+
528
+ connection.connect
529
+ end
530
+ end
531
+
532
+ context 'when disconnected' do
533
+ let(:retry_timeout) { 60 }
534
+ let(:client_options) do
535
+ default_options.merge(
536
+ disconnected_retry_timeout: retry_timeout,
537
+ suspended_retry_timeout: retry_timeout,
538
+ connection_state_ttl: 0
539
+ )
540
+ end
541
+
542
+ it 'reconnects immediately' do
543
+ connection.once(:connected) do
544
+ connection.on(:disconnected) do |connection_state_change|
545
+ disconnected_at = Time.now.to_f
546
+ connection.on(:connected) do
547
+ if connection_state_change.retry_in.zero?
548
+ # Exhausting immediate retries
549
+ close_connection_proc.call
550
+ else
551
+ expect(connection_state_change.retry_in).to eq(retry_timeout)
552
+ expect(connection.state).to eq(:connected)
553
+ expect(Time.now.to_f).to be_within(4).of(disconnected_at)
554
+ stop_reactor
555
+ end
556
+ end
557
+ connection.connect
558
+ end
559
+
560
+ close_connection_proc.call
561
+ end
562
+
563
+ connection.connect
564
+ end
565
+ end
566
+ end
567
+
568
+ context 'when reconnecting a failed connection' do
569
+ let(:channel) { client.channel(random_str) }
570
+ let(:client_options) { default_options.merge(log_level: :none) }
571
+
572
+ it 'transitions all channels state to initialized and cleares error_reason' do
573
+ connection.on(:failed) do |connection_state_change|
574
+ expect(connection.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
575
+ expect(channel.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
576
+ expect(channel).to be_failed
577
+
578
+ connection.on(:connected) do
579
+ expect(connection.error_reason).to eq(nil)
580
+ expect(channel).to be_initialized
581
+ expect(channel.error_reason).to eq(nil)
582
+ stop_reactor
583
+ end
584
+
585
+ connection.connect
586
+ end
587
+
588
+ connection.connect do
589
+ channel.attach do
590
+ error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
591
+ client.connection.manager.error_received_from_server error
592
+ end
593
+ end
594
+ end
595
+ end
596
+
485
597
  context 'with invalid auth details' do
486
598
  let(:client_options) { default_options.merge(key: 'this.is:invalid', log_level: :none) }
487
599
 
@@ -693,6 +805,18 @@ describe Ably::Realtime::Connection, :event_machine do
693
805
  end
694
806
 
695
807
  context '#close' do
808
+ let(:close_connection_proc) do
809
+ lambda do
810
+ EventMachine.add_timer(0.001) do
811
+ if connection.transport.nil?
812
+ close_connection_proc.call
813
+ else
814
+ connection.transport.close_connection_after_writing
815
+ end
816
+ end
817
+ end
818
+ end
819
+
696
820
  it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
697
821
  connection.connect do
698
822
  expect(connection.close).to be_a(Ably::Util::SafeDeferrable)
@@ -754,6 +878,56 @@ describe Ably::Realtime::Connection, :event_machine do
754
878
  end
755
879
  end
756
880
 
881
+ context ':connecting RTN12f' do
882
+ context ":connected does not arrive when trying to close" do
883
+ it 'moves to closed' do
884
+ connection.on(:closed) do |state_change|
885
+ state_changes = connection.state_history.map { |t| t[:state].to_sym }
886
+
887
+ expect(state_changes).to eq([:connecting, :closing, :closed])
888
+ stop_reactor
889
+ end
890
+
891
+ connection.on(:connecting) do
892
+ connection.close
893
+ connection.__outgoing_protocol_msgbus__.unsubscribe
894
+ end
895
+
896
+ connection.connect
897
+ end
898
+ end
899
+
900
+ context ":connected arrive when trying to close" do
901
+ let(:protocol_message_attributes) do
902
+ {
903
+ action: Ably::Models::ProtocolMessage::ACTION.Connected.to_i,
904
+ connection_serial: 55,
905
+ connection_details: {
906
+ max_idle_interval: 2 * 1000
907
+ }
908
+ }
909
+ end
910
+
911
+ it 'moves to connected and then to closed' do
912
+ connection.on(:closed) do |state_change|
913
+ state_changes = connection.state_history.map { |t| t[:state].to_sym }
914
+
915
+ expect(state_changes).to eq([:connecting, :connected, :closing, :closed])
916
+ stop_reactor
917
+ end
918
+
919
+ connection.on(:connecting) do
920
+ connection.__outgoing_protocol_msgbus__.unsubscribe
921
+
922
+ connection.__incoming_protocol_msgbus__.publish :protocol_message, Ably::Models::ProtocolMessage.new(protocol_message_attributes)
923
+ connection.close
924
+ end
925
+
926
+ connection.connect
927
+ end
928
+ end
929
+ end
930
+
757
931
  context ':connected' do
758
932
  it 'changes the connection state to :closing and waits for the server to confirm connection is :closed with a ProtocolMessage' do
759
933
  connection.on(:connected) do
@@ -800,6 +974,81 @@ describe Ably::Realtime::Connection, :event_machine do
800
974
  end
801
975
  end
802
976
  end
977
+
978
+ context ':suspended RTN12d' do
979
+ let(:suspended_retry_timeout) { 60 }
980
+ let(:client_options) do
981
+ default_options.merge(
982
+ disconnected_retry_timeout: 0.02,
983
+ suspended_retry_timeout: suspended_retry_timeout,
984
+ connection_state_ttl: 0
985
+ )
986
+ end
987
+
988
+ it 'immediatly closes connection' do
989
+ connection.on(:connecting) { close_connection_proc.call }
990
+ connection.on(:suspended) do |connection_state_change|
991
+ if connection_state_change.retry_in.zero?
992
+ # Exhausting immediate retries
993
+ connection.once(:connecting) { close_connection_proc.call }
994
+ else
995
+ suspended_at = Time.now.to_f
996
+ connection.on(:closed) do
997
+ expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
998
+ expect(connection.state).to eq(:closed)
999
+ expect(Time.now.to_f).to be_within(4).of(suspended_at)
1000
+ stop_reactor
1001
+ end
1002
+
1003
+ connection.close
1004
+ end
1005
+
1006
+ connection.connect
1007
+ end
1008
+
1009
+ connection.connect
1010
+ end
1011
+ end
1012
+
1013
+ context ':disconnected RTN12d' do
1014
+ let(:retry_timeout) { 60 }
1015
+ let(:client_options) do
1016
+ default_options.merge(
1017
+ disconnected_retry_timeout: retry_timeout,
1018
+ suspended_retry_timeout: retry_timeout,
1019
+ connection_state_ttl: 0
1020
+ )
1021
+ end
1022
+
1023
+ it 'immediatly closes connection' do
1024
+ connection.once(:connected) do
1025
+ connection.on(:disconnected) do |connection_state_change|
1026
+ disconnected_at = Time.now.to_f
1027
+ connection.on(:connected) do
1028
+ if connection_state_change.retry_in.zero?
1029
+ # Exhausting immediate retries
1030
+ close_connection_proc.call
1031
+ else
1032
+ connection.once(:closed) do
1033
+ expect(connection_state_change.retry_in).to eq(retry_timeout)
1034
+ expect(connection.state).to eq(:closed)
1035
+ expect(Time.now.to_f).to be_within(4).of(disconnected_at)
1036
+ stop_reactor
1037
+ end
1038
+
1039
+ connection.close
1040
+ end
1041
+ end
1042
+
1043
+ connection.connect
1044
+ end
1045
+
1046
+ close_connection_proc.call
1047
+ end
1048
+
1049
+ connection.connect
1050
+ end
1051
+ end
803
1052
  end
804
1053
  end
805
1054
 
@@ -2634,7 +2634,24 @@ describe Ably::Realtime::Presence, :event_machine do
2634
2634
  end
2635
2635
  end
2636
2636
 
2637
- context 'channel state side effects' do
2637
+ context 'channel state side effects (RTP5)' do
2638
+ context 'channel transitions to the ATTACHED state (RTP5b)' do
2639
+ it 'all queued presence messages are sent' do
2640
+ channel_client_one.on(:attached) do
2641
+ client_one.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
2642
+ if protocol_message.action == :presence
2643
+ expect(protocol_message.action).to eq(:presence)
2644
+ stop_reactor
2645
+ end
2646
+ end
2647
+ end
2648
+
2649
+ presence_client_one.enter do
2650
+ channel_client_one.attach
2651
+ end
2652
+ end
2653
+ end
2654
+
2638
2655
  context 'channel transitions to the FAILED state' do
2639
2656
  let(:anonymous_client) { auto_close Ably::Realtime::Client.new(client_options.merge(log_level: :fatal)) }
2640
2657
  let(:client_one) { auto_close Ably::Realtime::Client.new(client_options.merge(client_id: client_one_id, log_level: :fatal)) }