ably 1.1.4 → 1.1.8
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 +15 -1
- data/CHANGELOG.md +109 -0
- data/COPYRIGHT +1 -1
- data/README.md +23 -9
- data/SPEC.md +289 -228
- data/ably.gemspec +14 -9
- data/lib/ably/agent.rb +3 -0
- data/lib/ably/exceptions.rb +6 -0
- data/lib/ably/models/connection_details.rb +8 -0
- data/lib/ably/models/delta_extras.rb +29 -0
- data/lib/ably/models/error_info.rb +6 -2
- data/lib/ably/models/message.rb +25 -0
- data/lib/ably/models/presence_message.rb +14 -0
- data/lib/ably/models/protocol_message.rb +13 -8
- data/lib/ably/modules/ably.rb +11 -1
- data/lib/ably/realtime/channel/channel_manager.rb +2 -2
- data/lib/ably/realtime/channel/channel_state_machine.rb +5 -1
- data/lib/ably/realtime/channel/publisher.rb +6 -0
- data/lib/ably/realtime/channel.rb +2 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -6
- data/lib/ably/realtime/client.rb +1 -0
- 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 +2 -2
- data/lib/ably/rest/channel.rb +11 -3
- data/lib/ably/rest/client.rb +37 -18
- data/lib/ably/rest/middleware/fail_if_unsupported_mime_type.rb +4 -1
- data/lib/ably/version.rb +1 -13
- data/lib/ably.rb +1 -0
- data/spec/acceptance/realtime/auth_spec.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +25 -0
- data/spec/acceptance/realtime/channel_spec.rb +220 -1
- data/spec/acceptance/realtime/connection_failures_spec.rb +85 -13
- data/spec/acceptance/realtime/connection_spec.rb +263 -32
- data/spec/acceptance/realtime/presence_history_spec.rb +3 -1
- data/spec/acceptance/realtime/presence_spec.rb +31 -159
- data/spec/acceptance/rest/base_spec.rb +8 -4
- data/spec/acceptance/rest/channel_spec.rb +84 -9
- data/spec/acceptance/rest/channels_spec.rb +1 -1
- data/spec/acceptance/rest/client_spec.rb +72 -33
- data/spec/shared/client_initializer_behaviour.rb +131 -0
- data/spec/shared/model_behaviour.rb +1 -1
- data/spec/spec_helper.rb +11 -2
- data/spec/support/test_app.rb +1 -1
- 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 +83 -0
- data/spec/unit/models/presence_message_spec.rb +49 -0
- data/spec/unit/models/protocol_message_spec.rb +72 -20
- data/spec/unit/realtime/channel_spec.rb +3 -2
- data/spec/unit/realtime/channels_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +38 -0
- data/spec/unit/rest/channel_spec.rb +44 -1
- data/spec/unit/rest/client_spec.rb +47 -0
- metadata +48 -36
@@ -122,7 +122,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
-
context 'with immediately expired token' do
|
125
|
+
context 'with immediately expired token and no fallback hosts' do
|
126
126
|
let(:ttl) { 0.001 }
|
127
127
|
let(:auth_requests) { [] }
|
128
128
|
let(:token_callback) do
|
@@ -131,7 +131,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
131
131
|
Ably::Rest::Client.new(default_options).auth.request_token(ttl: ttl).token
|
132
132
|
end
|
133
133
|
end
|
134
|
-
let(:client_options) { default_options.merge(auth_callback: token_callback) }
|
134
|
+
let(:client_options) { default_options.merge(auth_callback: token_callback, fallback_hosts: []) }
|
135
135
|
|
136
136
|
it 'renews the token on connect, and makes one immediate subsequent attempt to obtain a new token (#RSA4b)' do
|
137
137
|
started_at = Time.now.to_f
|
@@ -146,7 +146,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
146
146
|
end
|
147
147
|
|
148
148
|
context 'when disconnected_retry_timeout is 0.5 seconds' do
|
149
|
-
let(:client_options) { default_options.merge(disconnected_retry_timeout: 0.5, auth_callback: token_callback) }
|
149
|
+
let(:client_options) { default_options.merge(disconnected_retry_timeout: 0.5, auth_callback: token_callback, fallback_hosts: []) }
|
150
150
|
|
151
151
|
it 'renews the token on connect, and continues to attempt renew based on the retry schedule' do
|
152
152
|
disconnect_count = 0
|
@@ -172,7 +172,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
172
172
|
end
|
173
173
|
|
174
174
|
context 'using implicit token auth' do
|
175
|
-
let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { ttl: ttl }) }
|
175
|
+
let(:client_options) { default_options.merge(use_token_auth: true, default_token_params: { ttl: ttl }, fallback_hosts: []) }
|
176
176
|
|
177
177
|
before do
|
178
178
|
stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', -10 # ensure client lib thinks token is still valid
|
@@ -441,7 +441,9 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
441
441
|
end
|
442
442
|
end
|
443
443
|
|
444
|
-
context '#connect' do
|
444
|
+
context '#connect with no fallbacks' do
|
445
|
+
let(:client_options) { default_options.merge(fallback_hosts: []) }
|
446
|
+
|
445
447
|
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
446
448
|
expect(connection.connect).to be_a(Ably::Util::SafeDeferrable)
|
447
449
|
stop_reactor
|
@@ -480,6 +482,118 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
480
482
|
end
|
481
483
|
end
|
482
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
|
+
|
483
597
|
context 'with invalid auth details' do
|
484
598
|
let(:client_options) { default_options.merge(key: 'this.is:invalid', log_level: :none) }
|
485
599
|
|
@@ -691,6 +805,18 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
691
805
|
end
|
692
806
|
|
693
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
|
+
|
694
820
|
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
695
821
|
connection.connect do
|
696
822
|
expect(connection.close).to be_a(Ably::Util::SafeDeferrable)
|
@@ -752,6 +878,56 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
752
878
|
end
|
753
879
|
end
|
754
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
|
+
|
755
931
|
context ':connected' do
|
756
932
|
it 'changes the connection state to :closing and waits for the server to confirm connection is :closed with a ProtocolMessage' do
|
757
933
|
connection.on(:connected) do
|
@@ -798,6 +974,81 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
798
974
|
end
|
799
975
|
end
|
800
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
|
801
1052
|
end
|
802
1053
|
end
|
803
1054
|
|
@@ -1167,6 +1418,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1167
1418
|
host: 'this.host.does.not.exist.com'
|
1168
1419
|
)
|
1169
1420
|
)
|
1421
|
+
allow(client).to receive(:fallback_hosts).and_return([])
|
1170
1422
|
|
1171
1423
|
connection.transition_state_machine! :disconnected
|
1172
1424
|
end
|
@@ -1451,7 +1703,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1451
1703
|
let(:client_options) { default_options.merge(tls: true) }
|
1452
1704
|
|
1453
1705
|
it 'uses TLS for the Internet check to https://internet-up.ably-realtime.com/is-the-internet-up.txt' do
|
1454
|
-
expect(EventMachine::HttpRequest).to receive(:new).with('https://internet-up.ably-realtime.com/is-the-internet-up.txt').and_return(http_request)
|
1706
|
+
expect(EventMachine::HttpRequest).to receive(:new).with('https://internet-up.ably-realtime.com/is-the-internet-up.txt', { tls: { verify_peer: true } }).and_return(http_request)
|
1455
1707
|
connection.internet_up?
|
1456
1708
|
stop_reactor
|
1457
1709
|
end
|
@@ -1461,7 +1713,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1461
1713
|
let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
|
1462
1714
|
|
1463
1715
|
it 'uses TLS for the Internet check to http://internet-up.ably-realtime.com/is-the-internet-up.txt' do
|
1464
|
-
expect(EventMachine::HttpRequest).to receive(:new).with('http://internet-up.ably-realtime.com/is-the-internet-up.txt').and_return(http_request)
|
1716
|
+
expect(EventMachine::HttpRequest).to receive(:new).with('http://internet-up.ably-realtime.com/is-the-internet-up.txt', { tls: { verify_peer: true } }).and_return(http_request)
|
1465
1717
|
connection.internet_up?
|
1466
1718
|
stop_reactor
|
1467
1719
|
end
|
@@ -1475,7 +1727,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1475
1727
|
let(:client_options) { default_options.merge(tls: true) }
|
1476
1728
|
|
1477
1729
|
it 'checks the Internet up URL over TLS' do
|
1478
|
-
expect(EventMachine::HttpRequest).to receive(:new).with("https:#{Ably::INTERNET_CHECK.fetch(:url)}").and_return(double('request', get: EventMachine::DefaultDeferrable.new))
|
1730
|
+
expect(EventMachine::HttpRequest).to receive(:new).with("https:#{Ably::INTERNET_CHECK.fetch(:url)}", { tls: { verify_peer: true } }).and_return(double('request', get: EventMachine::DefaultDeferrable.new))
|
1479
1731
|
connection.internet_up?
|
1480
1732
|
stop_reactor
|
1481
1733
|
end
|
@@ -1485,7 +1737,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1485
1737
|
let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
|
1486
1738
|
|
1487
1739
|
it 'checks the Internet up URL over TLS' do
|
1488
|
-
expect(EventMachine::HttpRequest).to receive(:new).with("http:#{Ably::INTERNET_CHECK.fetch(:url)}").and_return(double('request', get: EventMachine::DefaultDeferrable.new))
|
1740
|
+
expect(EventMachine::HttpRequest).to receive(:new).with("http:#{Ably::INTERNET_CHECK.fetch(:url)}", { tls: { verify_peer: true } }).and_return(double('request', get: EventMachine::DefaultDeferrable.new))
|
1489
1741
|
connection.internet_up?
|
1490
1742
|
stop_reactor
|
1491
1743
|
end
|
@@ -1837,35 +2089,14 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1837
2089
|
client
|
1838
2090
|
end
|
1839
2091
|
|
1840
|
-
it 'sends the lib version param
|
2092
|
+
it 'sends the lib version param agent (#RCS7d)' do
|
1841
2093
|
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1842
2094
|
uri = URI.parse(url)
|
1843
|
-
expect(CGI::parse(uri.query)['
|
2095
|
+
expect(CGI::parse(uri.query)['agent'][0]).to match(/^ably-ruby\/\d\.\d\.\d ruby\/\d\.\d\.\d$/)
|
1844
2096
|
stop_reactor
|
1845
2097
|
end
|
1846
2098
|
client
|
1847
2099
|
end
|
1848
|
-
|
1849
|
-
context 'with variant' do
|
1850
|
-
let(:variant) { 'foo' }
|
1851
|
-
|
1852
|
-
before do
|
1853
|
-
Ably.lib_variant = variant
|
1854
|
-
end
|
1855
|
-
|
1856
|
-
after do
|
1857
|
-
Ably.lib_variant = nil
|
1858
|
-
end
|
1859
|
-
|
1860
|
-
it 'sends the lib version param lib with the variant (#RTN2g + #RSC7b)' do
|
1861
|
-
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1862
|
-
uri = URI.parse(url)
|
1863
|
-
expect(CGI::parse(uri.query)['lib'][0]).to match(/^ruby-#{variant}-1\.1\.\d+(-[\w\.]+)?$/)
|
1864
|
-
stop_reactor
|
1865
|
-
end
|
1866
|
-
client
|
1867
|
-
end
|
1868
|
-
end
|
1869
2100
|
end
|
1870
2101
|
|
1871
2102
|
context 'transport_params (#RTC1f)' do
|
@@ -20,7 +20,7 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
|
|
20
20
|
|
21
21
|
it 'provides up to the moment presence history' do
|
22
22
|
presence_client_one.enter(data) do
|
23
|
-
presence_client_one.leave
|
23
|
+
presence_client_one.subscribe(:leave) do
|
24
24
|
presence_client_one.history do |history_page|
|
25
25
|
expect(history_page).to be_a(Ably::Models::PaginatedResult)
|
26
26
|
expect(history_page.items.count).to eql(2)
|
@@ -36,6 +36,8 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
|
|
36
36
|
stop_reactor
|
37
37
|
end
|
38
38
|
end
|
39
|
+
|
40
|
+
presence_client_one.leave(leave_data)
|
39
41
|
end
|
40
42
|
end
|
41
43
|
|
@@ -498,7 +498,9 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
498
498
|
end
|
499
499
|
|
500
500
|
presence_client_one.enter do
|
501
|
-
|
501
|
+
EventMachine.add_timer(0.5) do
|
502
|
+
presence_client_one.leave
|
503
|
+
end
|
502
504
|
end
|
503
505
|
end
|
504
506
|
end
|
@@ -705,7 +707,7 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
705
707
|
|
706
708
|
context '#sync_complete? and SYNC flags (#RTP1)' do
|
707
709
|
context 'when attaching to a channel without any members present' do
|
708
|
-
|
710
|
+
xit 'sync_complete? is true, there is no presence flag, and the presence channel is considered synced immediately (#RTP1)' do
|
709
711
|
flag_checked = false
|
710
712
|
|
711
713
|
anonymous_client.connection.__incoming_protocol_msgbus__.subscribe(:protocol_message) do |protocol_message|
|
@@ -1211,10 +1213,11 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
1211
1213
|
channel_client_one.attach do
|
1212
1214
|
presence_client_one.subscribe(:enter) do
|
1213
1215
|
presence_client_one.unsubscribe :enter
|
1214
|
-
|
1215
|
-
|
1216
|
-
|
1217
|
-
|
1216
|
+
EventMachine.add_timer(0.5) do
|
1217
|
+
expect(presence_client_one.members).to be_in_sync
|
1218
|
+
expect(presence_client_one.members.send(:members).count).to eql(1)
|
1219
|
+
presence_client_one.leave data
|
1220
|
+
end
|
1218
1221
|
end
|
1219
1222
|
|
1220
1223
|
presence_client_one.enter(enter_data) do
|
@@ -2137,16 +2140,18 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2137
2140
|
end
|
2138
2141
|
|
2139
2142
|
it 'emits an error when cipher does not match and presence data cannot be decoded' do
|
2140
|
-
incompatible_encrypted_channel.
|
2143
|
+
incompatible_encrypted_channel.once(:attached) do
|
2141
2144
|
expect(client_two.logger).to receive(:error) do |*args, &block|
|
2142
2145
|
expect(args.concat([block ? block.call : nil]).join(',')).to match(/Cipher algorithm AES-128-CBC does not match/)
|
2143
2146
|
stop_reactor
|
2144
|
-
end
|
2147
|
+
end.at_least(:once)
|
2145
2148
|
|
2146
2149
|
encrypted_channel.attach do
|
2147
2150
|
encrypted_channel.presence.enter data
|
2148
2151
|
end
|
2149
2152
|
end
|
2153
|
+
|
2154
|
+
incompatible_encrypted_channel.attach
|
2150
2155
|
end
|
2151
2156
|
end
|
2152
2157
|
end
|
@@ -2522,156 +2527,6 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2522
2527
|
client_one.connection.transport.__outgoing_protocol_msgbus__.unsubscribe
|
2523
2528
|
end
|
2524
2529
|
|
2525
|
-
context 'and the resume flag is true' do
|
2526
|
-
context 'and the presence flag is false' do
|
2527
|
-
it 'does not send any presence events as the PresenceMap is in sync (#RTP5c1)' do
|
2528
|
-
presence_client_one.enter
|
2529
|
-
presence_client_one.subscribe(:enter) do
|
2530
|
-
presence_client_one.unsubscribe :enter
|
2531
|
-
|
2532
|
-
client_one.connection.transport.__outgoing_protocol_msgbus__.subscribe do |message|
|
2533
|
-
raise "No presence state updates to Ably are expected. Message sent: #{message.to_json}" if client_one.connection.connected?
|
2534
|
-
end
|
2535
|
-
|
2536
|
-
cripple_websocket_transport
|
2537
|
-
|
2538
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2539
|
-
action: attached_action,
|
2540
|
-
channel: channel_name,
|
2541
|
-
flags: resume_flag
|
2542
|
-
)
|
2543
|
-
|
2544
|
-
EventMachine.add_timer(1) do
|
2545
|
-
presence_client_one.get do |members|
|
2546
|
-
expect(members.length).to eql(1)
|
2547
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2548
|
-
stop_reactor
|
2549
|
-
end
|
2550
|
-
end
|
2551
|
-
end
|
2552
|
-
end
|
2553
|
-
end
|
2554
|
-
|
2555
|
-
context 'and the presence flag is true' do
|
2556
|
-
context 'and following the SYNC all local MemberMap members are present in the PresenceMap' do
|
2557
|
-
it 'does nothing as MemberMap is in sync (#RTP5c2)' do
|
2558
|
-
presence_client_one.enter
|
2559
|
-
presence_client_one.subscribe(:enter) do
|
2560
|
-
presence_client_one.unsubscribe :enter
|
2561
|
-
|
2562
|
-
expect(presence_client_one.members.length).to eql(1)
|
2563
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2564
|
-
|
2565
|
-
presence_client_one.members.once(:in_sync) do
|
2566
|
-
presence_client_one.get do |members|
|
2567
|
-
expect(members.length).to eql(1)
|
2568
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2569
|
-
stop_reactor
|
2570
|
-
end
|
2571
|
-
end
|
2572
|
-
|
2573
|
-
client_one.connection.transport.__outgoing_protocol_msgbus__.subscribe do |message|
|
2574
|
-
raise "No presence state updates to Ably are expected. Message sent: #{message.to_json}" if client_one.connection.connected?
|
2575
|
-
end
|
2576
|
-
|
2577
|
-
cripple_websocket_transport
|
2578
|
-
|
2579
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2580
|
-
action: attached_action,
|
2581
|
-
channel: channel_name,
|
2582
|
-
flags: resume_flag + presence_flag
|
2583
|
-
)
|
2584
|
-
|
2585
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2586
|
-
action: sync_action,
|
2587
|
-
channel: channel_name,
|
2588
|
-
presence: presence_client_one.members.map(&:shallow_clone).map(&:as_json),
|
2589
|
-
channelSerial: nil # no further SYNC messages expected
|
2590
|
-
)
|
2591
|
-
end
|
2592
|
-
end
|
2593
|
-
end
|
2594
|
-
|
2595
|
-
context 'and following the SYNC a local MemberMap member is not present in the PresenceMap' do
|
2596
|
-
it 're-enters the missing members automatically (#RTP5c2)' do
|
2597
|
-
sync_check_completed = false
|
2598
|
-
|
2599
|
-
presence_client_one.enter
|
2600
|
-
presence_client_one.subscribe(:enter) do
|
2601
|
-
presence_client_one.unsubscribe :enter
|
2602
|
-
|
2603
|
-
expect(presence_client_one.members.length).to eql(1)
|
2604
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2605
|
-
|
2606
|
-
client_one.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |message|
|
2607
|
-
next if message.action == :close # ignore finalization of connection
|
2608
|
-
|
2609
|
-
expect(message.action).to eq(:presence)
|
2610
|
-
presence_message = message.presence.first
|
2611
|
-
expect(presence_message.action).to eq(:enter)
|
2612
|
-
expect(presence_message.client_id).to eq(client_one.auth.client_id)
|
2613
|
-
|
2614
|
-
presence_client_one.subscribe(:enter) do |message|
|
2615
|
-
expect(message.connection_id).to eql(client_one.connection.id)
|
2616
|
-
expect(message.client_id).to eq(client_one.auth.client_id)
|
2617
|
-
|
2618
|
-
EventMachine.next_tick do
|
2619
|
-
expect(presence_client_one.members.length).to eql(2)
|
2620
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2621
|
-
expect(sync_check_completed).to be_truthy
|
2622
|
-
stop_reactor
|
2623
|
-
end
|
2624
|
-
end
|
2625
|
-
|
2626
|
-
# Fabricate Ably sending back the Enter PresenceMessage to the client a short while after
|
2627
|
-
# ensuring the PresenceMap for a short period does not have this member as to be expected in reality
|
2628
|
-
EventMachine.add_timer(0.2) do
|
2629
|
-
connection_id = random_str
|
2630
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2631
|
-
action: presence_action,
|
2632
|
-
channel: channel_name,
|
2633
|
-
connectionId: client_one.connection.id,
|
2634
|
-
connectionSerial: 50,
|
2635
|
-
timestamp: as_since_epoch(Time.now),
|
2636
|
-
presence: [presence_message.shallow_clone(id: "#{client_one.connection.id}:0:0", timestamp: as_since_epoch(Time.now)).as_json]
|
2637
|
-
)
|
2638
|
-
end
|
2639
|
-
end
|
2640
|
-
|
2641
|
-
presence_client_one.members.once(:in_sync) do
|
2642
|
-
# For a brief period, the client will have re-entered the missing members from the local_members
|
2643
|
-
# but the enter from Ably will have not been received, so at this point the local_members will be empty
|
2644
|
-
presence_client_one.get do |members|
|
2645
|
-
expect(members.length).to eql(1)
|
2646
|
-
expect(members.first.connection_id).to_not eql(client_one.connection.id)
|
2647
|
-
expect(presence_client_one.members.local_members.length).to eql(0)
|
2648
|
-
sync_check_completed = true
|
2649
|
-
end
|
2650
|
-
end
|
2651
|
-
|
2652
|
-
cripple_websocket_transport
|
2653
|
-
|
2654
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2655
|
-
action: attached_action,
|
2656
|
-
channel: channel_name,
|
2657
|
-
flags: resume_flag + presence_flag
|
2658
|
-
)
|
2659
|
-
|
2660
|
-
# Complete the SYNC but without the member who was entered by this client
|
2661
|
-
connection_id = random_str
|
2662
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2663
|
-
action: sync_action,
|
2664
|
-
channel: channel_name,
|
2665
|
-
timestamp: as_since_epoch(Time.now),
|
2666
|
-
presence: [{ id: "#{connection_id}:0:0", action: present_action, connection_id: connection_id, client_id: random_str }],
|
2667
|
-
chanenlSerial: nil # no further SYNC messages expected
|
2668
|
-
)
|
2669
|
-
end
|
2670
|
-
end
|
2671
|
-
end
|
2672
|
-
end
|
2673
|
-
end
|
2674
|
-
|
2675
2530
|
context 'and the resume flag is false' do
|
2676
2531
|
context 'and the presence flag is false' do
|
2677
2532
|
let(:member_data) { random_str }
|
@@ -2779,7 +2634,24 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2779
2634
|
end
|
2780
2635
|
end
|
2781
2636
|
|
2782
|
-
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
|
+
|
2783
2655
|
context 'channel transitions to the FAILED state' do
|
2784
2656
|
let(:anonymous_client) { auto_close Ably::Realtime::Client.new(client_options.merge(log_level: :fatal)) }
|
2785
2657
|
let(:client_one) { auto_close Ably::Realtime::Client.new(client_options.merge(client_id: client_one_id, log_level: :fatal)) }
|
@@ -87,8 +87,10 @@ describe Ably::Rest do
|
|
87
87
|
let(:error_response) { '{ "error": { "statusCode": 500, "code": 50000, "message": "Internal error" } }' }
|
88
88
|
|
89
89
|
before do
|
90
|
-
|
91
|
-
|
90
|
+
(client.fallback_hosts.map { |host| "https://#{host}" } + [client.endpoint]).each do |host|
|
91
|
+
stub_request(:get, "#{host}/time")
|
92
|
+
.to_return(:status => 500, :body => error_response, :headers => { 'Content-Type' => 'application/json' })
|
93
|
+
end
|
92
94
|
end
|
93
95
|
|
94
96
|
it 'should raise a ServerError exception' do
|
@@ -98,8 +100,10 @@ describe Ably::Rest do
|
|
98
100
|
|
99
101
|
describe '500 server error without a valid JSON response body', :webmock do
|
100
102
|
before do
|
101
|
-
|
102
|
-
|
103
|
+
(client.fallback_hosts.map { |host| "https://#{host}" } + [client.endpoint]).each do |host|
|
104
|
+
stub_request(:get, "#{host}/time").
|
105
|
+
to_return(:status => 500, :headers => { 'Content-Type' => 'application/json' })
|
106
|
+
end
|
103
107
|
end
|
104
108
|
|
105
109
|
it 'should raise a ServerError exception' do
|