ably-rest 0.7.1 → 0.7.3

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.
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