ably 1.1.4.rc → 1.1.7
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 +41 -0
- data/CHANGELOG.md +85 -0
- data/COPYRIGHT +1 -0
- data/LICENSE +173 -10
- data/MAINTAINERS.md +1 -0
- data/README.md +24 -18
- data/SPEC.md +1020 -922
- data/ably.gemspec +13 -8
- data/lib/ably.rb +1 -0
- data/lib/ably/agent.rb +3 -0
- data/lib/ably/auth.rb +12 -2
- data/lib/ably/exceptions.rb +6 -0
- data/lib/ably/models/connection_details.rb +2 -0
- data/lib/ably/models/message.rb +14 -0
- data/lib/ably/models/presence_message.rb +14 -0
- data/lib/ably/models/protocol_message.rb +8 -0
- data/lib/ably/modules/ably.rb +11 -1
- data/lib/ably/realtime/channel.rb +7 -11
- data/lib/ably/realtime/channel/channel_manager.rb +3 -3
- data/lib/ably/realtime/channel/channel_properties.rb +24 -0
- data/lib/ably/realtime/channel/publisher.rb +5 -0
- data/lib/ably/realtime/client.rb +9 -0
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +14 -6
- data/lib/ably/realtime/connection.rb +9 -5
- data/lib/ably/realtime/connection/websocket_transport.rb +67 -1
- data/lib/ably/realtime/presence.rb +0 -14
- data/lib/ably/rest/channel.rb +10 -3
- data/lib/ably/rest/client.rb +22 -21
- data/lib/ably/version.rb +1 -13
- 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 +24 -0
- data/spec/acceptance/realtime/client_spec.rb +72 -16
- data/spec/acceptance/realtime/connection_failures_spec.rb +29 -12
- data/spec/acceptance/realtime/connection_spec.rb +31 -33
- data/spec/acceptance/realtime/presence_history_spec.rb +3 -59
- data/spec/acceptance/realtime/presence_spec.rb +66 -157
- data/spec/acceptance/realtime/push_admin_spec.rb +3 -19
- data/spec/acceptance/rest/auth_spec.rb +6 -75
- data/spec/acceptance/rest/base_spec.rb +8 -4
- data/spec/acceptance/rest/channel_spec.rb +13 -0
- data/spec/acceptance/rest/client_spec.rb +144 -45
- data/spec/acceptance/rest/push_admin_spec.rb +3 -19
- data/spec/shared/client_initializer_behaviour.rb +131 -8
- data/spec/shared/model_behaviour.rb +1 -1
- data/spec/spec_helper.rb +12 -2
- data/spec/support/serialization_helper.rb +21 -0
- data/spec/unit/models/message_spec.rb +59 -0
- data/spec/unit/models/presence_message_spec.rb +49 -0
- data/spec/unit/models/protocol_message_spec.rb +48 -0
- data/spec/unit/realtime/channel_spec.rb +1 -1
- data/spec/unit/realtime/client_spec.rb +19 -6
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +38 -0
- data/spec/unit/rest/channel_spec.rb +10 -0
- data/spec/unit/rest/client_spec.rb +20 -0
- metadata +52 -32
- data/.travis.yml +0 -18
@@ -77,18 +77,6 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
end
|
80
|
-
|
81
|
-
context 'with implicit authorisation' do
|
82
|
-
let(:client_options) { default_options.merge(client_id: 'force_token_auth') }
|
83
|
-
|
84
|
-
it 'uses the token created by the implicit authorisation' do
|
85
|
-
expect(client.rest_client.auth).to receive(:request_token).once.and_call_original
|
86
|
-
|
87
|
-
connection.once(:connected) do
|
88
|
-
stop_reactor
|
89
|
-
end
|
90
|
-
end
|
91
|
-
end
|
92
80
|
end
|
93
81
|
|
94
82
|
context 'that expire' do
|
@@ -134,7 +122,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
134
122
|
end
|
135
123
|
end
|
136
124
|
|
137
|
-
context 'with immediately expired token' do
|
125
|
+
context 'with immediately expired token and no fallback hosts' do
|
138
126
|
let(:ttl) { 0.001 }
|
139
127
|
let(:auth_requests) { [] }
|
140
128
|
let(:token_callback) do
|
@@ -143,7 +131,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
143
131
|
Ably::Rest::Client.new(default_options).auth.request_token(ttl: ttl).token
|
144
132
|
end
|
145
133
|
end
|
146
|
-
let(:client_options) { default_options.merge(auth_callback: token_callback) }
|
134
|
+
let(:client_options) { default_options.merge(auth_callback: token_callback, fallback_hosts: []) }
|
147
135
|
|
148
136
|
it 'renews the token on connect, and makes one immediate subsequent attempt to obtain a new token (#RSA4b)' do
|
149
137
|
started_at = Time.now.to_f
|
@@ -158,7 +146,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
158
146
|
end
|
159
147
|
|
160
148
|
context 'when disconnected_retry_timeout is 0.5 seconds' do
|
161
|
-
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: []) }
|
162
150
|
|
163
151
|
it 'renews the token on connect, and continues to attempt renew based on the retry schedule' do
|
164
152
|
disconnect_count = 0
|
@@ -184,7 +172,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
184
172
|
end
|
185
173
|
|
186
174
|
context 'using implicit token auth' do
|
187
|
-
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: []) }
|
188
176
|
|
189
177
|
before do
|
190
178
|
stub_const 'Ably::Models::TokenDetails::TOKEN_EXPIRY_BUFFER', -10 # ensure client lib thinks token is still valid
|
@@ -453,7 +441,9 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
453
441
|
end
|
454
442
|
end
|
455
443
|
|
456
|
-
context '#connect' do
|
444
|
+
context '#connect with no fallbacks' do
|
445
|
+
let(:client_options) { default_options.merge(fallback_hosts: []) }
|
446
|
+
|
457
447
|
it 'returns a SafeDeferrable that catches exceptions in callbacks and logs them' do
|
458
448
|
expect(connection.connect).to be_a(Ably::Util::SafeDeferrable)
|
459
449
|
stop_reactor
|
@@ -1179,6 +1169,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1179
1169
|
host: 'this.host.does.not.exist.com'
|
1180
1170
|
)
|
1181
1171
|
)
|
1172
|
+
allow(client).to receive(:fallback_hosts).and_return([])
|
1182
1173
|
|
1183
1174
|
connection.transition_state_machine! :disconnected
|
1184
1175
|
end
|
@@ -1463,7 +1454,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1463
1454
|
let(:client_options) { default_options.merge(tls: true) }
|
1464
1455
|
|
1465
1456
|
it 'uses TLS for the Internet check to https://internet-up.ably-realtime.com/is-the-internet-up.txt' do
|
1466
|
-
expect(EventMachine::HttpRequest).to receive(:new).with('https://internet-up.ably-realtime.com/is-the-internet-up.txt').and_return(http_request)
|
1457
|
+
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)
|
1467
1458
|
connection.internet_up?
|
1468
1459
|
stop_reactor
|
1469
1460
|
end
|
@@ -1473,7 +1464,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1473
1464
|
let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
|
1474
1465
|
|
1475
1466
|
it 'uses TLS for the Internet check to http://internet-up.ably-realtime.com/is-the-internet-up.txt' do
|
1476
|
-
expect(EventMachine::HttpRequest).to receive(:new).with('http://internet-up.ably-realtime.com/is-the-internet-up.txt').and_return(http_request)
|
1467
|
+
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)
|
1477
1468
|
connection.internet_up?
|
1478
1469
|
stop_reactor
|
1479
1470
|
end
|
@@ -1487,7 +1478,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1487
1478
|
let(:client_options) { default_options.merge(tls: true) }
|
1488
1479
|
|
1489
1480
|
it 'checks the Internet up URL over TLS' do
|
1490
|
-
expect(EventMachine::HttpRequest).to receive(:new).with("https:#{Ably::INTERNET_CHECK.fetch(:url)}").and_return(double('request', get: EventMachine::DefaultDeferrable.new))
|
1481
|
+
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))
|
1491
1482
|
connection.internet_up?
|
1492
1483
|
stop_reactor
|
1493
1484
|
end
|
@@ -1497,7 +1488,7 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1497
1488
|
let(:client_options) { default_options.merge(tls: false, use_token_auth: true) }
|
1498
1489
|
|
1499
1490
|
it 'checks the Internet up URL over TLS' do
|
1500
|
-
expect(EventMachine::HttpRequest).to receive(:new).with("http:#{Ably::INTERNET_CHECK.fetch(:url)}").and_return(double('request', get: EventMachine::DefaultDeferrable.new))
|
1491
|
+
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))
|
1501
1492
|
connection.internet_up?
|
1502
1493
|
stop_reactor
|
1503
1494
|
end
|
@@ -1849,32 +1840,39 @@ describe Ably::Realtime::Connection, :event_machine do
|
|
1849
1840
|
client
|
1850
1841
|
end
|
1851
1842
|
|
1852
|
-
it 'sends the lib version param
|
1843
|
+
it 'sends the lib version param agent (#RCS7d)' do
|
1853
1844
|
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1854
1845
|
uri = URI.parse(url)
|
1855
|
-
expect(CGI::parse(uri.query)['
|
1846
|
+
expect(CGI::parse(uri.query)['agent'][0]).to match(/^ably-ruby\/\d\.\d\.\d ruby\/\d\.\d\.\d$/)
|
1856
1847
|
stop_reactor
|
1857
1848
|
end
|
1858
1849
|
client
|
1859
1850
|
end
|
1851
|
+
end
|
1860
1852
|
|
1861
|
-
|
1862
|
-
|
1853
|
+
context 'transport_params (#RTC1f)' do
|
1854
|
+
let(:client_options) { default_options.merge(transport_params: { 'extra_param' => 'extra_param' }) }
|
1863
1855
|
|
1864
|
-
|
1865
|
-
|
1866
|
-
|
1856
|
+
it 'pases transport_params to query' do
|
1857
|
+
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1858
|
+
uri = URI.parse(url)
|
1859
|
+
expect(CGI::parse(uri.query)['extra_param'][0]).to eq('extra_param')
|
1860
|
+
stop_reactor
|
1861
|
+
end
|
1867
1862
|
|
1868
|
-
|
1869
|
-
|
1870
|
-
end
|
1863
|
+
client
|
1864
|
+
end
|
1871
1865
|
|
1872
|
-
|
1866
|
+
context 'when changing default param' do
|
1867
|
+
let(:client_options) { default_options.merge(transport_params: { v: '1.0' }) }
|
1868
|
+
|
1869
|
+
it 'overrides default param (#RTC1f1)' do
|
1873
1870
|
expect(EventMachine).to receive(:connect) do |host, port, transport, object, url|
|
1874
1871
|
uri = URI.parse(url)
|
1875
|
-
expect(CGI::parse(uri.query)['
|
1872
|
+
expect(CGI::parse(uri.query)['v'][0]).to eq('1.0')
|
1876
1873
|
stop_reactor
|
1877
1874
|
end
|
1875
|
+
|
1878
1876
|
client
|
1879
1877
|
end
|
1880
1878
|
end
|
@@ -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
|
|
@@ -52,63 +54,5 @@ describe Ably::Realtime::Presence, 'history', :event_machine do
|
|
52
54
|
|
53
55
|
presence_client_one.enter(data)
|
54
56
|
end
|
55
|
-
|
56
|
-
context 'with option until_attach: true' do
|
57
|
-
let(:event) { random_str }
|
58
|
-
let(:presence_data_before_attach) { random_str }
|
59
|
-
let(:presence_data_after_attach) { random_str }
|
60
|
-
|
61
|
-
it 'retrieves all presence messages before channel was attached' do
|
62
|
-
presence_client_two.enter(presence_data_before_attach) do
|
63
|
-
presence_client_one.enter(presence_data_after_attach) do
|
64
|
-
presence_client_one.history(until_attach: true) do |presence_page|
|
65
|
-
expect(presence_page.items.count).to eql(1)
|
66
|
-
expect(presence_page.items.first.data).to eql(presence_data_before_attach)
|
67
|
-
stop_reactor
|
68
|
-
end
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
context 'and two pages of messages' do
|
74
|
-
let(:wildcard_token) { lambda { |token_params| Ably::Rest::Client.new(default_options).auth.request_token(client_id: '*') } }
|
75
|
-
let(:client_one) { auto_close Ably::Realtime::Client.new(default_options.merge(auth_callback: wildcard_token)) }
|
76
|
-
let(:client_two) { auto_close Ably::Realtime::Client.new(default_options.merge(auth_callback: wildcard_token)) }
|
77
|
-
|
78
|
-
# TODO: Remove retry logic when presence history regression fixed
|
79
|
-
# https://github.com/ably/realtime/issues/1707
|
80
|
-
#
|
81
|
-
it 'retrieves two pages of messages before channel was attached', retry: 10, :retry_wait => 5 do
|
82
|
-
when_all(*10.times.map { |i| presence_client_two.enter_client("client:#{i}", presence_data_before_attach) }) do
|
83
|
-
when_all(*10.times.map { |i| presence_client_one.enter_client("client:#{i}", presence_data_after_attach) }) do
|
84
|
-
presence_client_one.history(until_attach: true, limit: 5) do |presence_page|
|
85
|
-
expect(presence_page.items.count).to eql(5)
|
86
|
-
expect(presence_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
|
87
|
-
|
88
|
-
presence_page.next do |presence_next_page|
|
89
|
-
expect(presence_next_page.items.count).to eql(5)
|
90
|
-
expect(presence_next_page.items.map(&:data).uniq.first).to eql(presence_data_before_attach)
|
91
|
-
if presence_next_page.has_next?
|
92
|
-
presence_next_page.next do |last|
|
93
|
-
expect(last.items.count).to eql(0)
|
94
|
-
end
|
95
|
-
else
|
96
|
-
expect(presence_next_page).to be_last
|
97
|
-
end
|
98
|
-
stop_reactor
|
99
|
-
end
|
100
|
-
end
|
101
|
-
end
|
102
|
-
end
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
it 'fails with an exception unless state is attached' do
|
107
|
-
presence_client_one.history(until_attach: true).errback do |error|
|
108
|
-
expect(error.message).to match(/not attached/)
|
109
|
-
stop_reactor
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
113
57
|
end
|
114
58
|
end
|
@@ -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
|
@@ -2449,173 +2454,77 @@ describe Ably::Realtime::Presence, :event_machine do
|
|
2449
2454
|
end
|
2450
2455
|
end
|
2451
2456
|
|
2452
|
-
|
2453
|
-
let(:
|
2454
|
-
let(:sync_action) { Ably::Models::ProtocolMessage::ACTION.Sync.to_i }
|
2455
|
-
let(:presence_action) { Ably::Models::ProtocolMessage::ACTION.Presence.to_i }
|
2456
|
-
let(:present_action) { Ably::Models::PresenceMessage::ACTION.Present.to_i }
|
2457
|
-
let(:resume_flag) { 4 }
|
2458
|
-
let(:presence_flag) { 1 }
|
2459
|
-
|
2460
|
-
def fabricate_incoming_protocol_message(protocol_message)
|
2461
|
-
client_one.connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
2462
|
-
end
|
2463
|
-
|
2464
|
-
# Prevents any messages from the WebSocket transport being sent / received
|
2465
|
-
# Connection protocol message subscriptions are still active, but nothing reaches or comes from the WebSocket transport
|
2466
|
-
def cripple_websocket_transport
|
2467
|
-
client_one.connection.transport.__incoming_protocol_msgbus__.unsubscribe
|
2468
|
-
client_one.connection.transport.__outgoing_protocol_msgbus__.unsubscribe
|
2469
|
-
end
|
2470
|
-
|
2471
|
-
context 'and the resume flag is true' do
|
2472
|
-
context 'and the presence flag is false' do
|
2473
|
-
it 'does not send any presence events as the PresenceMap is in sync (#RTP5c1)' do
|
2474
|
-
presence_client_one.enter
|
2475
|
-
presence_client_one.subscribe(:enter) do
|
2476
|
-
presence_client_one.unsubscribe :enter
|
2457
|
+
describe '#RTP17b' do
|
2458
|
+
let(:leave_action) { Ably::Models::PresenceMessage::ACTION.Leave }
|
2477
2459
|
|
2478
|
-
|
2479
|
-
|
2460
|
+
it 'updates presence members on leave' do
|
2461
|
+
presence_client_two.subscribe(:enter) do
|
2462
|
+
channel_anonymous_client.attach do
|
2463
|
+
channel_anonymous_client.presence.get do |members|
|
2464
|
+
presence_client_two.subscribe(:leave) do
|
2465
|
+
expect(presence_client_two.members.local_members).to be_empty
|
2466
|
+
stop_reactor
|
2480
2467
|
end
|
2481
2468
|
|
2482
|
-
|
2483
|
-
|
2484
|
-
|
2485
|
-
|
2486
|
-
|
2487
|
-
|
2469
|
+
leave_message = Ably::Models::PresenceMessage.new(
|
2470
|
+
'id' => "#{client_two.connection.id}:#{presence_client_two.client_id}:1",
|
2471
|
+
'clientId' => presence_client_two.client_id,
|
2472
|
+
'connectionId' => client_two.connection.id,
|
2473
|
+
'timestamp' => as_since_epoch(Time.now),
|
2474
|
+
'action' => leave_action
|
2488
2475
|
)
|
2489
2476
|
|
2490
|
-
|
2491
|
-
presence_client_one.get do |members|
|
2492
|
-
expect(members.length).to eql(1)
|
2493
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2494
|
-
stop_reactor
|
2495
|
-
end
|
2496
|
-
end
|
2477
|
+
presence_client_two.__incoming_msgbus__.publish :presence, leave_message
|
2497
2478
|
end
|
2498
2479
|
end
|
2499
2480
|
end
|
2500
2481
|
|
2501
|
-
|
2502
|
-
|
2503
|
-
it 'does nothing as MemberMap is in sync (#RTP5c2)' do
|
2504
|
-
presence_client_one.enter
|
2505
|
-
presence_client_one.subscribe(:enter) do
|
2506
|
-
presence_client_one.unsubscribe :enter
|
2507
|
-
|
2508
|
-
expect(presence_client_one.members.length).to eql(1)
|
2509
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2510
|
-
|
2511
|
-
presence_client_one.members.once(:in_sync) do
|
2512
|
-
presence_client_one.get do |members|
|
2513
|
-
expect(members.length).to eql(1)
|
2514
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2515
|
-
stop_reactor
|
2516
|
-
end
|
2517
|
-
end
|
2518
|
-
|
2519
|
-
client_one.connection.transport.__outgoing_protocol_msgbus__.subscribe do |message|
|
2520
|
-
raise "No presence state updates to Ably are expected. Message sent: #{message.to_json}" if client_one.connection.connected?
|
2521
|
-
end
|
2482
|
+
presence_client_two.enter
|
2483
|
+
end
|
2522
2484
|
|
2523
|
-
|
2485
|
+
it 'does no update presence members on fabricated leave' do
|
2486
|
+
presence_client_two.subscribe(:enter) do
|
2487
|
+
channel_anonymous_client.attach do
|
2488
|
+
channel_anonymous_client.presence.get do |members|
|
2489
|
+
presence_client_two.subscribe(:leave) do
|
2490
|
+
expect(presence_client_two.members.local_members).to_not be_empty
|
2491
|
+
stop_reactor
|
2492
|
+
end
|
2524
2493
|
|
2525
|
-
|
2526
|
-
|
2527
|
-
|
2528
|
-
|
2529
|
-
)
|
2494
|
+
fabricated_leave_message = Ably::Models::PresenceMessage.new(
|
2495
|
+
'id' => "#{client_two.connection.id}:#{presence_client_two.client_id}:1",
|
2496
|
+
'clientId' => presence_client_two.client_id,
|
2497
|
+
'connectionId' => "fabricated:#{presence_client_two.client_id}:0",
|
2498
|
+
'timestamp' => as_since_epoch(Time.now),
|
2499
|
+
'action' => leave_action
|
2500
|
+
)
|
2530
2501
|
|
2531
|
-
|
2532
|
-
action: sync_action,
|
2533
|
-
channel: channel_name,
|
2534
|
-
presence: presence_client_one.members.map(&:shallow_clone).map(&:as_json),
|
2535
|
-
channelSerial: nil # no further SYNC messages expected
|
2536
|
-
)
|
2537
|
-
end
|
2502
|
+
presence_client_two.__incoming_msgbus__.publish :presence, fabricated_leave_message
|
2538
2503
|
end
|
2539
2504
|
end
|
2505
|
+
end
|
2540
2506
|
|
2541
|
-
|
2542
|
-
|
2543
|
-
|
2544
|
-
|
2545
|
-
presence_client_one.enter
|
2546
|
-
presence_client_one.subscribe(:enter) do
|
2547
|
-
presence_client_one.unsubscribe :enter
|
2548
|
-
|
2549
|
-
expect(presence_client_one.members.length).to eql(1)
|
2550
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2551
|
-
|
2552
|
-
client_one.connection.__outgoing_protocol_msgbus__.subscribe(:protocol_message) do |message|
|
2553
|
-
next if message.action == :close # ignore finalization of connection
|
2554
|
-
|
2555
|
-
expect(message.action).to eq(:presence)
|
2556
|
-
presence_message = message.presence.first
|
2557
|
-
expect(presence_message.action).to eq(:enter)
|
2558
|
-
expect(presence_message.client_id).to eq(client_one.auth.client_id)
|
2559
|
-
|
2560
|
-
presence_client_one.subscribe(:enter) do |message|
|
2561
|
-
expect(message.connection_id).to eql(client_one.connection.id)
|
2562
|
-
expect(message.client_id).to eq(client_one.auth.client_id)
|
2563
|
-
|
2564
|
-
EventMachine.next_tick do
|
2565
|
-
expect(presence_client_one.members.length).to eql(2)
|
2566
|
-
expect(presence_client_one.members.local_members.length).to eql(1)
|
2567
|
-
expect(sync_check_completed).to be_truthy
|
2568
|
-
stop_reactor
|
2569
|
-
end
|
2570
|
-
end
|
2571
|
-
|
2572
|
-
# Fabricate Ably sending back the Enter PresenceMessage to the client a short while after
|
2573
|
-
# ensuring the PresenceMap for a short period does not have this member as to be expected in reality
|
2574
|
-
EventMachine.add_timer(0.2) do
|
2575
|
-
connection_id = random_str
|
2576
|
-
fabricate_incoming_protocol_message Ably::Models::ProtocolMessage.new(
|
2577
|
-
action: presence_action,
|
2578
|
-
channel: channel_name,
|
2579
|
-
connectionId: client_one.connection.id,
|
2580
|
-
connectionSerial: 50,
|
2581
|
-
timestamp: as_since_epoch(Time.now),
|
2582
|
-
presence: [presence_message.shallow_clone(id: "#{client_one.connection.id}:0:0", timestamp: as_since_epoch(Time.now)).as_json]
|
2583
|
-
)
|
2584
|
-
end
|
2585
|
-
end
|
2586
|
-
|
2587
|
-
presence_client_one.members.once(:in_sync) do
|
2588
|
-
# For a brief period, the client will have re-entered the missing members from the local_members
|
2589
|
-
# but the enter from Ably will have not been received, so at this point the local_members will be empty
|
2590
|
-
presence_client_one.get do |members|
|
2591
|
-
expect(members.length).to eql(1)
|
2592
|
-
expect(members.first.connection_id).to_not eql(client_one.connection.id)
|
2593
|
-
expect(presence_client_one.members.local_members.length).to eql(0)
|
2594
|
-
sync_check_completed = true
|
2595
|
-
end
|
2596
|
-
end
|
2507
|
+
presence_client_two.enter
|
2508
|
+
end
|
2509
|
+
end
|
2597
2510
|
|
2598
|
-
|
2511
|
+
context 'when a channel becomes attached again' do
|
2512
|
+
let(:attached_action) { Ably::Models::ProtocolMessage::ACTION.Attached.to_i }
|
2513
|
+
let(:sync_action) { Ably::Models::ProtocolMessage::ACTION.Sync.to_i }
|
2514
|
+
let(:presence_action) { Ably::Models::ProtocolMessage::ACTION.Presence.to_i }
|
2515
|
+
let(:present_action) { Ably::Models::PresenceMessage::ACTION.Present.to_i }
|
2516
|
+
let(:resume_flag) { 4 }
|
2517
|
+
let(:presence_flag) { 1 }
|
2599
2518
|
|
2600
|
-
|
2601
|
-
|
2602
|
-
|
2603
|
-
flags: resume_flag + presence_flag
|
2604
|
-
)
|
2519
|
+
def fabricate_incoming_protocol_message(protocol_message)
|
2520
|
+
client_one.connection.__incoming_protocol_msgbus__.publish :protocol_message, protocol_message
|
2521
|
+
end
|
2605
2522
|
|
2606
|
-
|
2607
|
-
|
2608
|
-
|
2609
|
-
|
2610
|
-
|
2611
|
-
timestamp: as_since_epoch(Time.now),
|
2612
|
-
presence: [{ id: "#{connection_id}:0:0", action: present_action, connection_id: connection_id, client_id: random_str }],
|
2613
|
-
chanenlSerial: nil # no further SYNC messages expected
|
2614
|
-
)
|
2615
|
-
end
|
2616
|
-
end
|
2617
|
-
end
|
2618
|
-
end
|
2523
|
+
# Prevents any messages from the WebSocket transport being sent / received
|
2524
|
+
# Connection protocol message subscriptions are still active, but nothing reaches or comes from the WebSocket transport
|
2525
|
+
def cripple_websocket_transport
|
2526
|
+
client_one.connection.transport.__incoming_protocol_msgbus__.unsubscribe
|
2527
|
+
client_one.connection.transport.__outgoing_protocol_msgbus__.unsubscribe
|
2619
2528
|
end
|
2620
2529
|
|
2621
2530
|
context 'and the resume flag is false' do
|