ably 1.1.7 → 1.2.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.
- 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
|
|