ably 0.6.2 → 0.7.0
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/.rspec +1 -0
- data/.ruby-version.old +1 -0
- data/.travis.yml +0 -2
- data/Rakefile +22 -4
- data/SPEC.md +1676 -0
- data/ably.gemspec +1 -1
- data/lib/ably.rb +0 -8
- data/lib/ably/auth.rb +54 -46
- data/lib/ably/exceptions.rb +19 -5
- data/lib/ably/logger.rb +1 -1
- data/lib/ably/models/error_info.rb +1 -1
- data/lib/ably/models/idiomatic_ruby_wrapper.rb +11 -9
- data/lib/ably/models/message.rb +15 -12
- data/lib/ably/models/message_encoders/base.rb +6 -5
- data/lib/ably/models/message_encoders/base64.rb +1 -0
- data/lib/ably/models/message_encoders/cipher.rb +6 -3
- data/lib/ably/models/message_encoders/json.rb +1 -0
- data/lib/ably/models/message_encoders/utf8.rb +2 -9
- data/lib/ably/models/nil_logger.rb +20 -0
- data/lib/ably/models/paginated_resource.rb +5 -2
- data/lib/ably/models/presence_message.rb +21 -12
- data/lib/ably/models/protocol_message.rb +22 -6
- data/lib/ably/modules/ably.rb +11 -0
- data/lib/ably/modules/async_wrapper.rb +2 -0
- data/lib/ably/modules/conversions.rb +23 -3
- data/lib/ably/modules/encodeable.rb +2 -1
- data/lib/ably/modules/enum.rb +2 -0
- data/lib/ably/modules/event_emitter.rb +7 -1
- data/lib/ably/modules/event_machine_helpers.rb +2 -0
- data/lib/ably/modules/http_helpers.rb +2 -0
- data/lib/ably/modules/model_common.rb +12 -2
- data/lib/ably/modules/state_emitter.rb +76 -0
- data/lib/ably/modules/state_machine.rb +53 -0
- data/lib/ably/modules/statesman_monkey_patch.rb +33 -0
- data/lib/ably/modules/uses_state_machine.rb +74 -0
- data/lib/ably/realtime.rb +4 -2
- data/lib/ably/realtime/channel.rb +51 -58
- data/lib/ably/realtime/channel/channel_manager.rb +91 -0
- data/lib/ably/realtime/channel/channel_state_machine.rb +68 -0
- data/lib/ably/realtime/client.rb +70 -26
- data/lib/ably/realtime/client/incoming_message_dispatcher.rb +31 -13
- data/lib/ably/realtime/client/outgoing_message_dispatcher.rb +1 -1
- data/lib/ably/realtime/connection.rb +135 -92
- data/lib/ably/realtime/connection/connection_manager.rb +216 -33
- data/lib/ably/realtime/connection/connection_state_machine.rb +30 -73
- data/lib/ably/realtime/models/nil_channel.rb +10 -1
- data/lib/ably/realtime/presence.rb +336 -92
- data/lib/ably/rest.rb +2 -2
- data/lib/ably/rest/channel.rb +13 -4
- data/lib/ably/rest/client.rb +138 -38
- data/lib/ably/rest/middleware/logger.rb +24 -3
- data/lib/ably/rest/presence.rb +12 -7
- data/lib/ably/version.rb +1 -1
- data/spec/acceptance/realtime/channel_history_spec.rb +101 -85
- data/spec/acceptance/realtime/channel_spec.rb +461 -120
- data/spec/acceptance/realtime/client_spec.rb +119 -0
- data/spec/acceptance/realtime/connection_failures_spec.rb +499 -0
- data/spec/acceptance/realtime/connection_spec.rb +571 -97
- data/spec/acceptance/realtime/message_spec.rb +347 -333
- data/spec/acceptance/realtime/presence_history_spec.rb +35 -40
- data/spec/acceptance/realtime/presence_spec.rb +769 -239
- data/spec/acceptance/realtime/stats_spec.rb +14 -22
- data/spec/acceptance/realtime/time_spec.rb +16 -20
- data/spec/acceptance/rest/auth_spec.rb +425 -364
- data/spec/acceptance/rest/base_spec.rb +108 -176
- data/spec/acceptance/rest/channel_spec.rb +89 -89
- data/spec/acceptance/rest/channels_spec.rb +30 -32
- data/spec/acceptance/rest/client_spec.rb +273 -0
- data/spec/acceptance/rest/encoders_spec.rb +185 -0
- data/spec/acceptance/rest/message_spec.rb +186 -163
- data/spec/acceptance/rest/presence_spec.rb +150 -111
- data/spec/acceptance/rest/stats_spec.rb +45 -40
- data/spec/acceptance/rest/time_spec.rb +8 -10
- data/spec/rspec_config.rb +10 -1
- data/spec/shared/client_initializer_behaviour.rb +212 -0
- data/spec/{support/model_helper.rb → shared/model_behaviour.rb} +6 -6
- data/spec/{support/protocol_msgbus_helper.rb → shared/protocol_msgbus_behaviour.rb} +1 -1
- data/spec/spec_helper.rb +9 -0
- data/spec/support/api_helper.rb +11 -0
- data/spec/support/event_machine_helper.rb +101 -3
- data/spec/support/markdown_spec_formatter.rb +90 -0
- data/spec/support/private_api_formatter.rb +36 -0
- data/spec/support/protocol_helper.rb +32 -0
- data/spec/support/random_helper.rb +15 -0
- data/spec/support/test_app.rb +4 -0
- data/spec/unit/auth_spec.rb +68 -0
- data/spec/unit/logger_spec.rb +77 -66
- data/spec/unit/models/error_info_spec.rb +1 -1
- data/spec/unit/models/idiomatic_ruby_wrapper_spec.rb +2 -3
- data/spec/unit/models/message_encoders/base64_spec.rb +2 -2
- data/spec/unit/models/message_encoders/cipher_spec.rb +2 -2
- data/spec/unit/models/message_encoders/utf8_spec.rb +2 -46
- data/spec/unit/models/message_spec.rb +160 -15
- data/spec/unit/models/paginated_resource_spec.rb +29 -27
- data/spec/unit/models/presence_message_spec.rb +163 -20
- data/spec/unit/models/protocol_message_spec.rb +43 -8
- data/spec/unit/modules/async_wrapper_spec.rb +2 -3
- data/spec/unit/modules/conversions_spec.rb +1 -1
- data/spec/unit/modules/enum_spec.rb +2 -3
- data/spec/unit/modules/event_emitter_spec.rb +62 -5
- data/spec/unit/modules/state_emitter_spec.rb +283 -0
- data/spec/unit/realtime/channel_spec.rb +107 -2
- data/spec/unit/realtime/channels_spec.rb +1 -0
- data/spec/unit/realtime/client_spec.rb +8 -48
- data/spec/unit/realtime/connection_spec.rb +3 -3
- data/spec/unit/realtime/incoming_message_dispatcher_spec.rb +2 -2
- data/spec/unit/realtime/presence_spec.rb +13 -4
- data/spec/unit/realtime/realtime_spec.rb +0 -11
- data/spec/unit/realtime/websocket_transport_spec.rb +2 -2
- data/spec/unit/rest/channel_spec.rb +109 -0
- data/spec/unit/rest/channels_spec.rb +4 -3
- data/spec/unit/rest/client_spec.rb +30 -125
- data/spec/unit/rest/rest_spec.rb +10 -0
- data/spec/unit/util/crypto_spec.rb +10 -5
- data/spec/unit/util/pub_sub_spec.rb +5 -5
- metadata +44 -12
- data/spec/integration/modules/state_emitter_spec.rb +0 -80
- data/spec/integration/rest/auth.rb +0 -9
@@ -1,60 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
3
|
|
4
|
-
describe
|
5
|
-
|
4
|
+
describe Ably::Realtime::Presence, 'history', :event_machine do
|
5
|
+
vary_by_protocol do
|
6
|
+
let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
|
6
7
|
|
7
|
-
|
8
|
-
context "over #{protocol}" do
|
9
|
-
let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
|
8
|
+
let(:channel_name) { "persisted:#{random_str(2)}" }
|
10
9
|
|
11
|
-
|
10
|
+
let(:client_one) { Ably::Realtime::Client.new(default_options.merge(client_id: random_str)) }
|
11
|
+
let(:channel_client_one) { client_one.channel(channel_name) }
|
12
|
+
let(:presence_client_one) { channel_client_one.presence }
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
let(:client_two) { Ably::Realtime::Client.new(default_options.merge(client_id: random_str)) }
|
15
|
+
let(:channel_client_two) { client_two.channel(channel_name) }
|
16
|
+
let(:presence_client_two) { channel_client_two.presence }
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
let(:presence_client_two) { channel_client_two.presence }
|
18
|
+
let(:data) { random_str }
|
19
|
+
let(:leave_data) { random_str }
|
20
20
|
|
21
|
-
|
21
|
+
it 'provides up to the moment presence history' do
|
22
|
+
presence_client_one.enter(data: data) do
|
23
|
+
presence_client_one.leave(data: leave_data) do
|
24
|
+
presence_client_one.history do |history|
|
25
|
+
expect(history.count).to eql(2)
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
presence_client_one.leave do
|
27
|
-
presence_client_one.history do |history|
|
28
|
-
expect(history.count).to eql(2)
|
27
|
+
expect(history[1].action).to eq(:enter)
|
28
|
+
expect(history[1].client_id).to eq(client_one.client_id)
|
29
|
+
expect(history[1].data).to eql(data)
|
29
30
|
|
30
|
-
|
31
|
-
|
31
|
+
expect(history[0].action).to eq(:leave)
|
32
|
+
expect(history[0].client_id).to eq(client_one.client_id)
|
33
|
+
expect(history[0].data).to eql(leave_data)
|
32
34
|
|
33
|
-
|
34
|
-
expect(history[0].client_id).to eq(client_one.client_id)
|
35
|
-
expect(history[0].data).to eql(data)
|
36
|
-
|
37
|
-
stop_reactor
|
38
|
-
end
|
39
|
-
end
|
35
|
+
stop_reactor
|
40
36
|
end
|
41
37
|
end
|
42
38
|
end
|
39
|
+
end
|
43
40
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
expect(history.count).to eql(1)
|
41
|
+
it 'ensures REST presence history message IDs match ProtocolMessage wrapped message and connection IDs via Realtime' do
|
42
|
+
presence_client_one.subscribe(:enter) do |message|
|
43
|
+
presence_client_one.history do |history|
|
44
|
+
expect(history.count).to eql(1)
|
49
45
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
end
|
54
|
-
|
55
|
-
presence_client_one.enter(data: data)
|
46
|
+
expect(history[0].id).to eql(message.id)
|
47
|
+
expect(history[0].connection_id).to eql(message.connection_id)
|
48
|
+
stop_reactor
|
56
49
|
end
|
57
50
|
end
|
51
|
+
|
52
|
+
presence_client_one.enter(data: data)
|
58
53
|
end
|
59
54
|
end
|
60
55
|
end
|
@@ -1,85 +1,152 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'spec_helper'
|
2
|
-
require 'securerandom'
|
3
3
|
|
4
|
-
describe
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
4
|
+
describe Ably::Realtime::Presence, :event_machine do
|
5
|
+
vary_by_protocol do
|
6
|
+
let(:default_options) { { api_key: api_key, environment: environment, protocol: protocol } }
|
7
|
+
let(:client_options) { default_options }
|
8
|
+
|
9
|
+
let(:anonymous_client) { Ably::Realtime::Client.new(client_options) }
|
10
|
+
let(:client_one) { Ably::Realtime::Client.new(client_options.merge(client_id: random_str)) }
|
11
|
+
let(:client_two) { Ably::Realtime::Client.new(client_options.merge(client_id: random_str)) }
|
12
|
+
|
13
|
+
let(:channel_name) { "presence-#{random_str(4)}" }
|
14
|
+
let(:channel_anonymous_client) { anonymous_client.channel(channel_name) }
|
15
|
+
let(:presence_anonymous_client) { channel_anonymous_client.presence }
|
16
|
+
let(:channel_client_one) { client_one.channel(channel_name) }
|
17
|
+
let(:channel_rest_client_one) { client_one.rest_client.channel(channel_name) }
|
18
|
+
let(:presence_client_one) { channel_client_one.presence }
|
19
|
+
let(:channel_client_two) { client_two.channel(channel_name) }
|
20
|
+
let(:presence_client_two) { channel_client_two.presence }
|
21
|
+
let(:data_payload) { random_str }
|
22
|
+
|
23
|
+
context 'when attached (but not present) on a presence channel with an anonymous client (no client ID)' do
|
24
|
+
it 'maintains state as other clients enter and leave the channel' do
|
25
|
+
channel_anonymous_client.attach do
|
26
|
+
presence_anonymous_client.subscribe(:enter) do |presence_message|
|
27
|
+
expect(presence_message.client_id).to eql(client_one.client_id)
|
26
28
|
|
27
|
-
|
28
|
-
run_reactor do
|
29
|
-
channel_anonymous_client.attach do
|
30
|
-
presence_anonymous_client.subscribe(:enter) do |presence_message|
|
31
|
-
expect(presence_message.client_id).to eql(client_one.client_id)
|
32
|
-
members = presence_anonymous_client.get
|
29
|
+
presence_anonymous_client.get do |members|
|
33
30
|
expect(members.first.client_id).to eql(client_one.client_id)
|
34
31
|
expect(members.first.action).to eq(:enter)
|
35
32
|
|
36
33
|
presence_anonymous_client.subscribe(:leave) do |presence_message|
|
37
34
|
expect(presence_message.client_id).to eql(client_one.client_id)
|
38
|
-
members = presence_anonymous_client.get
|
39
|
-
expect(members.count).to eql(0)
|
40
35
|
|
41
|
-
|
36
|
+
presence_anonymous_client.get do |members|
|
37
|
+
expect(members.count).to eql(0)
|
38
|
+
stop_reactor
|
39
|
+
end
|
42
40
|
end
|
43
41
|
end
|
44
42
|
end
|
43
|
+
end
|
45
44
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
45
|
+
presence_client_one.enter do
|
46
|
+
presence_client_one.leave
|
49
47
|
end
|
50
48
|
end
|
49
|
+
end
|
51
50
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
expect(presence.client_id).to eq("123")
|
51
|
+
context '#sync_complete?' do
|
52
|
+
context 'when attaching to a channel without any members present' do
|
53
|
+
it 'is true and the presence channel is considered synced immediately' do
|
54
|
+
channel_anonymous_client.attach do
|
55
|
+
expect(channel_anonymous_client.presence).to be_sync_complete
|
58
56
|
stop_reactor
|
59
57
|
end
|
60
58
|
end
|
61
59
|
end
|
62
60
|
|
63
|
-
|
64
|
-
|
65
|
-
run_reactor do
|
61
|
+
context 'when attaching to a channel with members present' do
|
62
|
+
it 'is false and the presence channel will subsequently be synced' do
|
66
63
|
presence_client_one.enter do
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
EventMachine.next_tick do
|
72
|
-
expect(leave_callback_called).to eql(true)
|
64
|
+
channel_anonymous_client.attach do
|
65
|
+
expect(channel_anonymous_client.presence).to_not be_sync_complete
|
66
|
+
channel_anonymous_client.presence.get do
|
67
|
+
expect(channel_anonymous_client.presence).to be_sync_complete
|
73
68
|
stop_reactor
|
74
69
|
end
|
75
70
|
end
|
76
71
|
end
|
77
72
|
end
|
78
73
|
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when the SYNC of a presence channel spans multiple ProtocolMessage messages' do
|
77
|
+
context 'with 250 existing (present) members' do
|
78
|
+
let(:enter_expected_count) { 250 }
|
79
|
+
let(:present) { [] }
|
80
|
+
let(:entered) { [] }
|
81
|
+
|
82
|
+
context 'when a new client attaches to the presence channel', em_timeout: 10 do
|
83
|
+
it 'emits :present for each member' do
|
84
|
+
enter_expected_count.times do |index|
|
85
|
+
presence_client_one.enter_client("client:#{index}") do |message|
|
86
|
+
entered << message
|
87
|
+
next unless entered.count == enter_expected_count
|
88
|
+
|
89
|
+
presence_anonymous_client.subscribe(:present) do |present_message|
|
90
|
+
expect(present_message.action).to eq(:present)
|
91
|
+
present << present_message
|
92
|
+
next unless present.count == enter_expected_count
|
93
|
+
|
94
|
+
expect(present.map(&:client_id).uniq.count).to eql(enter_expected_count)
|
95
|
+
stop_reactor
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context '#get' do
|
102
|
+
it '#waits until sync is complete', event_machine: 15 do
|
103
|
+
enter_expected_count.times do |index|
|
104
|
+
presence_client_one.enter_client("client:#{index}") do |message|
|
105
|
+
entered << message
|
106
|
+
next unless entered.count == enter_expected_count
|
107
|
+
|
108
|
+
presence_anonymous_client.get do |members|
|
109
|
+
expect(members.map(&:client_id).uniq.count).to eql(enter_expected_count)
|
110
|
+
expect(members.count).to eql(enter_expected_count)
|
111
|
+
stop_reactor
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'automatic attachment of channel on access to presence object' do
|
122
|
+
it 'is implicit if presence state is initalized' do
|
123
|
+
channel_client_one.presence
|
124
|
+
channel_client_one.on(:attached) do
|
125
|
+
expect(channel_client_one.state).to eq(:attached)
|
126
|
+
stop_reactor
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
it 'is disabled if presence state is not initialized' do
|
131
|
+
channel_client_one.attach do
|
132
|
+
channel_client_one.detach do
|
133
|
+
expect(channel_client_one.state).to eq(:detached)
|
134
|
+
|
135
|
+
channel_client_one.presence # access the presence object
|
136
|
+
EventMachine.add_timer(1) do
|
137
|
+
expect(channel_client_one.state).to eq(:detached)
|
138
|
+
stop_reactor
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
context 'state' do
|
146
|
+
context 'once opened' do
|
147
|
+
it 'once opened, enters the :left state if the channel detaches' do
|
148
|
+
detached = false
|
79
149
|
|
80
|
-
it 'enters the :left state if the channel detaches' do
|
81
|
-
detached = false
|
82
|
-
run_reactor do
|
83
150
|
channel_client_one.presence.on(:left) do
|
84
151
|
expect(channel_client_one.presence.state).to eq(:left)
|
85
152
|
EventMachine.next_tick do
|
@@ -87,6 +154,7 @@ describe 'Ably::Realtime::Presence Messages' do
|
|
87
154
|
stop_reactor
|
88
155
|
end
|
89
156
|
end
|
157
|
+
|
90
158
|
channel_client_one.presence.enter do |presence|
|
91
159
|
expect(presence.state).to eq(:entered)
|
92
160
|
channel_client_one.detach do
|
@@ -96,324 +164,786 @@ describe 'Ably::Realtime::Presence Messages' do
|
|
96
164
|
end
|
97
165
|
end
|
98
166
|
end
|
167
|
+
end
|
99
168
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
members = presence_client_one.get
|
104
|
-
expect(members.count).to eq(1)
|
169
|
+
context '#enter' do
|
170
|
+
it 'allows client_id to be set on enter for anonymous clients' do
|
171
|
+
channel_anonymous_client.presence.enter client_id: "123"
|
105
172
|
|
106
|
-
|
173
|
+
channel_anonymous_client.presence.subscribe do |presence|
|
174
|
+
expect(presence.client_id).to eq("123")
|
175
|
+
stop_reactor
|
176
|
+
end
|
177
|
+
end
|
107
178
|
|
108
|
-
|
109
|
-
|
179
|
+
context 'data attribute' do
|
180
|
+
context 'when provided as argument option to #enter' do
|
181
|
+
it 'remains intact following #leave' do
|
182
|
+
leave_callback_called = false
|
183
|
+
|
184
|
+
presence_client_one.enter(data: 'stored') do
|
185
|
+
expect(presence_client_one.data).to eql('stored')
|
186
|
+
|
187
|
+
presence_client_one.leave do |presence|
|
188
|
+
leave_callback_called = true
|
189
|
+
end
|
190
|
+
|
191
|
+
presence_client_one.on(:left) do
|
192
|
+
expect(presence_client_one.data).to eql('stored')
|
193
|
+
|
194
|
+
EventMachine.next_tick do
|
195
|
+
expect(leave_callback_called).to eql(true)
|
196
|
+
stop_reactor
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
it 'raises an exception if client_id is not set' do
|
205
|
+
expect { channel_anonymous_client.presence.enter }.to raise_error(Ably::Exceptions::Standard, /without a client_id/)
|
206
|
+
stop_reactor
|
207
|
+
end
|
110
208
|
|
209
|
+
it 'returns a Deferrable' do
|
210
|
+
expect(presence_client_one.enter).to be_a(EventMachine::Deferrable)
|
211
|
+
stop_reactor
|
212
|
+
end
|
213
|
+
|
214
|
+
it 'calls the Deferrable callback on success' do
|
215
|
+
presence_client_one.enter.callback do |presence|
|
216
|
+
expect(presence).to eql(presence_client_one)
|
217
|
+
expect(presence_client_one.state).to eq(:entered)
|
218
|
+
stop_reactor
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
context '#update' do
|
224
|
+
it 'without previous #enter automatically enters' do
|
225
|
+
presence_client_one.update(data: data_payload) do
|
226
|
+
EventMachine.add_timer(1) do
|
227
|
+
expect(presence_client_one.state).to eq(:entered)
|
111
228
|
stop_reactor
|
112
229
|
end
|
113
230
|
end
|
114
231
|
end
|
115
232
|
|
116
|
-
|
117
|
-
|
233
|
+
context 'when ENTERED' do
|
234
|
+
it 'has no effect on the state' do
|
118
235
|
presence_client_one.enter do
|
119
|
-
presence_client_one.
|
120
|
-
|
121
|
-
|
236
|
+
presence_client_one.once_state_changed { fail 'State should not have changed ' }
|
237
|
+
|
238
|
+
presence_client_one.update(data: data_payload) do
|
239
|
+
EventMachine.add_timer(1) do
|
240
|
+
expect(presence_client_one.state).to eq(:entered)
|
241
|
+
presence_client_one.off
|
242
|
+
stop_reactor
|
243
|
+
end
|
122
244
|
end
|
123
245
|
end
|
124
246
|
end
|
125
247
|
end
|
126
248
|
|
127
|
-
|
128
|
-
|
129
|
-
presence_client_one.
|
130
|
-
|
249
|
+
it 'updates the data if :data argument provided' do
|
250
|
+
presence_client_one.enter(data: 'prior') do
|
251
|
+
presence_client_one.update(data: data_payload)
|
252
|
+
end
|
253
|
+
presence_client_one.subscribe(:update) do |message|
|
254
|
+
expect(message.data).to eql(data_payload)
|
255
|
+
stop_reactor
|
256
|
+
end
|
257
|
+
end
|
131
258
|
|
132
|
-
|
133
|
-
|
259
|
+
it 'returns a Deferrable' do
|
260
|
+
presence_client_one.enter do
|
261
|
+
expect(presence_client_one.update).to be_a(EventMachine::Deferrable)
|
262
|
+
stop_reactor
|
263
|
+
end
|
264
|
+
end
|
134
265
|
|
135
|
-
|
136
|
-
|
266
|
+
it 'calls the Deferrable callback on success' do
|
267
|
+
presence_client_one.enter do
|
268
|
+
presence_client_one.update.callback do |presence|
|
269
|
+
expect(presence).to eql(presence_client_one)
|
270
|
+
expect(presence_client_one.state).to eq(:entered)
|
271
|
+
stop_reactor
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
137
276
|
|
138
|
-
|
139
|
-
|
140
|
-
|
277
|
+
context '#leave' do
|
278
|
+
context ':data option' do
|
279
|
+
let(:data) { random_str }
|
141
280
|
|
142
|
-
|
143
|
-
|
144
|
-
|
281
|
+
context 'when set to a string' do
|
282
|
+
it 'emits the new data for the leave event' do
|
283
|
+
presence_client_one.enter data: random_str do
|
284
|
+
presence_client_one.leave data: data
|
285
|
+
end
|
145
286
|
|
287
|
+
presence_client_one.subscribe(:leave) do |presence_message|
|
288
|
+
expect(presence_message.data).to eql(data)
|
146
289
|
stop_reactor
|
147
290
|
end
|
148
291
|
end
|
292
|
+
end
|
149
293
|
|
150
|
-
|
151
|
-
|
294
|
+
context 'when set to nil' do
|
295
|
+
it 'emits nil data for the leave event' do
|
296
|
+
presence_client_one.enter data: random_str do
|
297
|
+
presence_client_one.leave data: nil
|
298
|
+
end
|
299
|
+
|
300
|
+
presence_client_one.subscribe(:leave) do |presence_message|
|
301
|
+
expect(presence_message.data).to be_nil
|
302
|
+
stop_reactor
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
context 'when not passed as an argument' do
|
308
|
+
it 'emits the original data for the leave event' do
|
309
|
+
presence_client_one.enter data: data do
|
310
|
+
presence_client_one.leave
|
311
|
+
end
|
312
|
+
|
313
|
+
presence_client_one.subscribe(:leave) do |presence_message|
|
314
|
+
expect(presence_message.data).to eql(data)
|
315
|
+
stop_reactor
|
316
|
+
end
|
317
|
+
end
|
152
318
|
end
|
153
319
|
end
|
154
320
|
|
155
|
-
|
156
|
-
|
157
|
-
|
321
|
+
it 'raises an exception if not entered' do
|
322
|
+
expect { channel_anonymous_client.presence.leave }.to raise_error(Ably::Exceptions::Standard, /Unable to leave presence channel that is not entered/)
|
323
|
+
stop_reactor
|
324
|
+
end
|
158
325
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
326
|
+
it 'returns a Deferrable' do
|
327
|
+
presence_client_one.enter do
|
328
|
+
expect(presence_client_one.leave).to be_a(EventMachine::Deferrable)
|
329
|
+
stop_reactor
|
330
|
+
end
|
331
|
+
end
|
163
332
|
|
333
|
+
it 'calls the Deferrable callback on success' do
|
334
|
+
presence_client_one.enter do
|
335
|
+
presence_client_one.leave.callback do |presence|
|
336
|
+
expect(presence).to eql(presence_client_one)
|
337
|
+
expect(presence_client_one.state).to eq(:left)
|
164
338
|
stop_reactor
|
165
339
|
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
166
343
|
|
167
|
-
|
168
|
-
|
169
|
-
|
344
|
+
context ':left event' do
|
345
|
+
it 'emits the data defined in enter' do
|
346
|
+
channel_client_one.presence.enter(data: 'data') do
|
347
|
+
channel_client_one.presence.leave
|
348
|
+
end
|
170
349
|
|
171
|
-
|
172
|
-
|
350
|
+
channel_client_two.presence.subscribe(:leave) do |message|
|
351
|
+
expect(message.data).to eql('data')
|
352
|
+
stop_reactor
|
353
|
+
end
|
354
|
+
end
|
173
355
|
|
174
|
-
|
175
|
-
|
356
|
+
it 'emits the data defined in update' do
|
357
|
+
channel_client_one.presence.enter(data: 'something else') do
|
358
|
+
channel_client_one.presence.update(data: 'data') do
|
359
|
+
channel_client_one.presence.leave
|
176
360
|
end
|
361
|
+
end
|
177
362
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
end
|
363
|
+
channel_client_two.presence.subscribe(:leave) do |message|
|
364
|
+
expect(message.data).to eql('data')
|
365
|
+
stop_reactor
|
182
366
|
end
|
183
367
|
end
|
368
|
+
end
|
184
369
|
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
370
|
+
context 'entering/updating/leaving presence state on behalf of another client_id' do
|
371
|
+
let(:client_count) { 5 }
|
372
|
+
let(:clients) { [] }
|
373
|
+
let(:data) { random_str }
|
374
|
+
|
375
|
+
context '#enter_client' do
|
376
|
+
context 'multiple times on the same channel with different client_ids' do
|
377
|
+
it "has no affect on the client's presence state and only enters on behalf of the provided client_id" do
|
378
|
+
client_count.times do |client_id|
|
379
|
+
presence_client_one.enter_client("client:#{client_id}") do
|
380
|
+
presence_client_one.on(:entered) { raise 'Should not have entered' }
|
381
|
+
next unless client_id == client_count - 1
|
382
|
+
|
383
|
+
EventMachine.add_timer(0.5) do
|
384
|
+
expect(presence_client_one.state).to eq(:initialized)
|
385
|
+
stop_reactor
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
end
|
190
390
|
|
191
|
-
|
192
|
-
|
193
|
-
|
391
|
+
it 'enters a channel and sets the data based on the provided :data option' do
|
392
|
+
client_count.times do |client_id|
|
393
|
+
presence_client_one.enter_client("client:#{client_id}", data: data)
|
394
|
+
end
|
395
|
+
|
396
|
+
presence_anonymous_client.subscribe(:enter) do |presence|
|
397
|
+
expect(presence.data).to eql(data)
|
398
|
+
clients << presence
|
399
|
+
next unless clients.count == 5
|
194
400
|
|
401
|
+
expect(clients.map(&:client_id).uniq.count).to eql(5)
|
402
|
+
stop_reactor
|
403
|
+
end
|
404
|
+
end
|
405
|
+
end
|
406
|
+
|
407
|
+
it 'returns a Deferrable' do
|
408
|
+
expect(presence_client_one.enter_client('client_id')).to be_a(EventMachine::Deferrable)
|
409
|
+
stop_reactor
|
410
|
+
end
|
411
|
+
|
412
|
+
it 'calls the Deferrable callback on success' do
|
413
|
+
presence_client_one.enter_client('client_id').callback do |presence|
|
414
|
+
expect(presence).to eql(presence_client_one)
|
195
415
|
stop_reactor
|
196
416
|
end
|
197
417
|
end
|
198
418
|
end
|
199
419
|
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
420
|
+
context '#update_client' do
|
421
|
+
context 'multiple times on the same channel with different client_ids' do
|
422
|
+
it 'updates the data attribute for the member when :data option provided' do
|
423
|
+
updated_callback_count = 0
|
424
|
+
|
425
|
+
client_count.times do |client_id|
|
426
|
+
presence_client_one.enter_client("client:#{client_id}") do
|
427
|
+
presence_client_one.update_client("client:#{client_id}", data: data) do
|
428
|
+
updated_callback_count += 1
|
429
|
+
end
|
430
|
+
end
|
431
|
+
end
|
432
|
+
|
433
|
+
presence_anonymous_client.subscribe(:update) do |presence|
|
434
|
+
expect(presence.data).to eql(data)
|
435
|
+
clients << presence
|
436
|
+
next unless clients.count == 5
|
437
|
+
|
438
|
+
EventMachine.add_timer(0.5) do
|
439
|
+
expect(clients.map(&:client_id).uniq.count).to eql(5)
|
440
|
+
expect(updated_callback_count).to eql(5)
|
441
|
+
stop_reactor
|
442
|
+
end
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
it 'enters if not already entered' do
|
447
|
+
updated_callback_count = 0
|
448
|
+
|
449
|
+
client_count.times do |client_id|
|
450
|
+
presence_client_one.update_client("client:#{client_id}", data: data) do
|
451
|
+
updated_callback_count += 1
|
452
|
+
end
|
453
|
+
end
|
454
|
+
|
455
|
+
presence_anonymous_client.subscribe(:enter) do |presence|
|
456
|
+
expect(presence.data).to eql(data)
|
457
|
+
clients << presence
|
458
|
+
next unless clients.count == 5
|
459
|
+
|
460
|
+
EventMachine.add_timer(0.5) do
|
461
|
+
expect(clients.map(&:client_id).uniq.count).to eql(5)
|
462
|
+
expect(updated_callback_count).to eql(5)
|
463
|
+
stop_reactor
|
464
|
+
end
|
207
465
|
end
|
208
466
|
end
|
209
467
|
end
|
468
|
+
|
469
|
+
it 'returns a Deferrable' do
|
470
|
+
expect(presence_client_one.update_client('client_id')).to be_a(EventMachine::Deferrable)
|
471
|
+
stop_reactor
|
472
|
+
end
|
473
|
+
|
474
|
+
it 'calls the Deferrable callback on success' do
|
475
|
+
presence_client_one.update_client('client_id').callback do |presence|
|
476
|
+
expect(presence).to eql(presence_client_one)
|
477
|
+
stop_reactor
|
478
|
+
end
|
479
|
+
end
|
210
480
|
end
|
211
481
|
|
212
|
-
context '
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
let(:channel_rest_client_one) { client_one.rest_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
482
|
+
context '#leave_client' do
|
483
|
+
context 'leaves a channel' do
|
484
|
+
context 'multiple times on the same channel with different client_ids' do
|
485
|
+
it 'emits the :leave event for each client_id' do
|
486
|
+
left_callback_count = 0
|
218
487
|
|
219
|
-
|
488
|
+
client_count.times do |client_id|
|
489
|
+
presence_client_one.enter_client("client:#{client_id}", data: random_str) do
|
490
|
+
presence_client_one.leave_client("client:#{client_id}", data: data) do
|
491
|
+
left_callback_count += 1
|
492
|
+
end
|
493
|
+
end
|
494
|
+
end
|
220
495
|
|
221
|
-
|
222
|
-
|
223
|
-
|
496
|
+
presence_anonymous_client.subscribe(:leave) do |presence|
|
497
|
+
expect(presence.data).to eql(data)
|
498
|
+
clients << presence
|
499
|
+
next unless clients.count == 5
|
224
500
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
501
|
+
EventMachine.add_timer(0.5) do
|
502
|
+
expect(clients.map(&:client_id).uniq.count).to eql(5)
|
503
|
+
expect(left_callback_count).to eql(5)
|
504
|
+
stop_reactor
|
505
|
+
end
|
506
|
+
end
|
229
507
|
end
|
230
508
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
509
|
+
it 'succeeds if that client_id has not previously entered the channel' do
|
510
|
+
left_callback_count = 0
|
511
|
+
|
512
|
+
client_count.times do |client_id|
|
513
|
+
presence_client_one.leave_client("client:#{client_id}") do
|
514
|
+
left_callback_count += 1
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
presence_anonymous_client.subscribe(:leave) do |presence|
|
519
|
+
expect(presence.data).to be_nil
|
520
|
+
clients << presence
|
521
|
+
next unless clients.count == 5
|
522
|
+
|
523
|
+
EventMachine.add_timer(1) do
|
524
|
+
expect(clients.map(&:client_id).uniq.count).to eql(5)
|
525
|
+
expect(left_callback_count).to eql(5)
|
526
|
+
stop_reactor
|
527
|
+
end
|
239
528
|
end
|
240
|
-
stop_reactor
|
241
529
|
end
|
242
530
|
end
|
243
|
-
end
|
244
531
|
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
532
|
+
context 'with a new value in :data option' do
|
533
|
+
it 'emits the leave event with the new data value' do
|
534
|
+
presence_client_one.enter_client("client:unique", data: random_str) do
|
535
|
+
presence_client_one.leave_client("client:unique", data: data)
|
536
|
+
end
|
537
|
+
|
538
|
+
presence_client_one.subscribe(:leave) do |presence_message|
|
539
|
+
expect(presence_message.data).to eql(data)
|
540
|
+
stop_reactor
|
541
|
+
end
|
249
542
|
end
|
543
|
+
end
|
250
544
|
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
545
|
+
context 'with a nil value in :data option' do
|
546
|
+
it 'emits the leave event with a nil value' do
|
547
|
+
presence_client_one.enter_client("client:unique", data: data) do
|
548
|
+
presence_client_one.leave_client("client:unique", data: nil)
|
549
|
+
end
|
550
|
+
|
551
|
+
presence_client_one.subscribe(:leave) do |presence_message|
|
552
|
+
expect(presence_message.data).to be_nil
|
553
|
+
stop_reactor
|
554
|
+
end
|
255
555
|
end
|
256
556
|
end
|
257
|
-
end
|
258
557
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
558
|
+
context 'with no :data option' do
|
559
|
+
it 'emits the leave event with the previous data value' do
|
560
|
+
presence_client_one.enter_client("client:unique", data: data) do
|
561
|
+
presence_client_one.leave_client("client:unique")
|
562
|
+
end
|
563
|
+
|
564
|
+
presence_client_one.subscribe(:leave) do |presence_message|
|
565
|
+
expect(presence_message.data).to eql(data)
|
566
|
+
stop_reactor
|
264
567
|
end
|
265
568
|
end
|
569
|
+
end
|
570
|
+
end
|
266
571
|
|
267
|
-
|
268
|
-
|
269
|
-
|
572
|
+
it 'returns a Deferrable' do
|
573
|
+
expect(presence_client_one.leave_client('client_id')).to be_a(EventMachine::Deferrable)
|
574
|
+
stop_reactor
|
575
|
+
end
|
576
|
+
|
577
|
+
it 'calls the Deferrable callback on success' do
|
578
|
+
presence_client_one.leave_client('client_id').callback do |presence|
|
579
|
+
expect(presence).to eql(presence_client_one)
|
580
|
+
stop_reactor
|
581
|
+
end
|
582
|
+
end
|
583
|
+
end
|
584
|
+
end
|
585
|
+
|
586
|
+
context '#get' do
|
587
|
+
it 'returns a Deferrable' do
|
588
|
+
expect(presence_client_one.get).to be_a(EventMachine::Deferrable)
|
589
|
+
stop_reactor
|
590
|
+
end
|
591
|
+
|
592
|
+
it 'calls the Deferrable callback on success' do
|
593
|
+
presence_client_one.get.callback do |presence|
|
594
|
+
expect(presence).to eq([])
|
595
|
+
stop_reactor
|
596
|
+
end
|
597
|
+
end
|
598
|
+
|
599
|
+
it 'returns the current members on the channel' do
|
600
|
+
presence_client_one.enter do
|
601
|
+
presence_client_one.get do |members|
|
602
|
+
expect(members.count).to eq(1)
|
603
|
+
|
604
|
+
expect(client_one.client_id).to_not be_nil
|
605
|
+
|
606
|
+
this_member = members.first
|
607
|
+
expect(this_member.client_id).to eql(client_one.client_id)
|
608
|
+
|
609
|
+
stop_reactor
|
610
|
+
end
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
it 'filters by connection_id option if provided' do
|
615
|
+
when_all(presence_client_one.enter, presence_client_two.enter, and_wait: 0.5) do
|
616
|
+
presence_client_one.get(connection_id: client_one.connection.id) do |members|
|
617
|
+
expect(members.count).to eq(1)
|
618
|
+
expect(members.first.connection_id).to eql(client_one.connection.id)
|
619
|
+
|
620
|
+
presence_client_one.get(connection_id: client_two.connection.id) do |members|
|
621
|
+
expect(members.count).to eq(1)
|
622
|
+
expect(members.first.connection_id).to eql(client_two.connection.id)
|
270
623
|
stop_reactor
|
271
624
|
end
|
272
625
|
end
|
273
626
|
end
|
627
|
+
end
|
274
628
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
end
|
629
|
+
it 'filters by client_id option if provided' do
|
630
|
+
when_all(presence_client_one.enter(client_id: 'one'), presence_client_two.enter(client_id: 'two')) do
|
631
|
+
presence_client_one.get(client_id: 'one') do |members|
|
632
|
+
expect(members.count).to eq(1)
|
633
|
+
expect(members.first.client_id).to eql('one')
|
634
|
+
expect(members.first.connection_id).to eql(client_one.connection.id)
|
282
635
|
|
283
|
-
|
284
|
-
expect(
|
285
|
-
expect(
|
636
|
+
presence_client_one.get(client_id: 'two') do |members|
|
637
|
+
expect(members.count).to eq(1)
|
638
|
+
expect(members.first.client_id).to eql('two')
|
639
|
+
expect(members.first.connection_id).to eql(client_two.connection.id)
|
286
640
|
stop_reactor
|
287
641
|
end
|
288
642
|
end
|
289
643
|
end
|
644
|
+
end
|
290
645
|
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
646
|
+
it 'does not wait for SYNC to complete if :wait_for_sync option is false' do
|
647
|
+
presence_client_one.enter(client_id: 'one') do
|
648
|
+
presence_client_two.get(wait_for_sync: false) do |members|
|
649
|
+
expect(members.count).to eql(0)
|
650
|
+
stop_reactor
|
651
|
+
end
|
652
|
+
end
|
653
|
+
end
|
654
|
+
|
655
|
+
context 'when a member enters and then leaves' do
|
656
|
+
it 'has no members' do
|
657
|
+
presence_client_one.enter do
|
658
|
+
presence_client_one.leave do
|
659
|
+
presence_client_one.get do |members|
|
660
|
+
expect(members.count).to eq(0)
|
298
661
|
stop_reactor
|
299
662
|
end
|
300
663
|
end
|
301
664
|
end
|
302
665
|
end
|
666
|
+
end
|
667
|
+
|
668
|
+
it 'returns both members on both simultaneously connected clients' do
|
669
|
+
when_all(presence_client_one.enter(data: data_payload), presence_client_two.enter) do
|
670
|
+
EventMachine.add_timer(0.5) do
|
671
|
+
presence_client_one.get do |client_one_members|
|
672
|
+
presence_client_two.get do |client_two_members|
|
673
|
+
expect(client_one_members.count).to eq(client_two_members.count)
|
674
|
+
|
675
|
+
member_client_one = client_one_members.find { |presence| presence.client_id == client_one.client_id }
|
676
|
+
member_client_two = client_one_members.find { |presence| presence.client_id == client_two.client_id }
|
677
|
+
|
678
|
+
expect(member_client_one).to be_a(Ably::Models::PresenceMessage)
|
679
|
+
expect(member_client_one.data).to eql(data_payload)
|
680
|
+
expect(member_client_two).to be_a(Ably::Models::PresenceMessage)
|
303
681
|
|
304
|
-
it 'REST #get returns a list of members with decrypted data' do
|
305
|
-
run_reactor do
|
306
|
-
encrypted_channel.attach do
|
307
|
-
encrypted_channel.presence.enter(data: data) do
|
308
|
-
member = channel_rest_client_one.presence.get.first
|
309
|
-
expect(member.encoding).to be_nil
|
310
|
-
expect(member.data).to eql(data)
|
311
682
|
stop_reactor
|
312
683
|
end
|
313
684
|
end
|
314
685
|
end
|
315
686
|
end
|
687
|
+
end
|
688
|
+
end
|
316
689
|
|
317
|
-
|
318
|
-
|
319
|
-
let(:incompatible_encrypted_channel) { client_two.channel(channel_name, encrypted: true, cipher_params: incompatible_cipher_options) }
|
690
|
+
context '#subscribe' do
|
691
|
+
let(:messages) { [] }
|
320
692
|
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
end
|
331
|
-
end
|
332
|
-
end
|
693
|
+
context 'with no arguments' do
|
694
|
+
it 'calls the callback for all presence events' do
|
695
|
+
when_all(channel_client_one.attach, channel_client_two.attach) do
|
696
|
+
presence_client_two.subscribe do |presence_message|
|
697
|
+
messages << presence_message
|
698
|
+
next unless messages.count == 3
|
699
|
+
|
700
|
+
expect(messages.map(&:action).map(&:to_sym)).to contain_exactly(:enter, :update, :leave)
|
701
|
+
stop_reactor
|
333
702
|
end
|
703
|
+
|
704
|
+
presence_client_one.enter
|
705
|
+
presence_client_one.update
|
706
|
+
presence_client_one.leave
|
334
707
|
end
|
708
|
+
end
|
709
|
+
end
|
710
|
+
end
|
335
711
|
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
end
|
712
|
+
context '#unsubscribe' do
|
713
|
+
context 'with no arguments' do
|
714
|
+
it 'removes the callback for all presence events' do
|
715
|
+
when_all(channel_client_one.attach, channel_client_two.attach) do
|
716
|
+
subscribe_callback = proc { raise 'Should not be called' }
|
717
|
+
presence_client_two.subscribe &subscribe_callback
|
718
|
+
presence_client_two.unsubscribe &subscribe_callback
|
344
719
|
|
345
|
-
|
346
|
-
|
347
|
-
|
720
|
+
presence_client_one.enter
|
721
|
+
presence_client_one.update
|
722
|
+
presence_client_one.leave do
|
723
|
+
EventMachine.add_timer(0.5) do
|
724
|
+
stop_reactor
|
348
725
|
end
|
349
726
|
end
|
350
727
|
end
|
351
728
|
end
|
352
729
|
end
|
730
|
+
end
|
353
731
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
732
|
+
context 'REST #get' do
|
733
|
+
it 'returns current members' do
|
734
|
+
presence_client_one.enter(data: data_payload) do
|
735
|
+
members = channel_rest_client_one.presence.get
|
736
|
+
this_member = members.first
|
737
|
+
|
738
|
+
expect(this_member).to be_a(Ably::Models::PresenceMessage)
|
739
|
+
expect(this_member.client_id).to eql(client_one.client_id)
|
740
|
+
expect(this_member.data).to eql(data_payload)
|
741
|
+
|
742
|
+
stop_reactor
|
743
|
+
end
|
744
|
+
end
|
745
|
+
|
746
|
+
it 'returns no members once left' do
|
747
|
+
presence_client_one.enter(data: data_payload) do
|
748
|
+
presence_client_one.leave do
|
749
|
+
members = channel_rest_client_one.presence.get
|
750
|
+
expect(members.count).to eql(0)
|
358
751
|
stop_reactor
|
359
752
|
end
|
360
|
-
|
361
|
-
|
753
|
+
end
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
context 'client_id with ASCII_8BIT' do
|
758
|
+
let(:client_id) { random_str.encode(Encoding::ASCII_8BIT) }
|
759
|
+
|
760
|
+
context 'in connection set up' do
|
761
|
+
let(:client_one) { Ably::Realtime::Client.new(default_options.merge(client_id: client_id)) }
|
762
|
+
|
763
|
+
it 'is converted into UTF_8' do
|
764
|
+
presence_client_one.enter
|
765
|
+
presence_client_one.on(:entered) do |presence|
|
766
|
+
expect(presence.client_id.encoding).to eql(Encoding::UTF_8)
|
767
|
+
expect(presence.client_id.encode(Encoding::ASCII_8BIT)).to eql(client_id)
|
768
|
+
stop_reactor
|
362
769
|
end
|
363
770
|
end
|
364
771
|
end
|
365
772
|
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
773
|
+
context 'in channel options' do
|
774
|
+
let(:client_one) { Ably::Realtime::Client.new(default_options) }
|
775
|
+
|
776
|
+
it 'is converted into UTF_8' do
|
777
|
+
presence_client_one.enter(client_id: client_id)
|
778
|
+
presence_client_one.on(:entered) do |presence|
|
779
|
+
expect(presence.client_id.encoding).to eql(Encoding::UTF_8)
|
780
|
+
expect(presence.client_id.encode(Encoding::ASCII_8BIT)).to eql(client_id)
|
371
781
|
stop_reactor
|
372
782
|
end
|
373
|
-
|
374
|
-
|
783
|
+
end
|
784
|
+
end
|
785
|
+
end
|
786
|
+
|
787
|
+
context 'encoding and decoding of presence message data' do
|
788
|
+
let(:secret_key) { random_str }
|
789
|
+
let(:cipher_options) { { key: secret_key, algorithm: 'aes', mode: 'cbc', key_length: 256 } }
|
790
|
+
let(:channel_name) { random_str }
|
791
|
+
let(:encrypted_channel) { client_one.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
792
|
+
let(:channel_rest_client_one) { client_one.rest_client.channel(channel_name, encrypted: true, cipher_params: cipher_options) }
|
793
|
+
|
794
|
+
let(:crypto) { Ably::Util::Crypto.new(cipher_options) }
|
795
|
+
|
796
|
+
let(:data) { { 'key' => random_str } }
|
797
|
+
let(:data_as_json) { data.to_json }
|
798
|
+
let(:data_as_cipher) { crypto.encrypt(data.to_json) }
|
799
|
+
|
800
|
+
it 'encrypts presence message data' do
|
801
|
+
encrypted_channel.attach do
|
802
|
+
encrypted_channel.presence.enter data: data
|
803
|
+
end
|
804
|
+
|
805
|
+
encrypted_channel.presence.__incoming_msgbus__.unsubscribe(:presence) # remove all subscribe callbacks that could decrypt the message
|
806
|
+
encrypted_channel.presence.__incoming_msgbus__.subscribe(:presence) do |presence|
|
807
|
+
if protocol == :json
|
808
|
+
expect(presence['encoding']).to eql('json/utf-8/cipher+aes-256-cbc/base64')
|
809
|
+
expect(crypto.decrypt(Base64.decode64(presence['data']))).to eql(data_as_json)
|
810
|
+
else
|
811
|
+
expect(presence['encoding']).to eql('json/utf-8/cipher+aes-256-cbc')
|
812
|
+
expect(crypto.decrypt(presence['data'])).to eql(data_as_json)
|
375
813
|
end
|
814
|
+
stop_reactor
|
376
815
|
end
|
377
816
|
end
|
378
817
|
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
818
|
+
context '#subscribe' do
|
819
|
+
it 'emits decrypted enter events' do
|
820
|
+
encrypted_channel.attach do
|
821
|
+
encrypted_channel.presence.enter data: data
|
822
|
+
end
|
823
|
+
|
824
|
+
encrypted_channel.presence.subscribe(:enter) do |presence_message|
|
825
|
+
expect(presence_message.encoding).to be_nil
|
826
|
+
expect(presence_message.data).to eql(data)
|
827
|
+
stop_reactor
|
828
|
+
end
|
829
|
+
end
|
830
|
+
|
831
|
+
it 'emits decrypted update events' do
|
832
|
+
encrypted_channel.attach do
|
833
|
+
encrypted_channel.presence.enter(data: 'to be updated') do
|
834
|
+
encrypted_channel.presence.update data: data
|
835
|
+
end
|
836
|
+
end
|
837
|
+
|
838
|
+
encrypted_channel.presence.subscribe(:update) do |presence_message|
|
839
|
+
expect(presence_message.encoding).to be_nil
|
840
|
+
expect(presence_message.data).to eql(data)
|
841
|
+
stop_reactor
|
842
|
+
end
|
843
|
+
end
|
844
|
+
|
845
|
+
it 'emits previously set data for leave events' do
|
846
|
+
encrypted_channel.attach do
|
847
|
+
encrypted_channel.presence.enter(data: data) do
|
848
|
+
encrypted_channel.presence.leave
|
849
|
+
end
|
850
|
+
end
|
851
|
+
|
852
|
+
encrypted_channel.presence.subscribe(:leave) do |presence_message|
|
853
|
+
expect(presence_message.encoding).to be_nil
|
854
|
+
expect(presence_message.data).to eql(data)
|
383
855
|
stop_reactor
|
384
856
|
end
|
385
857
|
end
|
386
858
|
end
|
387
859
|
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
860
|
+
context '#get' do
|
861
|
+
it 'returns a list of members with decrypted data' do
|
862
|
+
encrypted_channel.presence.enter(data: data) do
|
863
|
+
encrypted_channel.presence.get do |members|
|
864
|
+
member = members.first
|
865
|
+
expect(member.encoding).to be_nil
|
866
|
+
expect(member.data).to eql(data)
|
867
|
+
stop_reactor
|
868
|
+
end
|
392
869
|
end
|
393
|
-
|
394
|
-
|
870
|
+
end
|
871
|
+
end
|
872
|
+
|
873
|
+
context 'REST #get' do
|
874
|
+
it 'returns a list of members with decrypted data' do
|
875
|
+
encrypted_channel.presence.enter(data: data) do
|
876
|
+
member = channel_rest_client_one.presence.get.first
|
877
|
+
expect(member.encoding).to be_nil
|
878
|
+
expect(member.data).to eql(data)
|
395
879
|
stop_reactor
|
396
880
|
end
|
397
881
|
end
|
398
882
|
end
|
399
883
|
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
884
|
+
context 'when cipher settings do not match publisher' do
|
885
|
+
let(:client_options) { default_options.merge(log_level: :fatal) }
|
886
|
+
let(:incompatible_cipher_options) { { key: secret_key, algorithm: 'aes', mode: 'cbc', key_length: 128 } }
|
887
|
+
let(:incompatible_encrypted_channel) { client_two.channel(channel_name, encrypted: true, cipher_params: incompatible_cipher_options) }
|
888
|
+
|
889
|
+
it 'delivers an unencoded presence message left with encoding value' do
|
890
|
+
encrypted_channel.presence.enter data: data
|
891
|
+
|
892
|
+
incompatible_encrypted_channel.presence.subscribe(:enter) do
|
893
|
+
incompatible_encrypted_channel.presence.get do |members|
|
894
|
+
member = members.first
|
895
|
+
expect(member.encoding).to match(/cipher\+aes-256-cbc/)
|
896
|
+
expect(member.data).to_not eql(data)
|
897
|
+
stop_reactor
|
898
|
+
end
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
it 'emits an error when cipher does not match and presence data cannot be decoded' do
|
903
|
+
incompatible_encrypted_channel.attach do
|
904
|
+
incompatible_encrypted_channel.on(:error) do |error|
|
905
|
+
expect(error).to be_a(Ably::Exceptions::CipherError)
|
906
|
+
expect(error.message).to match(/Cipher algorithm AES-128-CBC does not match/)
|
907
|
+
stop_reactor
|
908
|
+
end
|
909
|
+
|
910
|
+
encrypted_channel.attach do
|
911
|
+
encrypted_channel.presence.enter data: data
|
912
|
+
end
|
913
|
+
end
|
404
914
|
end
|
405
915
|
end
|
916
|
+
end
|
406
917
|
|
407
|
-
|
408
|
-
|
409
|
-
|
918
|
+
context 'leaving' do
|
919
|
+
specify 'expect :left event once underlying connection is closed' do
|
920
|
+
presence_client_one.on(:left) do
|
921
|
+
expect(presence_client_one.state).to eq(:left)
|
410
922
|
stop_reactor
|
411
923
|
end
|
924
|
+
presence_client_one.enter do
|
925
|
+
client_one.close
|
926
|
+
end
|
412
927
|
end
|
413
928
|
|
414
|
-
|
415
|
-
|
416
|
-
|
929
|
+
specify 'expect :left event with client data from enter event' do
|
930
|
+
presence_client_one.subscribe(:leave) do |message|
|
931
|
+
presence_client_one.get do |members|
|
932
|
+
expect(members.count).to eq(0)
|
933
|
+
expect(message.data).to eql(data_payload)
|
934
|
+
stop_reactor
|
935
|
+
end
|
936
|
+
end
|
937
|
+
presence_client_one.enter(data: data_payload) do
|
938
|
+
presence_client_one.leave
|
939
|
+
end
|
940
|
+
end
|
417
941
|
end
|
942
|
+
|
943
|
+
skip 'ensure connection_id is unique and updated on ENTER'
|
944
|
+
skip 'ensure connection_id for presence member matches the messages they publish on the channel'
|
945
|
+
skip 'stop a call to get when the channel has not been entered'
|
946
|
+
skip 'stop a call to get when the channel has been entered but the list is not up to date'
|
947
|
+
skip 'presence will resume sync if connection is dropped mid-way'
|
418
948
|
end
|
419
949
|
end
|