ably-rest 0.7.1 → 0.7.3

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