ably 1.1.7 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/check.yml +1 -1
- data/CHANGELOG.md +99 -0
- data/COPYRIGHT +1 -1
- data/README.md +2 -2
- data/SPEC.md +0 -7
- data/UPDATING.md +30 -0
- data/ably.gemspec +11 -24
- data/lib/ably/auth.rb +8 -8
- data/lib/ably/logger.rb +4 -4
- data/lib/ably/models/channel_options.rb +97 -0
- data/lib/ably/models/connection_details.rb +8 -2
- data/lib/ably/models/delta_extras.rb +29 -0
- data/lib/ably/models/device_details.rb +1 -1
- data/lib/ably/models/error_info.rb +6 -2
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +4 -0
- data/lib/ably/models/message.rb +14 -3
- data/lib/ably/models/protocol_message.rb +23 -14
- data/lib/ably/models/token_details.rb +7 -2
- data/lib/ably/models/token_request.rb +1 -1
- data/lib/ably/modules/ably.rb +1 -1
- data/lib/ably/modules/channels_collection.rb +22 -2
- data/lib/ably/modules/conversions.rb +34 -0
- data/lib/ably/realtime/auth.rb +2 -2
- data/lib/ably/realtime/channel/channel_manager.rb +16 -4
- data/lib/ably/realtime/channel/channel_state_machine.rb +10 -1
- data/lib/ably/realtime/channel/publisher.rb +3 -2
- data/lib/ably/realtime/channel.rb +54 -22
- data/lib/ably/realtime/channels.rb +1 -1
- data/lib/ably/realtime/connection/connection_manager.rb +13 -4
- data/lib/ably/realtime/connection/connection_state_machine.rb +4 -0
- data/lib/ably/realtime/connection.rb +0 -3
- data/lib/ably/rest/channel.rb +28 -35
- data/lib/ably/rest/client.rb +23 -8
- data/lib/ably/rest/middleware/encoder.rb +1 -1
- data/lib/ably/rest/middleware/exceptions.rb +1 -1
- data/lib/ably/rest/middleware/external_exceptions.rb +1 -1
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +1 -1
- data/lib/ably/rest/middleware/logger.rb +1 -1
- data/lib/ably/rest/middleware/parse_json.rb +1 -1
- data/lib/ably/rest/middleware/parse_message_pack.rb +1 -1
- data/lib/ably/util/crypto.rb +1 -1
- data/lib/ably/version.rb +2 -2
- data/spec/acceptance/realtime/channel_spec.rb +458 -27
- data/spec/acceptance/realtime/channels_spec.rb +59 -7
- data/spec/acceptance/realtime/connection_failures_spec.rb +56 -1
- data/spec/acceptance/realtime/connection_spec.rb +270 -1
- data/spec/acceptance/realtime/message_spec.rb +77 -0
- data/spec/acceptance/realtime/presence_spec.rb +18 -1
- data/spec/acceptance/rest/auth_spec.rb +18 -0
- data/spec/acceptance/rest/channel_spec.rb +73 -11
- data/spec/acceptance/rest/channels_spec.rb +23 -6
- data/spec/acceptance/rest/client_spec.rb +3 -3
- data/spec/acceptance/rest/message_spec.rb +61 -3
- data/spec/lib/unit/models/channel_options_spec.rb +52 -0
- data/spec/run_parallel_tests +2 -7
- data/spec/support/test_app.rb +1 -1
- data/spec/unit/logger_spec.rb +6 -14
- data/spec/unit/models/delta_extras_spec.rb +14 -0
- data/spec/unit/models/error_info_spec.rb +17 -1
- data/spec/unit/models/message_spec.rb +38 -0
- data/spec/unit/models/protocol_message_spec.rb +77 -27
- data/spec/unit/models/token_details_spec.rb +14 -0
- data/spec/unit/realtime/channel_spec.rb +2 -1
- data/spec/unit/realtime/channels_spec.rb +53 -15
- data/spec/unit/rest/channel_spec.rb +40 -7
- data/spec/unit/rest/channels_spec.rb +81 -14
- data/spec/unit/rest/client_spec.rb +27 -0
- metadata +46 -11
@@ -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,138 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
482
482
|
end
|
483
483
|
end
|
484
484
|
|
485
|
+
context "when can't connect to host" do
|
486
|
+
let(:client_options) { super().merge(realtime_host: 'non-existent.ably.io') }
|
487
|
+
|
488
|
+
it 'logs error on failed connection attempt' do
|
489
|
+
logger_expectation = lambda do |*args, &block|
|
490
|
+
error_message = "Connection to non-existent.ably.io:443 failed"
|
491
|
+
expect(args.concat([block ? block.call : nil]).join(',')).to include(error_message)
|
492
|
+
stop_reactor
|
493
|
+
end
|
494
|
+
|
495
|
+
expect(connection.logger).to receive(:warn, &logger_expectation).at_least(:once)
|
496
|
+
|
497
|
+
connection.on(:connected) do
|
498
|
+
raise "Connection should not succeed"
|
499
|
+
end
|
500
|
+
|
501
|
+
connection.connect
|
502
|
+
end
|
503
|
+
end
|
504
|
+
|
505
|
+
context 'when explicitly reconnecting disconnected/suspended connection in retry (#RTN11c)' do
|
506
|
+
let(:close_connection_proc) do
|
507
|
+
lambda do
|
508
|
+
EventMachine.add_timer(0.001) do
|
509
|
+
if connection.transport.nil?
|
510
|
+
close_connection_proc.call
|
511
|
+
else
|
512
|
+
connection.transport.close_connection_after_writing
|
513
|
+
end
|
514
|
+
end
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
context 'when suspended' do
|
519
|
+
let(:suspended_retry_timeout) { 60 }
|
520
|
+
let(:client_options) do
|
521
|
+
default_options.merge(
|
522
|
+
disconnected_retry_timeout: 0.02,
|
523
|
+
suspended_retry_timeout: suspended_retry_timeout,
|
524
|
+
connection_state_ttl: 0
|
525
|
+
)
|
526
|
+
end
|
527
|
+
|
528
|
+
it 'reconnects immediately' do
|
529
|
+
connection.once(:connecting) { close_connection_proc.call }
|
530
|
+
|
531
|
+
connection.on(:suspended) do |connection_state_change|
|
532
|
+
if connection_state_change.retry_in.zero?
|
533
|
+
# Exhausting immediate retries
|
534
|
+
connection.once(:connecting) { close_connection_proc.call }
|
535
|
+
else
|
536
|
+
suspended_at = Time.now.to_f
|
537
|
+
connection.on(:connected) do
|
538
|
+
expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
|
539
|
+
expect(connection.state).to eq(:connected)
|
540
|
+
expect(Time.now.to_f).to be_within(4).of(suspended_at)
|
541
|
+
stop_reactor
|
542
|
+
end
|
543
|
+
end
|
544
|
+
|
545
|
+
connection.connect
|
546
|
+
end
|
547
|
+
|
548
|
+
connection.connect
|
549
|
+
end
|
550
|
+
end
|
551
|
+
|
552
|
+
context 'when disconnected' do
|
553
|
+
let(:retry_timeout) { 60 }
|
554
|
+
let(:client_options) do
|
555
|
+
default_options.merge(
|
556
|
+
disconnected_retry_timeout: retry_timeout,
|
557
|
+
suspended_retry_timeout: retry_timeout,
|
558
|
+
connection_state_ttl: 0
|
559
|
+
)
|
560
|
+
end
|
561
|
+
|
562
|
+
it 'reconnects immediately' do
|
563
|
+
connection.once(:connected) do
|
564
|
+
connection.on(:disconnected) do |connection_state_change|
|
565
|
+
disconnected_at = Time.now.to_f
|
566
|
+
connection.on(:connected) do
|
567
|
+
if connection_state_change.retry_in.zero?
|
568
|
+
# Exhausting immediate retries
|
569
|
+
close_connection_proc.call
|
570
|
+
else
|
571
|
+
expect(connection_state_change.retry_in).to eq(retry_timeout)
|
572
|
+
expect(connection.state).to eq(:connected)
|
573
|
+
expect(Time.now.to_f).to be_within(4).of(disconnected_at)
|
574
|
+
stop_reactor
|
575
|
+
end
|
576
|
+
end
|
577
|
+
connection.connect
|
578
|
+
end
|
579
|
+
|
580
|
+
close_connection_proc.call
|
581
|
+
end
|
582
|
+
|
583
|
+
connection.connect
|
584
|
+
end
|
585
|
+
end
|
586
|
+
end
|
587
|
+
|
588
|
+
context 'when reconnecting a failed connection' do
|
589
|
+
let(:channel) { client.channel(random_str) }
|
590
|
+
let(:client_options) { default_options.merge(log_level: :none) }
|
591
|
+
|
592
|
+
it 'transitions all channels state to initialized and cleares error_reason' do
|
593
|
+
connection.on(:failed) do |connection_state_change|
|
594
|
+
expect(connection.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
|
595
|
+
expect(channel.error_reason).to be_a(Ably::Exceptions::BaseAblyException)
|
596
|
+
expect(channel).to be_failed
|
597
|
+
|
598
|
+
connection.on(:connected) do
|
599
|
+
expect(connection.error_reason).to eq(nil)
|
600
|
+
expect(channel).to be_initialized
|
601
|
+
expect(channel.error_reason).to eq(nil)
|
602
|
+
stop_reactor
|
603
|
+
end
|
604
|
+
|
605
|
+
connection.connect
|
606
|
+
end
|
607
|
+
|
608
|
+
connection.connect do
|
609
|
+
channel.attach do
|
610
|
+
error = Ably::Exceptions::ConnectionFailed.new('forced failure', 500, 50000)
|
611
|
+
client.connection.manager.error_received_from_server error
|
612
|
+
end
|
613
|
+
end
|
614
|
+
end
|
615
|
+
end
|
616
|
+
|
485
617
|
context 'with invalid auth details' do
|
486
618
|
let(:client_options) { default_options.merge(key: 'this.is:invalid', log_level: :none) }
|
487
619
|
|
@@ -693,6 +825,18 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
693
825
|
end
|
694
826
|
|
695
827
|
context '#close' do
|
828
|
+
let(:close_connection_proc) do
|
829
|
+
lambda do
|
830
|
+
EventMachine.add_timer(0.001) do
|
831
|
+
if connection.transport.nil?
|
832
|
+
close_connection_proc.call
|
833
|
+
else
|
834
|
+
connection.transport.close_connection_after_writing
|
835
|
+
end
|
836
|
+
end
|
837
|
+
end
|
838
|
+
end
|
839
|
+
|
696
840
|
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
697
841
|
connection.connect do
|
698
842
|
expect(connection.close).to be_a(Ably::Util::SafeDeferrable)
|
@@ -754,6 +898,56 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
754
898
|
end
|
755
899
|
end
|
756
900
|
|
901
|
+
context ':connecting RTN12f' do
|
902
|
+
context ":connected does not arrive when trying to close" do
|
903
|
+
it 'moves to closed' do
|
904
|
+
connection.on(:closed) do |state_change|
|
905
|
+
state_changes = connection.state_history.map { |t| t[:state].to_sym }
|
906
|
+
|
907
|
+
expect(state_changes).to eq([:connecting, :closing, :closed])
|
908
|
+
stop_reactor
|
909
|
+
end
|
910
|
+
|
911
|
+
connection.on(:connecting) do
|
912
|
+
connection.close
|
913
|
+
connection.__outgoing_protocol_msgbus__.unsubscribe
|
914
|
+
end
|
915
|
+
|
916
|
+
connection.connect
|
917
|
+
end
|
918
|
+
end
|
919
|
+
|
920
|
+
context ":connected arrive when trying to close" do
|
921
|
+
let(:protocol_message_attributes) do
|
922
|
+
{
|
923
|
+
action: Ably::Models::ProtocolMessage::ACTION.Connected.to_i,
|
924
|
+
connection_serial: 55,
|
925
|
+
connection_details: {
|
926
|
+
max_idle_interval: 2 * 1000
|
927
|
+
}
|
928
|
+
}
|
929
|
+
end
|
930
|
+
|
931
|
+
it 'moves to connected and then to closed' do
|
932
|
+
connection.on(:closed) do |state_change|
|
933
|
+
state_changes = connection.state_history.map { |t| t[:state].to_sym }
|
934
|
+
|
935
|
+
expect(state_changes).to eq([:connecting, :connected, :closing, :closed])
|
936
|
+
stop_reactor
|
937
|
+
end
|
938
|
+
|
939
|
+
connection.on(:connecting) do
|
940
|
+
connection.__outgoing_protocol_msgbus__.unsubscribe
|
941
|
+
|
942
|
+
connection.__incoming_protocol_msgbus__.publish :protocol_message, Ably::Models::ProtocolMessage.new(protocol_message_attributes)
|
943
|
+
connection.close
|
944
|
+
end
|
945
|
+
|
946
|
+
connection.connect
|
947
|
+
end
|
948
|
+
end
|
949
|
+
end
|
950
|
+
|
757
951
|
context ':connected' do
|
758
952
|
it 'changes the connection state to :closing and waits for the server to confirm connection is :closed with a ProtocolMessage' do
|
759
953
|
connection.on(:connected) do
|
@@ -800,6 +994,81 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
800
994
|
end
|
801
995
|
end
|
802
996
|
end
|
997
|
+
|
998
|
+
context ':suspended RTN12d' do
|
999
|
+
let(:suspended_retry_timeout) { 60 }
|
1000
|
+
let(:client_options) do
|
1001
|
+
default_options.merge(
|
1002
|
+
disconnected_retry_timeout: 0.02,
|
1003
|
+
suspended_retry_timeout: suspended_retry_timeout,
|
1004
|
+
connection_state_ttl: 0
|
1005
|
+
)
|
1006
|
+
end
|
1007
|
+
|
1008
|
+
it 'immediatly closes connection' do
|
1009
|
+
connection.on(:connecting) { close_connection_proc.call }
|
1010
|
+
connection.on(:suspended) do |connection_state_change|
|
1011
|
+
if connection_state_change.retry_in.zero?
|
1012
|
+
# Exhausting immediate retries
|
1013
|
+
connection.once(:connecting) { close_connection_proc.call }
|
1014
|
+
else
|
1015
|
+
suspended_at = Time.now.to_f
|
1016
|
+
connection.on(:closed) do
|
1017
|
+
expect(connection_state_change.retry_in).to eq(suspended_retry_timeout)
|
1018
|
+
expect(connection.state).to eq(:closed)
|
1019
|
+
expect(Time.now.to_f).to be_within(4).of(suspended_at)
|
1020
|
+
stop_reactor
|
1021
|
+
end
|
1022
|
+
|
1023
|
+
connection.close
|
1024
|
+
end
|
1025
|
+
|
1026
|
+
connection.connect
|
1027
|
+
end
|
1028
|
+
|
1029
|
+
connection.connect
|
1030
|
+
end
|
1031
|
+
end
|
1032
|
+
|
1033
|
+
context ':disconnected RTN12d' do
|
1034
|
+
let(:retry_timeout) { 60 }
|
1035
|
+
let(:client_options) do
|
1036
|
+
default_options.merge(
|
1037
|
+
disconnected_retry_timeout: retry_timeout,
|
1038
|
+
suspended_retry_timeout: retry_timeout,
|
1039
|
+
connection_state_ttl: 0
|
1040
|
+
)
|
1041
|
+
end
|
1042
|
+
|
1043
|
+
it 'immediatly closes connection' do
|
1044
|
+
connection.once(:connected) do
|
1045
|
+
connection.on(:disconnected) do |connection_state_change|
|
1046
|
+
disconnected_at = Time.now.to_f
|
1047
|
+
connection.on(:connected) do
|
1048
|
+
if connection_state_change.retry_in.zero?
|
1049
|
+
# Exhausting immediate retries
|
1050
|
+
close_connection_proc.call
|
1051
|
+
else
|
1052
|
+
connection.once(:closed) do
|
1053
|
+
expect(connection_state_change.retry_in).to eq(retry_timeout)
|
1054
|
+
expect(connection.state).to eq(:closed)
|
1055
|
+
expect(Time.now.to_f).to be_within(4).of(disconnected_at)
|
1056
|
+
stop_reactor
|
1057
|
+
end
|
1058
|
+
|
1059
|
+
connection.close
|
1060
|
+
end
|
1061
|
+
end
|
1062
|
+
|
1063
|
+
connection.connect
|
1064
|
+
end
|
1065
|
+
|
1066
|
+
close_connection_proc.call
|
1067
|
+
end
|
1068
|
+
|
1069
|
+
connection.connect
|
1070
|
+
end
|
1071
|
+
end
|
803
1072
|
end
|
804
1073
|
end
|
805
1074
|
|
@@ -1834,7 +2103,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1834
2103
|
it 'sends the protocol version param v (#G4, #RTN2f)' do
|
1835
2104
|
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1836
2105
|
uri = URI.parse(url)
|
1837
|
-
expect(CGI::parse(uri.query)['v'][0]).to eql('1.
|
2106
|
+
expect(CGI::parse(uri.query)['v'][0]).to eql('1.2')
|
1838
2107
|
stop_reactor
|
1839
2108
|
end
|
1840
2109
|
client
|
@@ -75,6 +75,83 @@ describe 'Ably::Realtime::Channel Message', :event_machine do
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
+
context 'a single Message object (#RSL1a)' do
|
79
|
+
let(:name) { random_str }
|
80
|
+
let(:data) { random_str }
|
81
|
+
let(:message) { Ably::Models::Message.new(name: name, data: data) }
|
82
|
+
|
83
|
+
it 'publishes the message' do
|
84
|
+
channel.attach
|
85
|
+
channel.publish(message)
|
86
|
+
channel.subscribe do |msg|
|
87
|
+
expect(msg.name).to eq(message.name)
|
88
|
+
expect(msg.data).to eq(message.data)
|
89
|
+
stop_reactor
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'an array of Message objects (#RSL1a)' do
|
95
|
+
let(:data) { random_str }
|
96
|
+
let(:message1) { Ably::Models::Message.new(name: random_str, data: data) }
|
97
|
+
let(:message2) { Ably::Models::Message.new(name: random_str, data: data) }
|
98
|
+
let(:message3) { Ably::Models::Message.new(name: random_str, data: data) }
|
99
|
+
|
100
|
+
it 'publishes three messages' do
|
101
|
+
channel.attach
|
102
|
+
channel.publish([message1, message2, message3])
|
103
|
+
counter = 0
|
104
|
+
channel.subscribe do |message|
|
105
|
+
counter += 1
|
106
|
+
expect(message.data).to eq(data)
|
107
|
+
expect(message.name).to eq(message1.name) if counter == 1
|
108
|
+
expect(message.name).to eq(message2.name) if counter == 2
|
109
|
+
if counter == 3
|
110
|
+
expect(message.name).to eq(message3.name)
|
111
|
+
stop_reactor
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'an array of hashes (#RSL1a)' do
|
118
|
+
let(:data) { random_str }
|
119
|
+
let(:message1) { { name: random_str, data: data } }
|
120
|
+
let(:message2) { { name: random_str, data: data } }
|
121
|
+
let(:message3) { { name: random_str, data: data } }
|
122
|
+
|
123
|
+
it 'publishes three messages' do
|
124
|
+
channel.attach
|
125
|
+
channel.publish([message1, message2, message3])
|
126
|
+
counter = 0
|
127
|
+
channel.subscribe do |message|
|
128
|
+
counter += 1
|
129
|
+
expect(message.data).to eq(data)
|
130
|
+
expect(message.name).to eq(message1[:name]) if counter == 1
|
131
|
+
expect(message.name).to eq(message2[:name]) if counter == 2
|
132
|
+
if counter == 3
|
133
|
+
expect(message.name).to eq(message3[:name])
|
134
|
+
stop_reactor
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
context 'a name with data payload (#RSL1a, #RSL1b)' do
|
141
|
+
let(:name) { random_str }
|
142
|
+
let(:data) { random_str }
|
143
|
+
|
144
|
+
it 'publishes a message' do
|
145
|
+
channel.attach
|
146
|
+
channel.publish(name, data)
|
147
|
+
channel.subscribe do |message|
|
148
|
+
expect(message.name).to eql(name)
|
149
|
+
expect(message.data).to eq(data)
|
150
|
+
stop_reactor
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
78
155
|
context 'with supported extra payload content type (#RTL6h, #RSL6a2)' do
|
79
156
|
let(:channel) { client.channel("pushenabled:#{random_str}") }
|
80
157
|
|
@@ -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)) }
|
@@ -1165,6 +1165,24 @@ describe Ably::Auth do
|
|
1165
1165
|
end
|
1166
1166
|
end
|
1167
1167
|
|
1168
|
+
context 'when token does not expire' do
|
1169
|
+
let(:client_options) { default_options.merge(use_token_auth: true, key: api_key, query_time: true) }
|
1170
|
+
let(:channel) { client.channels.get(random_str) }
|
1171
|
+
|
1172
|
+
context 'for the next 2 hours' do
|
1173
|
+
let(:local_time) { Time.now - 2 * 60 * 60 }
|
1174
|
+
|
1175
|
+
before { allow(Time).to receive(:now).and_return(local_time) }
|
1176
|
+
|
1177
|
+
it 'should not request for the new token (#RSA4b1)' do
|
1178
|
+
expect { channel.publish 'event' }.to change { auth.current_token_details }
|
1179
|
+
expect do
|
1180
|
+
expect { channel.publish 'event' }.not_to change { auth.current_token_details }
|
1181
|
+
end.not_to change { auth.current_token_details.expires }
|
1182
|
+
end
|
1183
|
+
end
|
1184
|
+
end
|
1185
|
+
|
1168
1186
|
context 'when :client_id is provided in a token' do
|
1169
1187
|
let(:client_id) { '123' }
|
1170
1188
|
let(:token) do
|
@@ -5,11 +5,13 @@ describe Ably::Rest::Channel do
|
|
5
5
|
include Ably::Modules::Conversions
|
6
6
|
|
7
7
|
vary_by_protocol do
|
8
|
-
let(:default_options) { { key: api_key, environment: environment, protocol: protocol} }
|
8
|
+
let(:default_options) { { key: api_key, environment: environment, protocol: protocol, max_frame_size: max_frame_size, max_message_size: max_message_size, idempotent_rest_publishing: false } }
|
9
9
|
let(:client_options) { default_options }
|
10
10
|
let(:client) do
|
11
11
|
Ably::Rest::Client.new(client_options)
|
12
12
|
end
|
13
|
+
let(:max_message_size) { nil }
|
14
|
+
let(:max_frame_size) { nil }
|
13
15
|
|
14
16
|
describe '#publish' do
|
15
17
|
let(:channel_name) { random_str }
|
@@ -60,7 +62,8 @@ describe Ably::Rest::Channel do
|
|
60
62
|
end
|
61
63
|
|
62
64
|
it 'publishes an array of messages in one HTTP request' do
|
63
|
-
expect(
|
65
|
+
expect(client.max_message_size).to eq(Ably::Rest::Client::MAX_MESSAGE_SIZE)
|
66
|
+
expect(messages.sum(&:size) < Ably::Rest::Client::MAX_MESSAGE_SIZE).to eq(true)
|
64
67
|
|
65
68
|
expect(client).to receive(:post).once.and_call_original
|
66
69
|
expect(channel.publish(messages)).to eql(true)
|
@@ -70,19 +73,78 @@ describe Ably::Rest::Channel do
|
|
70
73
|
end
|
71
74
|
|
72
75
|
context 'with an array of Message objects' do
|
73
|
-
|
74
|
-
|
75
|
-
Ably::
|
76
|
+
context 'when max_message_size and max_frame_size is not set' do
|
77
|
+
before do
|
78
|
+
expect(client.max_message_size).to eq(Ably::Rest::Client::MAX_MESSAGE_SIZE)
|
79
|
+
expect(client.max_frame_size).to eq(Ably::Rest::Client::MAX_FRAME_SIZE)
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'and messages size (130 bytes) is smaller than the max_message_size' do
|
83
|
+
let(:messages) do
|
84
|
+
10.times.map do |index|
|
85
|
+
Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 })
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'publishes an array of messages in one HTTP request' do
|
90
|
+
expect(messages.sum &:size).to eq(130)
|
91
|
+
expect(client).to receive(:post).once.and_call_original
|
92
|
+
expect(channel.publish(messages)).to eql(true)
|
93
|
+
expect(channel.history.items.map(&:name)).to match_array(messages.map(&:name))
|
94
|
+
expect(channel.history.items.map(&:data)).to match_array(messages.map(&:data))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'and messages size (177784 bytes) is bigger than the max_message_size' do
|
99
|
+
let(:messages) do
|
100
|
+
10000.times.map do |index|
|
101
|
+
Ably::Models::Message(name: index.to_s, data: { "index" => index + 1 })
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'should not publish and raise Ably::Exceptions::MaxMessageSizeExceeded' do
|
106
|
+
expect(messages.sum &:size).to eq(177784)
|
107
|
+
expect { channel.publish(messages) }.to raise_error(Ably::Exceptions::MaxMessageSizeExceeded)
|
108
|
+
end
|
76
109
|
end
|
77
110
|
end
|
78
111
|
|
79
|
-
|
80
|
-
|
112
|
+
context 'when max_message_size is 655 bytes' do
|
113
|
+
let(:max_message_size) { 655 }
|
81
114
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
115
|
+
before do
|
116
|
+
expect(client.max_message_size).to eq(max_message_size)
|
117
|
+
expect(client.max_frame_size).to eq(Ably::Rest::Client::MAX_FRAME_SIZE)
|
118
|
+
end
|
119
|
+
|
120
|
+
context 'and messages size (130 bytes) is smaller than the max_message_size' do
|
121
|
+
let(:messages) do
|
122
|
+
10.times.map do |index|
|
123
|
+
Ably::Models::Message(name: index.to_s, data: { "index" => index + 10 })
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'publishes an array of messages in one HTTP request' do
|
128
|
+
expect(messages.sum &:size).to eq(130)
|
129
|
+
expect(client).to receive(:post).once.and_call_original
|
130
|
+
expect(channel.publish(messages)).to eql(true)
|
131
|
+
expect(channel.history.items.map(&:name)).to match_array(messages.map(&:name))
|
132
|
+
expect(channel.history.items.map(&:data)).to match_array(messages.map(&:data))
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context 'and messages size (177784 bytes) is bigger than the max_message_size' do
|
137
|
+
let(:messages) do
|
138
|
+
10000.times.map do |index|
|
139
|
+
Ably::Models::Message(name: index.to_s, data: { "index" => index + 1 })
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should not publish and raise Ably::Exceptions::MaxMessageSizeExceeded' do
|
144
|
+
expect(messages.sum &:size).to eq(177784)
|
145
|
+
expect { channel.publish(messages) }.to raise_error(Ably::Exceptions::MaxMessageSizeExceeded)
|
146
|
+
end
|
147
|
+
end
|
86
148
|
end
|
87
149
|
end
|
88
150
|
|
@@ -5,11 +5,11 @@ describe Ably::Rest::Channels do
|
|
5
5
|
shared_examples 'a channel' do
|
6
6
|
it 'returns a channel object' do
|
7
7
|
expect(channel).to be_a Ably::Rest::Channel
|
8
|
-
expect(channel.name).to
|
8
|
+
expect(channel.name).to eq(channel_name)
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'returns channel object and passes the provided options' do
|
12
|
-
expect(channel_with_options.options).to
|
12
|
+
expect(channel_with_options.options.to_h).to eq(options)
|
13
13
|
end
|
14
14
|
end
|
15
15
|
|
@@ -32,12 +32,29 @@ describe Ably::Rest::Channels do
|
|
32
32
|
it_behaves_like 'a channel'
|
33
33
|
end
|
34
34
|
|
35
|
+
describe '#set_options (#RTL16)' do
|
36
|
+
let(:channel) { client.channel(channel_name) }
|
37
|
+
|
38
|
+
it "updates channel's options" do
|
39
|
+
expect { channel.options = options }.to change { channel.options.to_h }.from({}).to(options)
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'when providing Ably::Models::ChannelOptions object' do
|
43
|
+
let(:options_object) { Ably::Models::ChannelOptions.new(options) }
|
44
|
+
|
45
|
+
it "updates channel's options" do
|
46
|
+
expect { channel.options = options_object}.to change { channel.options.to_h }.from({}).to(options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
35
51
|
describe 'accessing an existing channel object with different options' do
|
36
52
|
let(:new_channel_options) { { encrypted: true } }
|
37
53
|
let(:original_channel) { client.channels.get(channel_name, options) }
|
38
54
|
|
39
|
-
it 'overrides the existing channel options and returns the channel object' do
|
40
|
-
expect(original_channel.options).to_not include(:encrypted)
|
55
|
+
it 'overrides the existing channel options and returns the channel object (RSN3c)' do
|
56
|
+
expect(original_channel.options.to_h).to_not include(:encrypted)
|
57
|
+
|
41
58
|
new_channel = client.channels.get(channel_name, new_channel_options)
|
42
59
|
expect(new_channel).to be_a(Ably::Rest::Channel)
|
43
60
|
expect(new_channel.options[:encrypted]).to eql(true)
|
@@ -48,10 +65,10 @@ describe Ably::Rest::Channels do
|
|
48
65
|
let(:original_channel) { client.channels.get(channel_name, options) }
|
49
66
|
|
50
67
|
it 'returns the existing channel without modifying the channel options' do
|
51
|
-
expect(original_channel.options).to
|
68
|
+
expect(original_channel.options.to_h).to eq(options)
|
52
69
|
new_channel = client.channels.get(channel_name)
|
53
70
|
expect(new_channel).to be_a(Ably::Rest::Channel)
|
54
|
-
expect(original_channel.options).to
|
71
|
+
expect(original_channel.options.to_h).to eq(options)
|
55
72
|
end
|
56
73
|
end
|
57
74
|
|